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 <[email protected]>
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 <[email protected]>
Co-authored-by: Copilot <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
|
@copilot resolve the merge conflicts in this pull request |
# Conflicts: # dotnet/src/Types.cs
This comment has been minimized.
This comment has been minimized.
…tderr Writing directly to os.Stderr from library code is unsuppressible and unroutable. Switch to log.Printf so consumers can call log.Default().SetOutput(io.Discard) (or any other writer) to control the warning. Default behavior is unchanged (log.Default() writes to stderr). Co-authored-by: Copilot <[email protected]>
This comment has been minimized.
This comment has been minimized.
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: mattdholloway <[email protected]>
Merged |
This comment has been minimized.
This comment has been minimized.
| * `--no-warnings`, or filter by the `name` field below. | ||
| */ | ||
| function warnIfMcpAppsDropped( | ||
| requested: boolean | undefined, | ||
| capabilities: { ui?: { mcpApps?: boolean } } | undefined | ||
| ): void { | ||
| if (requested && !capabilities?.ui?.mcpApps) { | ||
| process.emitWarning( | ||
| "enableMcpApps was requested but the runtime did not advertise capabilities.ui.mcpApps. The runtime's MCP_APPS feature flag or COPILOT_MCP_APPS=true environment override is likely unset; the MCP Apps surface is unavailable for this session.", | ||
| "McpAppsCapabilityDroppedWarning" |
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: mattdholloway <[email protected]>
Merged |
This comment has been minimized.
This comment has been minimized.
Co-authored-by: Copilot <[email protected]>
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 · ● 6.5M
| */ | ||
| function warnIfMcpAppsDropped( | ||
| requested: boolean | undefined, | ||
| capabilities: { ui?: { mcpApps?: boolean } } | undefined |
There was a problem hiding this comment.
Minor cross-SDK inconsistency: warnIfMcpAppsDropped omits the session ID
All other SDK implementations of this warning helper accept and include the session ID in the log output, e.g.:
- Python:
"Session %s: enable_mcp_apps was requested but ..."(passessession_idtologger.warning) - Go:
"[copilot-sdk] Session %s: EnableMcpApps was requested but ..."(usessessionIDinlog.Printf) - .NET:
"Session {SessionId}: EnableMcpApps was requested but ..."(structured log field) - Java:
"Session " + sessionId + ": enableMcpApps was requested but ..." - Rust:
tracing::warn!(session_id = %session_id, "...")
The Node warning message says "the MCP Apps surface is unavailable for this session" without identifying which session, which makes it harder to correlate the warning to a specific session in multi-session hosts.
Suggestion: accept the sessionId as a third parameter and include it in the warning message for parity:
function warnIfMcpAppsDropped(
requested: boolean | undefined,
capabilities: { ui?: { mcpApps?: boolean } } | undefined,
sessionId: string
): void {
if (requested && !capabilities?.ui?.mcpApps) {
process.emitWarning(
`Session ${sessionId}: enableMcpApps was requested but the runtime did not advertise capabilities.ui.mcpApps. ...`,
"McpAppsCapabilityDroppedWarning"
);
}
}Co-authored-by: mattdholloway <[email protected]>
Co-authored-by: mattdholloway <[email protected]>
Cross-SDK Consistency Review ✅This PR adds Feature parity check
Language-idiomatic warning mechanismsEach SDK routes the capability-drop warning through its ecosystem-appropriate channel — Node.js-specific iframe sandbox helpers
Naming conventionsAll names correctly follow each language's convention:
No cross-SDK consistency issues found. 🎉
|
Adds opt-in MCP Apps (SEP-1865) support across all SDKs: a new
enableMcpAppssession flag, Node helpers for safely sandboxingui://iframe rendering, and regenerated RPC/session-event types.Changes
enableMcpAppsopt-in (SessionConfig+ResumeSessionConfigin all SDKs) — plumbed to wire fieldrequestMcpApps. Defaults tofalse; hosts without an iframe renderer are unaffected.enableMcpAppswas requested butcapabilities.ui.mcpAppswas not advertised by the runtime (feature flag unset). Node usesprocess.emitWarningwithcode: "MCPAPPS_CAPABILITY_DROPPED"and includes the session ID for correlation across multi-session hosts:buildMcpAppsCspHeader,buildMcpAppsAllowAttribute) — pure functions for constructing theContent-Security-Policyheader andallowattribute per SEP-1865 §UI Resource Format.SessionUiCapabilities.mcpApps.