Repository navigation
feat(mcp): support MCP 2026-07-28 and MCP Apps in daemon WebShell #8968
Description
What would you like to be added?
Please add first-class support for the MCP 2026-07-28 core specification and MCP Apps, with qwen daemon / qwen serve WebShell as the primary UI host.
Scope
1. MCP 2026-07-28 core client
- Migrate Qwen Code's MCP client from SDK v1 to the official TypeScript SDK v2.
- Support the stateless core: no required
initialize/initializedexchange orMcp-Session-Id, optionalserver/discover, and per-request client identity/capabilities in_meta. - Send the required
MCP-Protocol-Version,Mcp-Method, andMcp-NameHTTP headers. - Honor cache hints such as
ttlMsandcacheScopeon list/read results. - Support Multi Round-Trip Requests (MRTR): handle
resultType: "input_required", collect user input/approval, and retry the original call withinputResponses. - Preserve compatibility with existing MCP servers during the migration where the SDK supports it.
2. MCP Apps host in daemon WebShell
- Advertise the
io.modelcontextprotocol/uiextension capability. - Preserve MCP tool UI metadata from
tools/list, especially_meta.ui.resourceUri(plus the deprecated flat key during the compatibility window). - Fetch and validate
ui://resources, includingtext/html;profile=mcp-app. - Render Apps inline in a sandboxed iframe and enforce the resource's
_meta.ui.cspand requested permissions. - Integrate the host-side AppBridge /
postMessageprotocol. - Forward tool input, partial input, result, cancellation, host context, theme, locale, styles, and size/lifecycle notifications to the App.
- Handle App requests such as
tools/call,ui/message,ui/update-model-context, open-link/download, and display-mode requests through the existing daemon permission boundary. - Carry App metadata and lifecycle state through daemon/WebShell event types so reload, resume, cancellation, and multi-workspace sessions remain correct.
- Keep progressive fallback behavior: TUI, headless, ACP, and clients without UI support should still receive text/structured tool results.
Acceptance criteria
- Qwen Code can connect to a server that only speaks MCP
2026-07-28and successfully discover/list/call tools without a protocol session. - The daemon WebShell renders an official MCP Apps example such as
@modelcontextprotocol/server-cohort-heatmapinline. - The rendered App can call another MCP tool and send
ui/update-model-contextback to the host. - An MRTR
input_requiredresponse is surfaced in WebShell, and approval retries the original tool call withinputResponses. - Theme/locale/context updates, resize, cancellation, teardown, reload, and session resume are covered.
- CSP, iframe sandboxing, tool permission checks, and untrusted HTML isolation have focused security tests.
- Legacy MCP tool-only servers continue to work.
Why is this needed?
MCP 2026-07-28 is now the current specification and is a breaking protocol update: it introduces a stateless core, header-based routing, MRTR, cacheable list results, and a formal extensions framework. MCP Apps is one of the official extensions in that framework.
Current Qwen Code main is not compatible yet:
packages/core/package.jsonstill depends on@modelcontextprotocol/sdk ^1.30.0, while the2026-07-28support is delivered by SDK v2.- MCP discovery re-reads
tools/listonly to preserve tool annotations; it does not retain_meta.ui.resourceUri. - MCP tool results are transformed into a limited set of GenAI parts, so App-specific metadata/lifecycle information is not carried to a UI host.
- WebShell tool message types carry ordinary content/diff/terminal data but no MCP App descriptor or lifecycle state.
- WebShell has a static sandboxed HTML artifact preview, but it has no AppBridge, MCP Apps capability negotiation, or bidirectional host/App channel.
As a result, a 2026-07-28-only MCP server cannot be used correctly, and UI-enabled MCP tools cannot render or interact inside daemon WebShell.
Daemon WebShell is the most useful Qwen Code surface for MCP Apps because it already provides a browser UI, approvals, persistent sessions, workspace routing, and sandboxed HTML rendering. Data-heavy tools especially benefit from inline tables, charts, drill-down, reconciliation, and approval forms instead of reducing everything to text.
Additional context
References
- MCP 2026-07-28 announcement: https://blog.modelcontextprotocol.io/posts/2026-07-28/
- MCP 2026-07-28 specification: https://modelcontextprotocol.io/specification/2026-07-28
- MCP Apps overview: https://modelcontextprotocol.io/extensions/apps/overview
- MCP Apps SDK/spec: https://github.com/modelcontextprotocol/ext-apps
- TypeScript SDK v2: https://github.com/modelcontextprotocol/typescript-sdk
- Current Qwen Code MCP dependency: https://github.com/QwenLM/qwen-code/blob/main/packages/core/package.json
- Current MCP discovery path: https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/tools/mcp-client.ts
- Current WebShell HTML iframe preview: https://github.com/QwenLM/qwen-code/blob/main/packages/web-shell/client/components/artifacts/ArtifactPanel.tsx
I searched existing Qwen Code issues for MCP Apps, MCP UI, 2026-07-28, stateless MCP, and SDK v2 before filing this request and did not find an equivalent issue.
Activity
samuelhsin commentedon Aug 12, 2026
Ecosystem update (verified 2026-08-12)
One important versioning correction: MCP Apps did not start with Core
2026-07-28. The first official MCP Apps extension specification is versioned2026-01-26. It negotiates theio.modelcontextprotocol/uiextension through the legacyinitializeflow; its example even uses CoreprotocolVersion: "2024-11-05". Core2026-07-28later formalized extension negotiation as part of the new stateless protocol. Therefore Core support and Apps Host support are two independent compatibility axes.2026-07-28enable_mcp_appsfeature under development and disabled by default; CLI/TUI is not a visual iframe hostserver/discover, MRTRinput_required, and legacy fallback)2026-07-28/server/discovermarkersio.modelcontextprotocol/ui,ui://, or AppBridge markers@modelcontextprotocol/sdk ^1.30.0ui://resource loading, MCP AppBridge/postMessage, or Apps lifecycle/state propagationThe official Apps overview currently also lists VS Code GitHub Copilot, Microsoft 365 Copilot, Goose, Postman, MCPJam, and Archestra.AI as supported Apps clients. This shows that MCP Apps already has a multi-client ecosystem independent of Core
2026-07-28.This issue should therefore keep two explicitly separate deliverables:
io.modelcontextprotocol/ui; preserve_meta.ui.resourceUri; loadui://+text/html;profile=mcp-app; render through the existing sandbox iframe; implement the host-side AppBridge, CSP/permissions, tool-call proxy, lifecycle, reload/resume, and fallback behavior.References:
yiliang114 commentedon Sep 24, 2026
Status re-checked against current
main(ca8146a). Leaving this open because some items remain.Resolved / no longer applicable
@modelcontextprotocol/client2.0.0 inpackages/core/package.json).Still open
input_required/inputResponses) andui/update-model-context(acceptance items 3 and 4): no occurrences onmain.BigDaddy1337 commentedon Oct 18, 2026
Concrete repro of the MCP Apps / deferred-form gap from a Desktop (Web Shell) session, desktop-v0.25.2 runtime:
pkg/github/ui_capability.go→shouldDeferToForm()returns true when the client advertises theio.modelcontextprotocol/uicapability, and the handler then returns the "An interactive form has been shown to the user ... the issue has NOT been created yet" stub without making the API call (pkg/github/issues.go,pkg/github/pullrequests.go).io.modelcontextprotocol/uiandmcp_appstrings are present in the bundled chunks of desktop-v0.25.2.packages/web-shell/client/components/messages/McpApp.tsx, but it mounts the iframe only when the tool display carries an object withtype: 'mcp_app'and a non-emptyhtml(plusserverName,resourceUri,fallbackText,toolResult,toolArguments); otherwise it rendersfallbackText.issue_writereturned the deferred-form stub as a tool error with plain text and nomcp_appdisplay. Nothing rendered, the write silently never happened, and there is no way for the user to complete or cancel it.So the failure mode is a silent dead end rather than a visible "form could not be rendered": advertising
io.modelcontextprotocol/uimakes servers route writes to a form the host never mounts, and neither the model nor the user gets an actionable error. RenderingfallbackText(or explicitly declining the deferral) would at least surface the state.This sits on the same surface as the MRTR
input_requiredwork in scope item 1 / acceptance criterion 4 — flagging the display-less deferral case so the surfacing path can cover it too.Paraphern commentedon Oct 22, 2026
The capability matrix above is also why one pin per server isn't enough: as @BigDaddy1337's repro shows, the GitHub server serves a different surface when the client advertises
io.modelcontextprotocol/ui— write tools deferred to forms. Capability-dependent surfaces need capability-aware pinning: pin what a client of your shape negotiates, re-diff after any runtime/Desktop update, and the regression shows up as contract drift instead of a mystery behavior change.We built the adjacent case into our CLI (rugsnare):
scan --chameleonre-lists one server as claude-desktop/cursor/openai-mcp and fails when the surfaces differ per client. The capability axis is the same mechanism one layer over.