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
| * | ||
| * @default false | ||
| */ | ||
| enableMcpApps?: boolean; |
There was a problem hiding this comment.
Cross-SDK consistency gap: enableMcpApps is added here and to ResumeSessionConfig in Node.js, but the equivalent option is missing from the other four SDK implementations.
The requestMcpApps wire flag won't be sent by Python, Go, .NET, or Rust sessions, so hosts using those SDKs can't opt in to MCP Apps support. Suggested additions to bring the other SDKs into parity:
| SDK | Location | What to add |
|---|---|---|
| Python | python/copilot/client.py — create_session() + resume_session() signatures |
enable_mcp_apps: bool = False parameter; payload["requestMcpApps"] = enable_mcp_apps |
| Go | go/types.go — SessionConfig + ResumeSessionConfig |
EnableMcpApps bool field; set req.RequestMcpApps = Bool(true) in client.go when the flag is set |
| .NET | dotnet/src/Types.cs — SessionConfig + ResumeSessionConfig |
public bool EnableMcpApps { get; set; } property; pass RequestMcpApps: config.EnableMcpApps ? true : null in Client.cs |
| Rust | rust/src/types.rs — SessionConfig + ResumeSessionConfig |
pub request_mcp_apps: Option<bool> field + pub fn with_request_mcp_apps(mut self, enable: bool) -> Self builder |
There was a problem hiding this comment.
@copilot Complete the implementation of the SDK surfaces across all implementations
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>
Cross-SDK Consistency Review ✅This PR adds
Notes:
No cross-SDK consistency issues identified. 🎉
|
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