feat: add MCP Apps (SEP-1865) support#1335
Conversation
|
@copilot resolve the merge conflicts in this pull request |
Adds opt-in 'enableMcpApps' session capability that advertises the
'extensions.io.modelcontextprotocol/ui' extension to MCP servers and
exposes 'session.rpc.mcp.apps.*' JSON-RPC methods.
Node SDK gains two pure helpers for hosts rendering 'ui://' MCP App
bundles in iframes:
- buildMcpAppsCspHeader — constructs the Content-Security-Policy header
per SEP-1865 §UI Resource Format + §Security Implications, including
the restrictive default ('connect-src none') when '_meta.ui.csp' is
absent and constructed defaults ('connect-src self', etc.) when it is
declared.
- buildMcpAppsAllowAttribute — maps '_meta.ui.permissions' to the iframe
'allow' attribute (Permission Policy).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
5f12d41 to
0827b5a
Compare
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Generated by SDK Consistency Review Agent for issue #1335 · ● 793.2K
Mirror nodejs enableMcpApps across the other four SDKs so hosts using them can opt into MCP Apps (SEP-1865) UI passthrough by sending requestMcpApps on session.create / session.resume. - python: enable_mcp_apps kwarg on create_session / resume_session - go: EnableMcpApps field on SessionConfig / ResumeSessionConfig - dotnet: EnableMcpApps property on SessionConfig / ResumeSessionConfig - rust: request_mcp_apps field + with_request_mcp_apps builder Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Port the CSP directive injection defense from copilot-agent-runtime PR #7605 into the SDK. Without sanitization, an MCP server returning `frameDomains: ['evil.com; form-action *']` could break out of one CSP directive and inject sibling directives (CSP first-occurrence rule then lets an earlier injected `script-src *` win). Each server-supplied entry is now: - rejected if it contains CSP metacharacters ([;,\\s'"\\\\]) - accepted verbatim for the bare-scheme allowlist (data:, blob:, mediastream:, filesystem:) - otherwise parsed via URL and canonicalized to its origin; opaque origins (where `URL.origin` is the literal string 'null') are dropped Adds 10 sanitization tests mirroring runtime PR coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reflect the runtime-side gate added in copilot-agent-runtime PR #7605: requestMcpApps is now honored server-side only when the MCP_APPS feature flag or COPILOT_MCP_APPS=true env override is set; otherwise the opt-in is silently dropped (the runtime logs a warning, but the SDK consumer sees nothing). Update the JSDoc / docstrings on Node, Go, .NET, and Rust to document this and to point at capabilities.ui.mcpApps on the create/resume response as the way to detect the silent drop. Also adds the diagnose method to the enumerated mcp.apps.* RPCs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Expose the runtime's response capability so consumers can detect when their enableMcpApps opt-in was silently dropped by the runtime gate (MCP_APPS feature flag / COPILOT_MCP_APPS env override unset). For each SDK: - Add mcpApps?: bool to the SessionUiCapabilities type - After session.create / session.resume, if the consumer requested the opt-in but capabilities.ui.mcpApps is not true on the response, log a warning (console.warn / logger.warning / slog / tracing::warn / fmt.Fprintf(os.Stderr, ...)) so the silent drop is discoverable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
- python: ruff format reflowed the new _warn_if_mcp_apps_dropped helper - rust: tests/e2e/elicitation.rs constructs UiCapabilities as a struct literal; the new mcp_apps field made it non-exhaustive Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
…t-logging CodeQL flags any value flowing from process.env as sensitive via taint analysis (joinSession() reads process.env.SESSION_ID which propagates to resumeSession's sessionId argument). The session ID is a UUID and not actually sensitive, but the alert noise is not worth it -- the warning is per-call so the consumer already knows which session triggered it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@copilot resolve the merge conflicts in this pull request |
# Conflicts: # dotnet/src/Types.cs
Cross-SDK Consistency ReviewI reviewed this PR across all five SDK implementations (Node.js, Python, Go, .NET, Rust) for feature parity and API consistency. ✅ Consistent across all SDKs
The core 🟡 Intentionally Node.js-only:
|
Adds opt-in MCP Apps (SEP-1865) support to the SDK.
What
enableMcpAppssession capability (SessionConfig+ResumeSessionConfig). Whentrue, the runtime advertises theextensions.io.modelcontextprotocol/uiextension to MCP servers and exposes thesession.rpc.mcp.apps.{listTools,callTool,readResource,setHostContext,getHostContext}JSON-RPC methods. Defaults tofalseso hosts without an iframe renderer don't accidentally register UI-enabled tool variants they can't display.ui://MCP App bundles in iframes:buildMcpAppsCspHeader(csp)— builds theContent-Security-Policyheader per SEP-1865 §UI Resource Format + §Security Implications. Emits the restrictive default (connect-src 'none') when_meta.ui.cspis absent, and the constructed default (connect-src 'self'+ declared domains, etc.) when it is declared (even with empty arrays).buildMcpAppsAllowAttribute(permissions)— maps_meta.ui.permissionsto the iframeallowattribute (Permission Policy), including the hyphenatedclipboard-writename.sequenceDiagram autonumber participant Host as Host App participant SDK as Copilot SDK participant Sandbox as mcpAppsSandbox helpers participant Conn as JSON-RPC Connection participant Runtime as Copilot Runtime participant Iframe as ui iframe Note over Host,Runtime: 1. Opt-in via SDK config Host->>SDK: createSession({ enableMcpApps: true }) SDK->>Conn: session.create { ..., requestMcpApps: true } Conn->>Runtime: JSON-RPC request Runtime-->>Conn: CreateSessionResponse Conn-->>SDK: response SDK-->>Host: CopilotSession Note over Runtime,Host: 2. Tool execution events forwarded unchanged Runtime-->>Conn: tool.execution_complete { uiResource, toolDescription._meta.ui } Conn-->>SDK: SessionEvent (untyped pass-through) SDK-->>Host: onEvent(event) Note over Host,Iframe: 3. Host builds sandboxed iframe using SDK helpers Host->>Sandbox: buildMcpAppsCspHeader(uiResource._meta.ui.csp) Sandbox-->>Host: CSP header value Host->>Sandbox: buildMcpAppsAllowAttribute(uiResource._meta.ui.permissions) Sandbox-->>Host: allow attribute value Host->>Iframe: render with CSP + allow + uiResource Note over Iframe,Runtime: 4. App-to-server RPCs (no typed SDK surface) Iframe->>Host: postMessage RPC Host->>Conn: raw JSON-RPC: session.rpc.mcp.apps.{listTools|callTool|readResource} Conn->>Runtime: forwarded Runtime-->>Conn: result (+ optional ephemeral mcp_app.tool_call_complete event) Conn-->>SDK: SessionEvent (untyped pass-through, callTool only) SDK-->>Host: onEvent(event) Conn-->>Host: RPC response Host-->>Iframe: postMessage response Note over Host,Runtime: 5. Resume preserves opt-in Host->>SDK: resumeSession({ enableMcpApps: true }) SDK->>Conn: session.resume { ..., requestMcpApps: true }Closes https://github.com/github/copilot-mcp-core/issues/1715
Depends on https://github.com/github/copilot-agent-runtime/pull/7605