Skip to content

feat(agent): expose correlated per-invocation progress for native and MCP tools #2239

Description

@suxiaoshao
  • I have looked for existing issues (including closed) about this

Feature Request

Expose a host-facing progress channel for one concrete tool invocation, shared by native and MCP tools.

Rig already gives hosts a stable internal_call_id around tool-call lifecycle events, but the executing tool cannot publish intermediate progress that the host can correlate with that invocation. A host can therefore show requested/running/completed states, but it cannot faithfully show updates such as “scanned 42/100 files” or “downloaded 8 MiB”.

This is different from streamed tool-call argument deltas: the requested API reports progress while the tool itself is executing.

Motivation

Without a public per-invocation progress contract, downstream hosts must either show an indefinite spinner, infer progress from unrelated events, or correlate by tool name/arguments. The latter two approaches are incorrect when identical tools run concurrently or recursively.

The same gap exists for MCP. MCP can carry a progress token on the request and send progress notifications, but Rig does not currently project those notifications into a host-facing event associated with the Rig invocation.

Related work provides useful pieces but not the complete contract:

  • #1311 added internal_call_id for lifecycle correlation.
  • #1906 discusses host-facing tool metadata including emits_progress and MCP progress tokens, but does not provide a progress transport.
  • #2094 discusses call-scoped state under concurrency, but not live tool-to-host events.
  • #2121 contains an operation-backend ProgressSink; it is not a general progress channel for arbitrary Tool/McpTool dispatch.
  • The closed #2122 explicitly listed a general tool-progress event channel as remaining work.

Proposal

Add an additive, runtime-neutral progress contract with these properties.

Correlated event

Expose a normalized event whose exact shape is open for design, but which carries at least:

pub struct ToolProgress {
    pub internal_call_id: String,
    pub sequence: u64,
    pub message: Option<String>,
    pub completed: Option<f64>,
    pub total: Option<f64>,
}

It must support both determinate and indeterminate progress. Sequence values must be monotonic within one invocation so hosts can discard stale updates.

Native tools

The per-call execution context/extensions supplied to a native tool should expose:

  1. the stable internal_call_id for that dispatch; and
  2. a host-installed, concurrency-safe ToolProgressSink or reporter bound to that invocation.

Progress is host-only observation and must not be appended to the model-visible tool result.

MCP tools

For an MCP call, Rig should:

  1. allocate or project a matching MCP progress token without overwriting unrelated caller metadata;
  2. route matching MCP progress notifications into the same host-facing progress channel; and
  3. preserve the same internal_call_id used by normal Rig lifecycle events.

MCP servers that do not emit progress remain valid tools and must not receive fabricated percentage updates.

Semantics

  • Native, MCP, parallel, nested, and duplicate-name calls remain independently correlated.
  • Streaming and non-streaming agent runs expose the same progress semantics.
  • Updates after completion, failure, denial, or cancellation are ignored or rejected deterministically.
  • Progress observation does not alter the tool result or agent control flow.

Acceptance criteria

  • A native tool can emit multiple updates during execution and the host receives them with the matching internal_call_id.
  • Two concurrent identical calls cannot cross-update.
  • Real MCP progress notifications are forwarded through the same public API.
  • A non-progress MCP tool emits no synthetic progress.
  • Cancellation and all terminal tool states handle late updates deterministically.
  • Tests cover native, MCP, concurrency, nested calls, cancellation, and streaming/non-streaming parity.

Alternatives

  • Downstream-only progress registries: cannot generically bind Rig's private invocation lifecycle to arbitrary native and MCP tools, and every host would rebuild the same fragile bridge.
  • A running spinner: useful as lifecycle UI, but it is not tool-reported progress.
  • Tool-name or argument correlation: incorrect for concurrent or recursive identical calls.
  • Operation-backend-only progress: useful for command/code backends, but it does not cover the general Tool and McpTool execution boundary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions