Skip to content

Commit f251469

Browse files
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>
1 parent 0d544c0 commit f251469

6 files changed

Lines changed: 98 additions & 0 deletions

File tree

dotnet/src/Client.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,7 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
634634
config.InfiniteSessions,
635635
Commands: config.Commands?.Select(c => new CommandWireDefinition(c.Name, c.Description)).ToList(),
636636
RequestElicitation: config.OnElicitationRequest != null,
637+
RequestMcpApps: config.EnableMcpApps ? true : null,
637638
Traceparent: traceparent,
638639
Tracestate: tracestate,
639640
ModelCapabilities: config.ModelCapabilities,
@@ -793,6 +794,7 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
793794
config.InfiniteSessions,
794795
Commands: config.Commands?.Select(c => new CommandWireDefinition(c.Name, c.Description)).ToList(),
795796
RequestElicitation: config.OnElicitationRequest != null,
797+
RequestMcpApps: config.EnableMcpApps ? true : null,
796798
Traceparent: traceparent,
797799
Tracestate: tracestate,
798800
ModelCapabilities: config.ModelCapabilities,
@@ -2025,6 +2027,7 @@ internal record CreateSessionRequest(
20252027
InfiniteSessionConfig? InfiniteSessions,
20262028
IList<CommandWireDefinition>? Commands = null,
20272029
bool? RequestElicitation = null,
2030+
bool? RequestMcpApps = null,
20282031
string? Traceparent = null,
20292032
string? Tracestate = null,
20302033
ModelCapabilitiesOverride? ModelCapabilities = null,
@@ -2087,6 +2090,7 @@ internal record ResumeSessionRequest(
20872090
InfiniteSessionConfig? InfiniteSessions,
20882091
IList<CommandWireDefinition>? Commands = null,
20892092
bool? RequestElicitation = null,
2093+
bool? RequestMcpApps = null,
20902094
string? Traceparent = null,
20912095
string? Tracestate = null,
20922096
ModelCapabilitiesOverride? ModelCapabilities = null,

dotnet/src/Types.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,6 +2058,7 @@ protected SessionConfig(SessionConfig? other)
20582058
Agent = other.Agent;
20592059
DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null;
20602060
EnableConfigDiscovery = other.EnableConfigDiscovery;
2061+
EnableMcpApps = other.EnableMcpApps;
20612062
ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null;
20622063
Hooks = other.Hooks;
20632064
InfiniteSessions = other.InfiniteSessions;
@@ -2210,6 +2211,23 @@ protected SessionConfig(SessionConfig? other)
22102211
/// </summary>
22112212
public AutoModeSwitchHandler? OnAutoModeSwitch { get; set; }
22122213

2214+
/// <summary>
2215+
/// Enable MCP Apps (SEP-1865) UI passthrough on this session.
2216+
/// <para>
2217+
/// When <c>true</c>, the runtime adds the <c>mcp-apps</c> capability to the session, which
2218+
/// causes it to advertise the <c>extensions.io.modelcontextprotocol/ui</c> extension to MCP
2219+
/// servers (so they expose <c>_meta.ui.resourceUri</c> on tools) and to expose the
2220+
/// <c>session.rpc.mcp.apps.{listTools,callTool,readResource,setHostContext,getHostContext}</c>
2221+
/// JSON-RPC methods.
2222+
/// </para>
2223+
/// <para>
2224+
/// SDK consumers MUST set this to <c>true</c> only when they have an iframe renderer that can
2225+
/// display <c>ui://</c> MCP App bundles. Setting it without a renderer will cause MCP servers
2226+
/// to register UI-enabled tool variants the consumer cannot display.
2227+
/// </para>
2228+
/// </summary>
2229+
public bool EnableMcpApps { get; set; }
2230+
22132231
/// <summary>
22142232
/// Hook handlers for session lifecycle events.
22152233
/// </summary>
@@ -2370,6 +2388,7 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)
23702388
EnableConfigDiscovery = other.EnableConfigDiscovery;
23712389
ContinuePendingWork = other.ContinuePendingWork;
23722390
ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null;
2391+
EnableMcpApps = other.EnableMcpApps;
23732392
Hooks = other.Hooks;
23742393
InfiniteSessions = other.InfiniteSessions;
23752394
McpServers = other.McpServers is not null
@@ -2500,6 +2519,12 @@ protected ResumeSessionConfig(ResumeSessionConfig? other)
25002519
/// </summary>
25012520
public AutoModeSwitchHandler? OnAutoModeSwitch { get; set; }
25022521

2522+
/// <summary>
2523+
/// Enable MCP Apps (SEP-1865) UI passthrough on the resumed session.
2524+
/// See <see cref="SessionConfig.EnableMcpApps"/>.
2525+
/// </summary>
2526+
public bool EnableMcpApps { get; set; }
2527+
25032528
/// <summary>
25042529
/// Hook handlers for session lifecycle events.
25052530
/// </summary>

go/client.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,9 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses
663663
if config.OnAutoModeSwitch != nil {
664664
req.RequestAutoModeSwitch = Bool(true)
665665
}
666+
if config.EnableMcpApps {
667+
req.RequestMcpApps = Bool(true)
668+
}
666669

667670
if config.Streaming {
668671
req.Streaming = Bool(true)
@@ -866,6 +869,9 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string,
866869
if config.OnAutoModeSwitch != nil {
867870
req.RequestAutoModeSwitch = Bool(true)
868871
}
872+
if config.EnableMcpApps {
873+
req.RequestMcpApps = Bool(true)
874+
}
869875

870876
traceparent, tracestate := getTraceContext(ctx)
871877
req.Traceparent = traceparent

go/types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,19 @@ type SessionConfig struct {
688688
// OnAutoModeSwitch is a handler for auto-mode-switch requests from the server.
689689
// When provided, enables autoModeSwitch.request callbacks for the session.
690690
OnAutoModeSwitch AutoModeSwitchHandler
691+
// EnableMcpApps enables MCP Apps (SEP-1865) UI passthrough on this session.
692+
//
693+
// When true, the runtime adds the mcp-apps capability to the session, which
694+
// causes it to advertise the extensions.io.modelcontextprotocol/ui extension
695+
// to MCP servers (so they expose _meta.ui.resourceUri on tools) and to expose
696+
// the session.rpc.mcp.apps.{listTools,callTool,readResource,setHostContext,
697+
// getHostContext} JSON-RPC methods.
698+
//
699+
// SDK consumers MUST set this to true only when they have an iframe renderer
700+
// that can display ui:// MCP App bundles. Setting it without a renderer will
701+
// cause MCP servers to register UI-enabled tool variants the consumer cannot
702+
// display.
703+
EnableMcpApps bool
691704
// GitHubToken is an optional per-session GitHub token used for authentication.
692705
// When provided, the session authenticates as the token's owner instead of
693706
// using the global client-level auth.
@@ -947,6 +960,9 @@ type ResumeSessionConfig struct {
947960
// OnAutoModeSwitch is a handler for auto-mode-switch requests from the server.
948961
// See SessionConfig.OnAutoModeSwitch.
949962
OnAutoModeSwitch AutoModeSwitchHandler
963+
// EnableMcpApps enables MCP Apps (SEP-1865) UI passthrough on resume.
964+
// See SessionConfig.EnableMcpApps.
965+
EnableMcpApps bool
950966
}
951967
type ProviderConfig struct {
952968
// Type is the provider type: "openai", "azure", or "anthropic". Defaults to "openai".
@@ -1168,6 +1184,7 @@ type createSessionRequest struct {
11681184
InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"`
11691185
Commands []wireCommand `json:"commands,omitempty"`
11701186
RequestElicitation *bool `json:"requestElicitation,omitempty"`
1187+
RequestMcpApps *bool `json:"requestMcpApps,omitempty"`
11711188
GitHubToken string `json:"gitHubToken,omitempty"`
11721189
RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"`
11731190
Cloud *CloudSessionOptions `json:"cloud,omitempty"`
@@ -1224,6 +1241,7 @@ type resumeSessionRequest struct {
12241241
InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"`
12251242
Commands []wireCommand `json:"commands,omitempty"`
12261243
RequestElicitation *bool `json:"requestElicitation,omitempty"`
1244+
RequestMcpApps *bool `json:"requestMcpApps,omitempty"`
12271245
GitHubToken string `json:"gitHubToken,omitempty"`
12281246
RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"`
12291247
Traceparent string `json:"traceparent,omitempty"`

python/copilot/client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,7 @@ async def create_session(
13541354
on_elicitation_request: ElicitationHandler | None = None,
13551355
on_exit_plan_mode: ExitPlanModeHandler | None = None,
13561356
on_auto_mode_switch: AutoModeSwitchHandler | None = None,
1357+
enable_mcp_apps: bool = False,
13571358
create_session_fs_handler: CreateSessionFsHandler | None = None,
13581359
github_token: str | None = None,
13591360
remote_session: RemoteSessionMode | None = None,
@@ -1497,6 +1498,7 @@ async def create_session(
14971498
payload["requestElicitation"] = bool(on_elicitation_request)
14981499
payload["requestExitPlanMode"] = bool(on_exit_plan_mode)
14991500
payload["requestAutoModeSwitch"] = bool(on_auto_mode_switch)
1501+
payload["requestMcpApps"] = bool(enable_mcp_apps)
15001502

15011503
# Serialize commands (name + description only) into payload
15021504
if commands:
@@ -1725,6 +1727,7 @@ async def resume_session(
17251727
on_elicitation_request: ElicitationHandler | None = None,
17261728
on_exit_plan_mode: ExitPlanModeHandler | None = None,
17271729
on_auto_mode_switch: AutoModeSwitchHandler | None = None,
1730+
enable_mcp_apps: bool = False,
17281731
create_session_fs_handler: CreateSessionFsHandler | None = None,
17291732
github_token: str | None = None,
17301733
remote_session: RemoteSessionMode | None = None,
@@ -1883,6 +1886,7 @@ async def resume_session(
18831886
payload["requestElicitation"] = bool(on_elicitation_request)
18841887
payload["requestExitPlanMode"] = bool(on_exit_plan_mode)
18851888
payload["requestAutoModeSwitch"] = bool(on_auto_mode_switch)
1889+
payload["requestMcpApps"] = bool(enable_mcp_apps)
18861890

18871891
# Serialize commands (name + description only) into payload
18881892
if commands:

rust/src/types.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,23 @@ pub struct SessionConfig {
10781078
/// Defaults to `Some(true)` via [`SessionConfig::default`].
10791079
#[serde(skip_serializing_if = "Option::is_none")]
10801080
pub request_elicitation: Option<bool>,
1081+
/// Enable MCP Apps (SEP-1865) UI passthrough on this session.
1082+
///
1083+
/// When `Some(true)`, the runtime adds the `mcp-apps` capability to the
1084+
/// session, which causes it to advertise the
1085+
/// `extensions.io.modelcontextprotocol/ui` extension to MCP servers (so
1086+
/// they expose `_meta.ui.resourceUri` on tools) and to expose the
1087+
/// `session.rpc.mcp.apps.{listTools,callTool,readResource,setHostContext,
1088+
/// getHostContext}` JSON-RPC methods.
1089+
///
1090+
/// SDK consumers MUST set this to `Some(true)` only when they have an
1091+
/// iframe renderer that can display `ui://` MCP App bundles. Setting it
1092+
/// without a renderer will cause MCP servers to register UI-enabled tool
1093+
/// variants the consumer cannot display.
1094+
///
1095+
/// Defaults to `None` (disabled).
1096+
#[serde(skip_serializing_if = "Option::is_none")]
1097+
pub request_mcp_apps: Option<bool>,
10811098
/// Skill directory paths passed through to the GitHub Copilot CLI.
10821099
#[serde(skip_serializing_if = "Option::is_none")]
10831100
pub skill_directories: Option<Vec<PathBuf>>,
@@ -1208,6 +1225,7 @@ impl std::fmt::Debug for SessionConfig {
12081225
.field("request_exit_plan_mode", &self.request_exit_plan_mode)
12091226
.field("request_auto_mode_switch", &self.request_auto_mode_switch)
12101227
.field("request_elicitation", &self.request_elicitation)
1228+
.field("request_mcp_apps", &self.request_mcp_apps)
12111229
.field("skill_directories", &self.skill_directories)
12121230
.field("instruction_directories", &self.instruction_directories)
12131231
.field("disabled_skills", &self.disabled_skills)
@@ -1271,6 +1289,7 @@ impl Default for SessionConfig {
12711289
request_exit_plan_mode: Some(true),
12721290
request_auto_mode_switch: Some(true),
12731291
request_elicitation: Some(true),
1292+
request_mcp_apps: None,
12741293
skill_directories: None,
12751294
instruction_directories: None,
12761295
disabled_skills: None,
@@ -1491,6 +1510,13 @@ impl SessionConfig {
14911510
self
14921511
}
14931512

1513+
/// Enable MCP Apps (SEP-1865) UI passthrough on this session. Defaults
1514+
/// to `None` (disabled). See [`SessionConfig::request_mcp_apps`].
1515+
pub fn with_request_mcp_apps(mut self, enable: bool) -> Self {
1516+
self.request_mcp_apps = Some(enable);
1517+
self
1518+
}
1519+
14941520
/// Set skill directory paths passed through to the CLI.
14951521
pub fn with_skill_directories<I, P>(mut self, paths: I) -> Self
14961522
where
@@ -1680,6 +1706,10 @@ pub struct ResumeSessionConfig {
16801706
/// Advertise elicitation provider capability on resume.
16811707
#[serde(skip_serializing_if = "Option::is_none")]
16821708
pub request_elicitation: Option<bool>,
1709+
/// Enable MCP Apps (SEP-1865) UI passthrough on resume. See
1710+
/// [`SessionConfig::request_mcp_apps`].
1711+
#[serde(skip_serializing_if = "Option::is_none")]
1712+
pub request_mcp_apps: Option<bool>,
16831713
/// Skill directory paths passed through to the GitHub Copilot CLI on resume.
16841714
#[serde(skip_serializing_if = "Option::is_none")]
16851715
pub skill_directories: Option<Vec<PathBuf>>,
@@ -1790,6 +1820,7 @@ impl std::fmt::Debug for ResumeSessionConfig {
17901820
.field("request_exit_plan_mode", &self.request_exit_plan_mode)
17911821
.field("request_auto_mode_switch", &self.request_auto_mode_switch)
17921822
.field("request_elicitation", &self.request_elicitation)
1823+
.field("request_mcp_apps", &self.request_mcp_apps)
17931824
.field("skill_directories", &self.skill_directories)
17941825
.field("instruction_directories", &self.instruction_directories)
17951826
.field("disabled_skills", &self.disabled_skills)
@@ -1852,6 +1883,7 @@ impl ResumeSessionConfig {
18521883
request_exit_plan_mode: Some(true),
18531884
request_auto_mode_switch: Some(true),
18541885
request_elicitation: Some(true),
1886+
request_mcp_apps: None,
18551887
skill_directories: None,
18561888
instruction_directories: None,
18571889
disabled_skills: None,
@@ -2043,6 +2075,13 @@ impl ResumeSessionConfig {
20432075
self
20442076
}
20452077

2078+
/// Enable MCP Apps (SEP-1865) UI passthrough on resume. Defaults to
2079+
/// `None` (disabled). See [`SessionConfig::request_mcp_apps`].
2080+
pub fn with_request_mcp_apps(mut self, enable: bool) -> Self {
2081+
self.request_mcp_apps = Some(enable);
2082+
self
2083+
}
2084+
20462085
/// Set skill directory paths passed through to the CLI on resume.
20472086
pub fn with_skill_directories<I, P>(mut self, paths: I) -> Self
20482087
where
@@ -3366,6 +3405,7 @@ mod tests {
33663405
assert_eq!(cfg.request_elicitation, Some(true));
33673406
assert_eq!(cfg.request_exit_plan_mode, Some(true));
33683407
assert_eq!(cfg.request_auto_mode_switch, Some(true));
3408+
assert_eq!(cfg.request_mcp_apps, None);
33693409
}
33703410

33713411
#[test]
@@ -3376,6 +3416,7 @@ mod tests {
33763416
assert_eq!(cfg.request_elicitation, Some(true));
33773417
assert_eq!(cfg.request_exit_plan_mode, Some(true));
33783418
assert_eq!(cfg.request_auto_mode_switch, Some(true));
3419+
assert_eq!(cfg.request_mcp_apps, None);
33793420
}
33803421

33813422
#[test]

0 commit comments

Comments
 (0)