Skip to content

Commit cf89893

Browse files
mattdhollowayCopilotCopilotcopilotSteveSandersonMS
authored
feat: add MCP Apps (SEP-1865) support (#1335)
* feat: add MCP Apps (SEP-1865) support 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> * feat: add MCP Apps option to Python, Go, .NET, Rust SDKs 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> * chore: prettier format mcpAppsSandbox files Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: sanitize CSP domain inputs in mcpAppsSandbox (SEP-1865) 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> * docs: note runtime MCP_APPS gate on enableMcpApps across SDKs 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> * feat: surface capabilities.ui.mcpApps and warn on silent drop 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> * fix: ruff format + add mcp_apps field to Rust e2e UiCapabilities literal - 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> * fix: drop sessionId from MCP Apps warning to silence CodeQL clear-text-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> * feat: add enableMcpApps support to Java SDK Mirrors the MCP Apps (SEP-1865) opt-in already wired into Node, Python, Go, .NET, and Rust: - SessionConfig / ResumeSessionConfig: enableMcpApps field with isEnableMcpApps / setEnableMcpApps accessors and copy() inclusion - CreateSessionRequest / ResumeSessionRequest: requestMcpApps wire field with getter/setter/clearer (Boolean nullable, matches requestElicitation) - SessionUiCapabilities: mcpApps response field with getter/setter/clearer - SessionRequestBuilder: wires config.isEnableMcpApps() -> requestMcpApps on both create and resume paths - CopilotClient: warnIfMcpAppsDropped helper logs when the consumer requested the opt-in but the runtime did not advertise it back (runtime silently drops the opt-in when its MCP_APPS feature flag / COPILOT_MCP_APPS env override is unset) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: address sanity-check findings - Revert unintended java/mvnw mode change (644 -> 755) introduced in the Java SDK commit; CI runs mvnw with explicit bash and doesn't require the exec bit. - Refresh Rust doc comments left stale after renaming request_mcp_apps -> enable_mcp_apps on the user-facing API (session.rs warn helper docstring + tracing message; UiCapabilities.mcp_apps cref). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * style: apply spotless formatting to Java MCP Apps additions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Omit requestMcpApps from wire payload when disabled Aligns Node.js and Python with Go/.NET/Java/Rust, which all omit the field when the feature is not opted in. Previously these two SDKs always sent requestMcpApps: false, cluttering protocol logs and risking ambiguity if the protocol ever distinguishes 'not sent' from 'explicitly false'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(mcp-apps): address high-priority review feedback - nodejs: switch warnIfMcpAppsDropped from console.warn to process.emitWarning with name McpAppsCapabilityDroppedWarning so consumers can route/suppress it (--no-warnings, process.on('warning', ...)) like any other Node deprecation warning. - go: add TestCreateSessionRequest_RequestMcpApps / TestResumeSessionRequest_RequestMcpApps mirroring the existing RequestElicitation marshal/omit tests. - rust: add session_config_enable_mcp_apps_sets_wire_flag_and_serializes and resume_session_config_enable_mcp_apps_sets_wire_flag_and_serializes to cover the opt-in path (config field -> wire flag -> requestMcpApps in serialized JSON). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(mcp-apps): address worth-doing review feedback - python: add enable_mcp_apps entries to create_session and resume_session docstring Args lists, describing the runtime gate and the capabilities.ui.mcpApps detection mechanism. - java: thread sessionId through warnIfMcpAppsDropped and include it in the warning message, matching the Python/Go/.NET/Rust pattern for multi-session debugging. - nodejs/test: replace the 'see review feedback' marker in the sandbox sanitization section header with a self-contained reference to SEP-1865 \xc2\xa7Security Implications. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(go): route MCP Apps warning through log.Default() instead of os.Stderr 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 <223556219+Copilot@users.noreply.github.com> * Revert mode change on .githooks/pre-commit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: include session ID in warnIfMcpAppsDropped warning Co-authored-by: mattdholloway <918573+mattdholloway@users.noreply.github.com> * fix: include session ID in warnIfMcpAppsDropped warning Co-authored-by: mattdholloway <918573+mattdholloway@users.noreply.github.com> * remove nodejs specific mcp apps sandbox code * fix: update session ID handling in MCP apps configuration * refactor: remove MCP Apps warning handling from multiple clients * style: remove unused SessionCapabilities import in Java client Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: address review feedback on MCP Apps PR - Fix broken @link in Java SessionConfig (com.github.copilot.sdk.CopilotSession -> com.github.copilot.CopilotSession) - Revert stray mode change on .githooks/pre-commit (100755 -> 100644) - Rust: make enable_mcp_apps Option<bool> for consistency with sibling opt-ins (e.g. enable_config_discovery) - Python: remove stray blank line after logger init in client.py - Drop incorrect 'the SDK also logs a warning' wording from Python docstrings (the SDK no longer emits a warning; only the runtime does) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: mark MCP Apps APIs as experimental Mark the new SEP-1865 MCP Apps public APIs as experimental across .NET, Node, Python, Go and Rust SDKs, following each SDK's existing convention (e.g. canvas surface): - .NET: [Experimental(Diagnostics.Experimental)] on SessionConfigBase.EnableMcpApps and SessionUiCapabilities.McpApps. No #pragma needed at internal call sites because GHCP001 is in the project's NoWarn. - Node: @experimental JSDoc tag on SessionConfigBase.enableMcpApps and SessionCapabilities.ui.mcpApps. - Python: **Experimental.** lead-in on enable_mcp_apps parameter docstrings (create_session, resume_session) and SessionUiCapabilities.mcpApps. - Go: // Experimental: ... doc lines on SessionConfig.EnableMcpApps, ResumeSessionConfig.EnableMcpApps and UICapabilities.McpApps. - Rust: **Experimental.** first paragraph on SessionConfig.enable_mcp_apps, ResumeSessionConfig.enable_mcp_apps, with_enable_mcp_apps (x2) and UiCapabilities.mcp_apps. Java is intentionally skipped — the repo has no precedent for marking Java APIs as experimental, so introducing a convention here is out of scope for this commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor(rust): drop skip_serializing_if from request_mcp_apps wire field Match the surrounding request_* bool fields (request_user_input, request_permission, request_exit_plan_mode, request_auto_mode_switch, request_elicitation, hooks) which all serialize unconditionally. Snapshots don't capture these fields so there is no compatibility cost. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * style: spotless reflow on SessionConfig MCP Apps javadoc Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: copilot <copilot@github.com> Co-authored-by: Steve Sanderson <SteveSandersonMS@users.noreply.github.com>
1 parent 45aae2f commit cf89893

18 files changed

Lines changed: 478 additions & 0 deletions

File tree

dotnet/src/Client.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,7 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
878878
config.InfiniteSessions,
879879
Commands: config.Commands?.Select(c => new CommandWireDefinition(c.Name, c.Description)).ToList(),
880880
RequestElicitation: config.OnElicitationRequest != null,
881+
RequestMcpApps: config.EnableMcpApps ? true : null,
881882
Traceparent: traceparent,
882883
Tracestate: tracestate,
883884
ModelCapabilities: config.ModelCapabilities,
@@ -1059,6 +1060,7 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
10591060
config.InfiniteSessions,
10601061
Commands: config.Commands?.Select(c => new CommandWireDefinition(c.Name, c.Description)).ToList(),
10611062
RequestElicitation: config.OnElicitationRequest != null,
1063+
RequestMcpApps: config.EnableMcpApps ? true : null,
10621064
Traceparent: traceparent,
10631065
Tracestate: tracestate,
10641066
ModelCapabilities: config.ModelCapabilities,
@@ -2171,6 +2173,7 @@ internal record CreateSessionRequest(
21712173
InfiniteSessionConfig? InfiniteSessions,
21722174
IList<CommandWireDefinition>? Commands = null,
21732175
bool? RequestElicitation = null,
2176+
bool? RequestMcpApps = null,
21742177
string? Traceparent = null,
21752178
string? Tracestate = null,
21762179
ModelCapabilitiesOverride? ModelCapabilities = null,
@@ -2243,6 +2246,7 @@ internal record ResumeSessionRequest(
22432246
InfiniteSessionConfig? InfiniteSessions,
22442247
IList<CommandWireDefinition>? Commands = null,
22452248
bool? RequestElicitation = null,
2249+
bool? RequestMcpApps = null,
22462250
string? Traceparent = null,
22472251
string? Tracestate = null,
22482252
ModelCapabilitiesOverride? ModelCapabilities = null,

dotnet/src/Types.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,16 @@ public sealed class SessionUiCapabilities
11301130
/// Whether the host supports interactive elicitation dialogs.
11311131
/// </summary>
11321132
public bool? Elicitation { get; set; }
1133+
1134+
/// <summary>
1135+
/// Whether the runtime has accepted the session's MCP Apps (SEP-1865) opt-in.
1136+
/// <c>true</c> when the consumer set <see cref="SessionConfigBase.EnableMcpApps"/>
1137+
/// to <c>true</c> on create/resume <b>and</b> the runtime's <c>MCP_APPS</c> feature flag
1138+
/// (or <c>COPILOT_MCP_APPS=true</c> env override) is on. Otherwise absent or
1139+
/// <c>false</c>, indicating the runtime silently dropped the opt-in.
1140+
/// </summary>
1141+
[Experimental(Diagnostics.Experimental)]
1142+
public bool? McpApps { get; set; }
11331143
}
11341144

11351145
// ============================================================================
@@ -2346,6 +2356,7 @@ protected SessionConfigBase(SessionConfigBase? other)
23462356
Agent = other.Agent;
23472357
DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null;
23482358
EnableConfigDiscovery = other.EnableConfigDiscovery;
2359+
EnableMcpApps = other.EnableMcpApps;
23492360
ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null;
23502361
Hooks = other.Hooks;
23512362
InfiniteSessions = other.InfiniteSessions;
@@ -2507,6 +2518,31 @@ protected SessionConfigBase(SessionConfigBase? other)
25072518
/// <summary>Handler for auto-mode-switch requests from the server.</summary>
25082519
public Func<AutoModeSwitchRequest, AutoModeSwitchInvocation, Task<AutoModeSwitchResponse>>? OnAutoModeSwitchRequest { get; set; }
25092520

2521+
/// <summary>
2522+
/// Enable MCP Apps (SEP-1865) UI passthrough on this session.
2523+
/// <para>
2524+
/// When <c>true</c> <b>and</b> the runtime has MCP Apps enabled (via the
2525+
/// <c>MCP_APPS</c> feature flag or <c>COPILOT_MCP_APPS=true</c> environment override), the
2526+
/// runtime adds the <c>mcp-apps</c> capability to the session, which causes it to advertise
2527+
/// the <c>extensions.io.modelcontextprotocol/ui</c> extension to MCP servers (so they expose
2528+
/// <c>_meta.ui.resourceUri</c> on tools) and to expose the
2529+
/// <c>session.rpc.mcp.apps.{listTools,callTool,readResource,setHostContext,getHostContext,diagnose}</c>
2530+
/// JSON-RPC methods.
2531+
/// </para>
2532+
/// <para>
2533+
/// If the runtime gate is off, the opt-in is silently dropped server-side (the runtime logs a
2534+
/// warning); the session is created normally but the MCP Apps surface is unavailable. Inspect
2535+
/// the runtime's <c>capabilities.ui.mcpApps</c> on the create/resume response to detect this.
2536+
/// </para>
2537+
/// <para>
2538+
/// SDK consumers MUST set this to <c>true</c> only when they have an iframe renderer that can
2539+
/// display <c>ui://</c> MCP App bundles. Setting it without a renderer will cause MCP servers
2540+
/// to register UI-enabled tool variants the consumer cannot display.
2541+
/// </para>
2542+
/// </summary>
2543+
[Experimental(Diagnostics.Experimental)]
2544+
public bool EnableMcpApps { get; set; }
2545+
25102546
/// <summary>Hook handlers for session lifecycle events.</summary>
25112547
public SessionHooks? Hooks { get; set; }
25122548

go/client.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,9 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses
659659
if config.OnAutoModeSwitchRequest != nil {
660660
req.RequestAutoModeSwitch = Bool(true)
661661
}
662+
if config.EnableMcpApps {
663+
req.RequestMcpApps = Bool(true)
664+
}
662665

663666
if config.Streaming != nil {
664667
req.Streaming = config.Streaming
@@ -985,6 +988,9 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string,
985988
if config.OnAutoModeSwitchRequest != nil {
986989
req.RequestAutoModeSwitch = Bool(true)
987990
}
991+
if config.EnableMcpApps {
992+
req.RequestMcpApps = Bool(true)
993+
}
988994

989995
traceparent, tracestate := getTraceContext(ctx)
990996
req.Traceparent = traceparent

go/client_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,65 @@ func TestResumeSessionRequest_RequestElicitation(t *testing.T) {
946946
})
947947
}
948948

949+
func TestCreateSessionRequest_RequestMcpApps(t *testing.T) {
950+
t.Run("sends requestMcpApps flag when EnableMcpApps is set", func(t *testing.T) {
951+
req := createSessionRequest{
952+
RequestMcpApps: Bool(true),
953+
}
954+
data, err := json.Marshal(req)
955+
if err != nil {
956+
t.Fatalf("Failed to marshal: %v", err)
957+
}
958+
var m map[string]any
959+
if err := json.Unmarshal(data, &m); err != nil {
960+
t.Fatalf("Failed to unmarshal: %v", err)
961+
}
962+
if m["requestMcpApps"] != true {
963+
t.Errorf("Expected requestMcpApps to be true, got %v", m["requestMcpApps"])
964+
}
965+
})
966+
967+
t.Run("does not send requestMcpApps when EnableMcpApps is unset", func(t *testing.T) {
968+
req := createSessionRequest{}
969+
data, _ := json.Marshal(req)
970+
var m map[string]any
971+
json.Unmarshal(data, &m)
972+
if _, ok := m["requestMcpApps"]; ok {
973+
t.Error("Expected requestMcpApps to be omitted when not set")
974+
}
975+
})
976+
}
977+
978+
func TestResumeSessionRequest_RequestMcpApps(t *testing.T) {
979+
t.Run("sends requestMcpApps flag when EnableMcpApps is set", func(t *testing.T) {
980+
req := resumeSessionRequest{
981+
SessionID: "s1",
982+
RequestMcpApps: Bool(true),
983+
}
984+
data, err := json.Marshal(req)
985+
if err != nil {
986+
t.Fatalf("Failed to marshal: %v", err)
987+
}
988+
var m map[string]any
989+
if err := json.Unmarshal(data, &m); err != nil {
990+
t.Fatalf("Failed to unmarshal: %v", err)
991+
}
992+
if m["requestMcpApps"] != true {
993+
t.Errorf("Expected requestMcpApps to be true, got %v", m["requestMcpApps"])
994+
}
995+
})
996+
997+
t.Run("does not send requestMcpApps when EnableMcpApps is unset", func(t *testing.T) {
998+
req := resumeSessionRequest{SessionID: "s1"}
999+
data, _ := json.Marshal(req)
1000+
var m map[string]any
1001+
json.Unmarshal(data, &m)
1002+
if _, ok := m["requestMcpApps"]; ok {
1003+
t.Error("Expected requestMcpApps to be omitted when not set")
1004+
}
1005+
})
1006+
}
1007+
9491008
func TestResumeSessionRequest_ModeCallbackFlags(t *testing.T) {
9501009
req := resumeSessionRequest{
9511010
SessionID: "s1",

go/types.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,29 @@ type SessionConfig struct {
987987
// OnAutoModeSwitchRequest is a handler for auto-mode-switch requests from the server.
988988
// When provided, enables autoModeSwitch.request callbacks for the session.
989989
OnAutoModeSwitchRequest AutoModeSwitchRequestHandler
990+
// EnableMcpApps enables MCP Apps (SEP-1865) UI passthrough on this session.
991+
//
992+
// Experimental: EnableMcpApps is part of an experimental wire-protocol
993+
// surface (SEP-1865) and may change or be removed in a future release.
994+
//
995+
// When true AND the runtime has MCP Apps enabled (via the MCP_APPS feature
996+
// flag or COPILOT_MCP_APPS=true environment override), the runtime adds the
997+
// mcp-apps capability to the session, which causes it to advertise the
998+
// extensions.io.modelcontextprotocol/ui extension to MCP servers (so they
999+
// expose _meta.ui.resourceUri on tools) and to expose the
1000+
// session.rpc.mcp.apps.{listTools,callTool,readResource,setHostContext,
1001+
// getHostContext,diagnose} JSON-RPC methods.
1002+
//
1003+
// If the runtime gate is off, the opt-in is silently dropped server-side
1004+
// (the runtime logs a warning); the session is created normally but the
1005+
// MCP Apps surface is unavailable. Inspect the runtime's
1006+
// capabilities.ui.mcpApps on the create/resume response to detect this.
1007+
//
1008+
// SDK consumers MUST set this to true only when they have an iframe renderer
1009+
// that can display ui:// MCP App bundles. Setting it without a renderer will
1010+
// cause MCP servers to register UI-enabled tool variants the consumer cannot
1011+
// display.
1012+
EnableMcpApps bool
9901013
// GitHubToken is an optional per-session GitHub token used for authentication.
9911014
// When provided, the session authenticates as the token's owner instead of
9921015
// using the global client-level auth.
@@ -1089,6 +1112,15 @@ type SessionCapabilities struct {
10891112
type UICapabilities struct {
10901113
// Elicitation indicates whether the host supports interactive elicitation dialogs.
10911114
Elicitation bool `json:"elicitation,omitempty"`
1115+
// McpApps indicates whether the runtime has accepted the session's MCP Apps
1116+
// (SEP-1865) opt-in. True when the consumer set EnableMcpApps=true on
1117+
// create/resume AND the runtime's MCP_APPS feature flag (or
1118+
// COPILOT_MCP_APPS=true env override) is on. Otherwise false, indicating
1119+
// the runtime silently dropped the opt-in.
1120+
//
1121+
// Experimental: McpApps is part of an experimental wire-protocol surface
1122+
// (SEP-1865) and may change or be removed in a future release.
1123+
McpApps bool `json:"mcpApps,omitempty"`
10921124
}
10931125

10941126
// ElicitationResult is the user's response to an elicitation dialog.
@@ -1275,6 +1307,12 @@ type ResumeSessionConfig struct {
12751307
// OnAutoModeSwitchRequest is a handler for auto-mode-switch requests from the server.
12761308
// See SessionConfig.OnAutoModeSwitchRequest.
12771309
OnAutoModeSwitchRequest AutoModeSwitchRequestHandler
1310+
// EnableMcpApps enables MCP Apps (SEP-1865) UI passthrough on resume.
1311+
// See SessionConfig.EnableMcpApps.
1312+
//
1313+
// Experimental: EnableMcpApps is part of an experimental wire-protocol
1314+
// surface (SEP-1865) and may change or be removed in a future release.
1315+
EnableMcpApps bool
12781316
// Canvases declares canvases this session provides. Sent over the wire on
12791317
// `session.resume`. See SessionConfig.Canvases.
12801318
Canvases []CanvasDeclaration
@@ -1534,6 +1572,7 @@ type createSessionRequest struct {
15341572
InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"`
15351573
Commands []wireCommand `json:"commands,omitempty"`
15361574
RequestElicitation *bool `json:"requestElicitation,omitempty"`
1575+
RequestMcpApps *bool `json:"requestMcpApps,omitempty"`
15371576
GitHubToken string `json:"gitHubToken,omitempty"`
15381577
RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"`
15391578
Cloud *CloudSessionOptions `json:"cloud,omitempty"`
@@ -1599,6 +1638,7 @@ type resumeSessionRequest struct {
15991638
InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"`
16001639
Commands []wireCommand `json:"commands,omitempty"`
16011640
RequestElicitation *bool `json:"requestElicitation,omitempty"`
1641+
RequestMcpApps *bool `json:"requestMcpApps,omitempty"`
16021642
GitHubToken string `json:"gitHubToken,omitempty"`
16031643
RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"`
16041644
Canvases []CanvasDeclaration `json:"canvases,omitempty"`

java/src/main/java/com/github/copilot/SessionRequestBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
144144
if (config.getOnElicitationRequest() != null) {
145145
request.setRequestElicitation(true);
146146
}
147+
if (config.isEnableMcpApps()) {
148+
request.setRequestMcpApps(true);
149+
}
147150
if (config.getOnExitPlanMode() != null) {
148151
request.setRequestExitPlanMode(true);
149152
}
@@ -238,6 +241,9 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo
238241
if (config.getOnElicitationRequest() != null) {
239242
request.setRequestElicitation(true);
240243
}
244+
if (config.isEnableMcpApps()) {
245+
request.setRequestMcpApps(true);
246+
}
241247
if (config.getOnExitPlanMode() != null) {
242248
request.setRequestExitPlanMode(true);
243249
}

java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ public final class CreateSessionRequest {
114114
@JsonProperty("requestElicitation")
115115
private Boolean requestElicitation;
116116

117+
@JsonProperty("requestMcpApps")
118+
private Boolean requestMcpApps;
119+
117120
@JsonProperty("requestExitPlanMode")
118121
private Boolean requestExitPlanMode;
119122

@@ -503,6 +506,21 @@ public void clearRequestElicitation() {
503506
this.requestElicitation = null;
504507
}
505508

509+
/** Gets the requestMcpApps flag. @return the flag */
510+
public Boolean getRequestMcpApps() {
511+
return requestMcpApps;
512+
}
513+
514+
/** Sets the requestMcpApps flag. @param requestMcpApps the flag */
515+
public void setRequestMcpApps(boolean requestMcpApps) {
516+
this.requestMcpApps = requestMcpApps;
517+
}
518+
519+
/** Clears the requestMcpApps setting, reverting to the default behavior. */
520+
public void clearRequestMcpApps() {
521+
this.requestMcpApps = null;
522+
}
523+
506524
/** Gets the requestExitPlanMode flag. @return the flag */
507525
public Boolean getRequestExitPlanMode() {
508526
return requestExitPlanMode;

java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public class ResumeSessionConfig {
7474
private ElicitationHandler onElicitationRequest;
7575
private ExitPlanModeHandler onExitPlanMode;
7676
private AutoModeSwitchHandler onAutoModeSwitch;
77+
private boolean enableMcpApps;
7778
private String gitHubToken;
7879
private String remoteSession;
7980

@@ -972,6 +973,31 @@ public ResumeSessionConfig setOnElicitationRequest(ElicitationHandler onElicitat
972973
return this;
973974
}
974975

976+
/**
977+
* Returns whether MCP Apps (SEP-1865) UI passthrough is enabled on resume.
978+
*
979+
* @return {@code true} if the consumer has opted into MCP Apps, otherwise
980+
* {@code false}
981+
* @see #setEnableMcpApps(boolean)
982+
*/
983+
public boolean isEnableMcpApps() {
984+
return enableMcpApps;
985+
}
986+
987+
/**
988+
* Enables MCP Apps (SEP-1865) UI passthrough on the resumed session. See
989+
* {@link SessionConfig#setEnableMcpApps(boolean)} for full semantics (runtime
990+
* gate, capability inspection, renderer requirement).
991+
*
992+
* @param enableMcpApps
993+
* {@code true} to opt into MCP Apps support on resume
994+
* @return this config for method chaining
995+
*/
996+
public ResumeSessionConfig setEnableMcpApps(boolean enableMcpApps) {
997+
this.enableMcpApps = enableMcpApps;
998+
return this;
999+
}
1000+
9751001
/**
9761002
* Gets the exit-plan-mode request handler.
9771003
*
@@ -1129,6 +1155,7 @@ public ResumeSessionConfig clone() {
11291155
copy.onElicitationRequest = this.onElicitationRequest;
11301156
copy.onExitPlanMode = this.onExitPlanMode;
11311157
copy.onAutoModeSwitch = this.onAutoModeSwitch;
1158+
copy.enableMcpApps = this.enableMcpApps;
11321159
copy.gitHubToken = this.gitHubToken;
11331160
copy.remoteSession = this.remoteSession;
11341161
return copy;

java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ public final class ResumeSessionRequest {
119119
@JsonProperty("requestElicitation")
120120
private Boolean requestElicitation;
121121

122+
@JsonProperty("requestMcpApps")
123+
private Boolean requestMcpApps;
124+
122125
@JsonProperty("requestExitPlanMode")
123126
private Boolean requestExitPlanMode;
124127

@@ -528,6 +531,21 @@ public void clearRequestElicitation() {
528531
this.requestElicitation = null;
529532
}
530533

534+
/** Gets the requestMcpApps flag. @return the flag */
535+
public Boolean getRequestMcpApps() {
536+
return requestMcpApps;
537+
}
538+
539+
/** Sets the requestMcpApps flag. @param requestMcpApps the flag */
540+
public void setRequestMcpApps(boolean requestMcpApps) {
541+
this.requestMcpApps = requestMcpApps;
542+
}
543+
544+
/** Clears the requestMcpApps setting, reverting to the default behavior. */
545+
public void clearRequestMcpApps() {
546+
this.requestMcpApps = null;
547+
}
548+
531549
/** Gets the requestExitPlanMode flag. @return the flag */
532550
public Boolean getRequestExitPlanMode() {
533551
return requestExitPlanMode;

0 commit comments

Comments
 (0)