modelcontextprotocol / modelcontextprotocol/ext-apps

Claude iOS: template renders but host delivers no lifecycle notifications to MCP App iframes (tool-input, tool-result, size-changed all absent)

Open
#734 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
2.9k
Forks
387
Avg merge
3h 21m
Merged PRs (30d)
6

Description

Environment: Claude iOS app (2026-07-30, Opus 5), remote MCP server via custom OAuth connector. Server: @modelcontextprotocol/ext-apps@1.7.5, @modelcontextprotocol/sdk@1.30.0, Streamable HTTP (stateless per-request). Tool registered with registerAppTool + _meta.ui.resourceUri; single-file HTML template with a version stamp in the footer so screenshots identify exactly which template version a host rendered.

Observed (4 instrumented live test rounds, template versions v1.2.0 → v1.4.0):

  1. The ui:// template loads and renders — chrome, inputs, buttons, and our version stamp are all visible, so resource fetch + iframe mount work on iOS.
  2. ui/notifications/tool-result is never delivered. The app registers ontoolresult before connect() per SDK guidance; the handler never fires. The same template receives it on Claude Desktop (when Desktop renders at all — see #671).
  3. ui/notifications/tool-input is never delivered either — instrumented separately from v1.3.0 onward.
  4. ui/notifications/size-changed from the app is ignored — the iframe stays at a short initial height, clipping content (and McpUiResourceMeta offers no initial-size hint, so servers have no workaround).
  5. iframe-initiated tools/call via callServerTool also appears non-functional in our v1.4.0 polling fallback, though we have less instrumentation certainty here than for 2–4.

Impact: any tool-result-driven interactive UI is non-functional on Claude iOS — the card renders as permanent chrome with no data and no working actions. We shipped a full accept/regenerate image-review card and had to park it. The workaround that works everywhere is putting next-step instructions in the tool result text and letting the model execute follow-ups conversationally.

Expected: per SEP-1865, the host delivers tool lifecycle notifications to the view after ui/initialize completes.

Repro: any registerAppTool sample whose ontoolresult handler displays received data — on iOS the handler never fires, while the same server renders end-to-end in the basic-host harness and MCPJam. Happy to share our server URL, template source, and timestamped screenshots on request.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with a registerAppTool sample and the ui/initialize lifecycle, then compare notification delivery in Claude iOS with the basic-host harness and MCPJam. Verify whether tool-input, tool-result, and size-changed events reach the iframe, and determine whether the issue is fixable in ext-apps or is a host incompatibility.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.