[BUG] MCP progress notifications are no longer displayed in the UI (regression)

Status Fixed / completed
Reported on v2.1.101
Maintainer reply ✓ Yes — claude[bot]
Activity 8 comments · opened Apr 21, 2026 · closed May 28, 2026
💡 Likely answer: A maintainer (claude[bot], contributor) responded on this thread — see the highlighted reply below.

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

MCP tool calls are unconditionally collapsed in the UI (isCollapsible: true for all MCP tools), showing only "Calling {server}..." with no visible streaming output, even when Ctrl+O is pressed.

This is fine for quick MCP calls, but breaks the UX for long-running tools that stream meaningful progress (build status, deploy steps, test execution). The user sees a static "Calling {server}..." for minutes with no indication of what's happening.

Built-in tools like Bash stream their output visibly by default. MCP tools doing equivalent work (running builds, deploying, testing, etc) should have the same affordance.

Request: Add an MCP tool annotation (e.g. _meta["anthropic/expandByDefault"]: true) that lets servers opt specific tools out of auto-collapse, or at minimum show streaming content inline while the tool is in progress and collapse only after completion. Another option is to prevent collapsing when the MCP is opting into APIs to stream progress.

What Should Happen?

When an MCP tool is actively running and streaming output, its content should be visible inline — the same way Bash tool output streams visibly while the command is executing. After the tool completes, collapsing the result is fine.

Ideally, MCP servers should also be able to opt specific tools out of auto-collapse entirely via a tool annotation (e.g. _meta["anthropic/expandByDefault"]: true), so that long-running tools like build pipelines, test runners, and deploy steps always show their progress without requiring the user to manually expand with Ctrl+O.

Error Messages/Logs

Steps to Reproduce

  1. Set up any MCP server that has a tool which runs for more than a few seconds and streams progress output (e.g. a build tool, test runner, or deploy pipeline)
  2. Call the MCP tool from Claude Code
  3. Observe that the UI shows only "Calling {server}..." with no visible streaming output
  4. Compare with running an equivalent long command via the built-in Bash tool, which streams output visibly by default
  5. Press Ctrl+O to expand the MCP tool call — still no streaming progress output, just the tool invocation itself.

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.101

Claude Code Version

2.1.116

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

_No response_

View original on GitHub ↗

8 Comments

github-actions[bot] · 4 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/48005
  2. https://github.com/anthropics/claude-code/issues/37123
  3. https://github.com/anthropics/claude-code/issues/45839

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

EDmitry · 4 months ago

these are similar ones, but the most important bit covered by this issue is that transient status isn't streamed anymore (MCP notification/progress APIs) for long-running tools, not just that the tool invocation itself is hidden.

zahidzorbaz · 4 months ago

Cross-SDK reproduction on 2.1.126

Independently confirming this regression with three separate MCP servers, all targeting the same Claude Code 2.1.126 client. The bar never renders in any of them.

1. TypeScript MCP SDK 1.29.0 (canonical pattern from docs/server.md)

const server = new McpServer(
  { name: 'progress-mcp-ts', version: '0.0.1' },
  { capabilities: { tools: {}, logging: {} } },
);

server.registerTool(
  'progress_demo',
  {
    description: 'Emit fake progress notifications to test the client UI.',
    inputSchema: {
      steps: z.number().int().min(1).max(1000).optional().default(10),
      delay_ms: z.number().int().min(0).max(60000).optional().default(200),
    },
  },
  async ({ steps, delay_ms }, ctx) => {
    const progressToken = ctx._meta?.progressToken;
    for (let i = 1; i <= steps; i++) {
      if (progressToken !== undefined) {
        await ctx.sendNotification({
          method: 'notifications/progress',
          params: { progressToken, progress: i, total: steps, message: `Step ${i}/${steps}` },
        });
      }
      if (i < steps) await new Promise((r) => setTimeout(r, delay_ms));
    }
    return { content: [{ type: 'text', text: JSON.stringify({ status: 'done', steps }) }] };
  },
);

