Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An MCP tool missing from the cached catalog made the tool-result handler re-read Claude Code's MCP status while holding the SDK message lock, so a slow server could stall streamed text and other tools for up to the lookup timeout. Codex captures off its event reader for the same reason. The reader now only checks the cached catalog. A tool it does not know is looked up by the capture fiber, which already keeps the row running until it lands and falls back to the plain row. Written by Claude Opus 5.5 in Claude Code, through T3 Code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Screenshots from the real run described in Verification (dev server from this branch, Claude Code 2.1.316, Claude Haiku 5.5, local stdio MCP server with MCP Apps). Before (upstream main): the app tool call is a plain tool row. After (this branch): the app renders in place of the row. App-initiated tool call: T3 asks before Result: the app received the call's result (event log inside the app). |
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
SDK operations and capability wiring apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts, apps/server/src/orchestration-v2/Adapters/ClaudeAgentSdkQueryRunner.test.ts, apps/server/package.json, pnpm-workspace.yaml |
The query session exposes MCP server status, resource reads, and tool calls. Claude MCP Apps support is enabled when the CLI advertises both required capabilities. The SDK version and its workspace exclusion are updated. |
Tool mapping and MCP Apps runtime apps/server/src/orchestration-v2/Adapters/ClaudeMcpApps.ts, apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts, apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts, docs/user/html-renders.md |
Claude MCP tools map to app targets when their names are unique and their metadata contains a valid resource URI. The runtime lists tools, reads resources, and calls tools. The documentation describes Claude support and its stated exceptions. |
App capture and tool-row settlement apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts, apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts |
Eligible completed tool calls can read and snapshot an app resource before the tool row settles. Capture failures, interruptions, and query exits settle the row as a plain MCP row. |
Estimated code review effort: 4 (Complex) | ~45 minutes
Change: Feature
Sequence Diagram(s)
sequenceDiagram
participant Claude CLI
participant ClaudeAdapterV2
participant ClaudeAgentSdkQuerySession
participant MCP resource
participant Tool row
Claude CLI->>ClaudeAdapterV2: Complete an eligible MCP tool call
ClaudeAdapterV2->>ClaudeAgentSdkQuerySession: Read the app resource
ClaudeAgentSdkQuerySession-->>ClaudeAdapterV2: Return resource contents
ClaudeAdapterV2->>Tool row: Attach snapshot and normalized result
Suggested reviewers: juliusmarminge
Merge Risk: 
Minimal · up to 492d1
-
Claude threads can now render MCP Apps, with a consent prompt before each app tool call and a fallback to ordinary tool rows when capture fails. No merge-blocking issue was found. The one thing to keep in mind is that
callToolrelies on an SDK request method that is not yet public, so a future SDK upgrade could break it.
Pre-merge checks: 
3 · 
1
-
Failed checks: 1 warning Passed checks: 3 passedFull details: Description check
-
Explanation
The description includes all required sections and provides detailed problem, implementation, verification, limitations, and scope information. However, the Scope and approval section explicitly states that no maintainer has approved the direction, despite the template requiring explicit maintainer approval or an explanation for a small focused fix.
Resolution
Obtain and link explicit maintainer approval for the direction and scope, including approval for the request-channel mcp_call implementation and reliance on CLAUDE_CODE_MCP_APPS_HOST, or explain why the change qualifies for the small-fix exception.
-
Finishing Touches
-
Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts




Problem
MCP apps render only in Codex threads. In a Claude thread, a tool whose server declares an MCP App (
_meta.ui.resourceUri) shows as an ordinary tool row, and an app view cannot reach Claude's MCP connections (McpAppRequestsanswersprovider-unsupported). #16236 left Claude out because its SDK had no resource read. It has one now:Query.readMcpResource()and the tools'_meta.uiinmcpServerStatus()shipped in@anthropic-ai/claude-agent-sdk0.3.280.Change
Claude Code owns the MCP connections, so
ClaudeAdapterV2implements the same capture and the same threemcpAppsoperations asCodexAdapterV2, through the live query:CLAUDE_CODE_MCP_APPS_HOST=1, which makes Claude Code announceio.modelcontextprotocol/uito its MCP servers (Claude's counterpart ofCODEX_CLIENT_CAPABILITIES). A user env value still wins.mcp_tool_ui_meta_v1andmcp_read_resource_v1, the adapter looks the tool up in a per-process catalog built frommcpServerStatus()(prefetched atinit; a tool the cached catalog does not know is looked up by the capture, never on the message reader), reads itsui://page withreadMcpResource(), stores it withsnapshotMcpApp, and emits the row once asserver.toolwithoutput: { t3McpApp, result }. As in Codex, the capture runs off the reader and the row stays running until it lands, which keeps the run open past the turn's end. An interrupted, failed or cancelled turn, or the Claude process exiting, shows the plain row instead; a capture that loses that race discards its stored page. Any failure falls back to the plain row.mcpServerStatus()read per Claude process serves every later call;listToolsrefreshes it, an unknown tool triggers one re-read, and a failed or unhelpful read waits 60 s before the next. Non-app MCP tools pay no extra request once the catalog is loaded.resultis Claude Code'stool_use_resultfor MCP Apps tools (content,structuredContent,_meta), else the model-facing content, withisErrorfor a failed call.listToolsandreadResourceuse the public SDK methods.callToolsends themcp_callcontrol request withresult_not_for_model. The SDK types this request but has no public method for it yet (Typed SDK surface for out-of-band MCP tool calls / resources (MCP Apps support) anthropics/claude-agent-sdk-typescript#380), so it goes through the query's request channel in one place, and replies from CLIs withoutresult_not_for_modelare converted. A tool whose nativemcp__server__toolname is shared by another configured tool after Claude Code's normalization is refused.mcp_callruns outside Claude Code's permission pipeline, so tool annotations are not forwarded to apps and T3 asks before every app tool call in Claude threads.@anthropic-ai/claude-agent-sdkfrom 0.3.276 to 0.3.316 (0.3.280 is the minimum for these APIs).The env and
callToolship together on purpose: announcing the extension without working app tool calls would turn writes that servers defer to an app form (#23850's GitHub case) into forms that cannot submit.Not covered, and stated in the user guide: apps called by Claude subagents (their rows have no run, so the app cannot reach the live query) and
ui/update-model-contextnotes, which only Codex consumes today. Also not covered: a model change while a page is loading shows the plain row (blocking the change would surfaceClaudeBackgroundWorkBlocksQueryReplacementErrorinstead), and a user frame that carries several tool results keeps the model-facing content only, because Claude Code sendstool_use_resultper message.Scope and approval
This extends the established MCP Apps capability to a second provider through the adapter contract #16236 built for it ("adding a provider means implementing three adapter methods", "through the provider's own MCP client"). The same direction was proposed in Ideas discussion #21859 (by broomva, with a prototype on an older main); this PR follows that design on current main. No maintainer has approved the direction yet. Two points need a maintainer decision: sending
mcp_callthrough the request channel until #380 lands, and relying on the undocumentedCLAUDE_CODE_MCP_APPS_HOST.Verification
ClaudeAdapterV2.test.ts(wake harness): an app replaces its row, Claude's messages keep flowing while the catalog loads (this test hangs without the fix), the row stays running pastturn.terminaluntil the page lands, the result keepsstructuredContentfrom the recordedtool_use_resultshape, failed calls keep the app withisError; plain rows for a tool without an app, an unreadable page, an unreadable catalog and a CLI without the capabilities (nomcp_statuscall); the plain row when the turn is interrupted or Claude exits mid-capture;listTools,readResourceandcallToolthrough the live query, including the ambiguous-name refusal.ClaudeAgentSdkQueryRunner.test.tschecks the CLI env.vp run check:changedpasses (format, lint, knip, typecheck, 3 test files).visionneuse.bc_viewwitht3McpAppand the page attachment. An MCP Apps lab card made app-initiatedlab_reportcalls; each one went through T3's approval dialog and reached Claude Code asmcp_call(provider logquery.mcp_call). Screenshots (before, after, app tool call approval, app tool call result) follow in a comment.Written by Claude Opus 5.5 in Claude Code, through T3 Code. Reviewed by GPT-6.1 Sol (Codex), Grok 4.7 and Claude Opus 5.5.