Tested at 10×200 ms (2 s) and 30×500 ms (15 s). No bar. Brief flicker as the tool-call container opens, then collapses to the result with no progress UI in between.

2. rmcp 1.6.0 (Rust, canonical pattern)

#[tool(description = "Emit fake progress notifications to test the client UI")]
async fn progress_demo(
    &self,
    Parameters(args): Parameters<ProgressDemoArgs>,
    ctx: RequestContext<RoleServer>,
) -> Result<CallToolResult, ErrorData> {
    let steps = args.steps.unwrap_or(10).max(1);
    let delay_ms = args.delay_ms.unwrap_or(200);
    let token = ctx.meta.get_progress_token();
    for i in 1..=steps {
        if let Some(ref tok) = token {
            let params = ProgressNotificationParam::new(tok.clone(), i as f64)
                .with_total(steps as f64)
                .with_message(format!(\"Step {i}/{steps}\"));
            let _ = ctx.peer.notify_progress(params).await;
        }
        if i < steps {
            tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await;
        }
    }
    /* ... */
}

Same outcome. No bar.

3. Real-world MCP server (dolusoft/ssh-mcp)

ssh_upload emits await-ordered, throttled progress during SFTP transfers. Frame ordering is verified (notifications precede the response on the wire — confirmed with a stdin/stdout probe). No bar.

Common factor

Three different SDKs (TS, rmcp, custom rmcp wrapper). Three different transports of the same protocol. One identical outcome under Claude Code 2.1.126: no progress UI.

The only common factor is the Claude Code client. Server-side all three are MCP-spec compliant.

Worth noting alongside the UI regression

Active progress emission in 2.1.126 also triggers the stdio transport kill described in #47378 / #53617 — so on top of the missing UI, the next call to the server returns -32000 Connection closed. This makes it a double regression, not just a missing-render: opting into progress today actively breaks subsequent tool calls.

For consumers, the pragmatic move is to gate progress emission behind an opt-in flag until both #51713 and #47378 are resolved (we've done this in dolusoft/ssh-mcp via SSH_MCP_PROGRESS_ENABLED, default off).

sivsoft · 3 months ago

Yes, output is absent. Especially hard when start mcp ssh. And I don't see anything. Very big bug.

tcfurrer · 3 months ago

Is this about stdout/stderr, inband json MCP status messages, inband json MCP progress messages, or all three? Just want to point out that an MCP server that's sending back json status and progress and that should work as well.

See:
https://modelcontextprotocol.io/specification/draft/basic/utilities/progress

Brian-McM · 3 months ago

Adding a third independently-verified repro on the Go SDK (github.com/modelcontextprotocol/go-sdk@v1.4.0), against Claude Code 2.1.147. Behavior matches @zahidzorbaz's TypeScript repro: req.Session.NotifyProgress(...) calls leave the binary correctly (verified via MCP Inspector and direct stdio capture), but Claude Code shows no inline progress text during the call, and --debug logs contain no trace of received progress notifications either.

To answer @tcfurrer's scoping question: this is specifically about in-band JSON MCP notifications/progress per the spec (https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress). The spec is unambiguous that "either side can send progress notifications" and that the message field "SHOULD provide relevant human readable progress information" — i.e. it exists specifically for client display.

Closing related issue #4157 with "use streaming tool responses instead" was incorrect — MCP has no streaming tool-response mechanism (a tools/call returns a single CallToolResult); notifications/progress is the protocol's only spec-compliant path for incremental updates during a long tool call.

Strongly +1 on the bug status and regression label. Also note related #55374 — server-side notifications/message is also silently dropped, which means the natural fallback (have the server emit log records instead) is also blocked in Claude Code today.

claude[bot] contributor · 3 months ago

This issue was fixed as of version 2.1.153.

github-actions[bot] · 1 month ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.