Skip to content

Commit c3e1524

Browse files
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>
1 parent 93fa5fe commit c3e1524

8 files changed

Lines changed: 183 additions & 0 deletions

File tree

java/mvnw

100644100755
File mode changed.

java/src/main/java/com/github/copilot/sdk/CopilotClient.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.github.copilot.sdk.json.PingResponse;
3535
import com.github.copilot.sdk.json.ResumeSessionConfig;
3636
import com.github.copilot.sdk.json.ResumeSessionResponse;
37+
import com.github.copilot.sdk.json.SessionCapabilities;
3738
import com.github.copilot.sdk.json.SessionConfig;
3839
import com.github.copilot.sdk.json.SessionLifecycleHandler;
3940
import com.github.copilot.sdk.json.SessionListFilter;
@@ -290,6 +291,31 @@ private static boolean isUnsupportedConnectMethod(JsonRpcException ex) {
290291
return ex.getCode() == METHOD_NOT_FOUND_ERROR_CODE || "Unhandled method connect".equals(ex.getMessage());
291292
}
292293

294+
/**
295+
* Logs a warning when the consumer set {@code enableMcpApps=true} on
296+
* create/resume but the runtime did not advertise
297+
* {@code capabilities.ui.mcpApps} in the response. The runtime silently
298+
* drops the opt-in when its {@code MCP_APPS} feature flag (or
299+
* {@code COPILOT_MCP_APPS=true} env override) is unset, so without this
300+
* warning a consumer trying to use MCP Apps would see no error -- just
301+
* tools that never expose {@code _meta.ui.resourceUri}.
302+
*/
303+
private static void warnIfMcpAppsDropped(boolean requested, SessionCapabilities capabilities) {
304+
if (!requested) {
305+
return;
306+
}
307+
boolean advertised = capabilities != null
308+
&& capabilities.getUi() != null
309+
&& capabilities.getUi().getMcpApps().orElse(false);
310+
if (advertised) {
311+
return;
312+
}
313+
LOG.warning(
314+
"enableMcpApps was requested but the runtime did not advertise capabilities.ui.mcpApps. "
315+
+ "The runtime's MCP_APPS feature flag or COPILOT_MCP_APPS=true environment override is "
316+
+ "likely unset; the MCP Apps surface is unavailable for this session.");
317+
}
318+
293319
/**
294320
* Disconnects from the Copilot server and closes all active sessions.
295321
* <p>
@@ -467,6 +493,7 @@ public CompletableFuture<CopilotSession> createSession(SessionConfig config) {
467493
rpcNanos);
468494
session.setWorkspacePath(response.workspacePath());
469495
session.setCapabilities(response.capabilities());
496+
warnIfMcpAppsDropped(config.isEnableMcpApps(), response.capabilities());
470497
// If the server returned a different sessionId (e.g. a v2 CLI that ignores
471498
// the client-supplied ID), re-key the sessions map.
472499
String returnedId = response.sessionId();
@@ -552,6 +579,7 @@ public CompletableFuture<CopilotSession> resumeSession(String sessionId, ResumeS
552579
rpcNanos);
553580
session.setWorkspacePath(response.workspacePath());
554581
session.setCapabilities(response.capabilities());
582+
warnIfMcpAppsDropped(config.isEnableMcpApps(), response.capabilities());
555583
// If the server returned a different sessionId than what was requested, re-key.
556584
String returnedId = response.sessionId();
557585
if (returnedId != null && !returnedId.equals(sessionId)) {

java/src/main/java/com/github/copilot/sdk/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/sdk/json/CreateSessionRequest.java

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

115+
@JsonProperty("requestMcpApps")
116+
private Boolean requestMcpApps;
117+
115118
@JsonProperty("requestExitPlanMode")
116119
private Boolean requestExitPlanMode;
117120

@@ -488,6 +491,21 @@ public void clearRequestElicitation() {
488491
this.requestElicitation = null;
489492
}
490493

494+
/** Gets the requestMcpApps flag. @return the flag */
495+
public Boolean getRequestMcpApps() {
496+
return requestMcpApps;
497+
}
498+
499+
/** Sets the requestMcpApps flag. @param requestMcpApps the flag */
500+
public void setRequestMcpApps(boolean requestMcpApps) {
501+
this.requestMcpApps = requestMcpApps;
502+
}
503+
504+
/** Clears the requestMcpApps setting, reverting to the default behavior. */
505+
public void clearRequestMcpApps() {
506+
this.requestMcpApps = null;
507+
}
508+
491509
/** Gets the requestExitPlanMode flag. @return the flag */
492510
public Boolean getRequestExitPlanMode() {
493511
return requestExitPlanMode;

java/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public class ResumeSessionConfig {
7070
private ElicitationHandler onElicitationRequest;
7171
private ExitPlanModeHandler onExitPlanMode;
7272
private AutoModeSwitchHandler onAutoModeSwitch;
73+
private boolean enableMcpApps;
7374
private String gitHubToken;
7475
private String remoteSession;
7576

@@ -806,6 +807,31 @@ public ResumeSessionConfig setOnElicitationRequest(ElicitationHandler onElicitat
806807
return this;
807808
}
808809

810+
/**
811+
* Returns whether MCP Apps (SEP-1865) UI passthrough is enabled on resume.
812+
*
813+
* @return {@code true} if the consumer has opted into MCP Apps, otherwise
814+
* {@code false}
815+
* @see #setEnableMcpApps(boolean)
816+
*/
817+
public boolean isEnableMcpApps() {
818+
return enableMcpApps;
819+
}
820+
821+
/**
822+
* Enables MCP Apps (SEP-1865) UI passthrough on the resumed session. See
823+
* {@link SessionConfig#setEnableMcpApps(boolean)} for full semantics
824+
* (runtime gate, capability inspection, renderer requirement).
825+
*
826+
* @param enableMcpApps
827+
* {@code true} to opt into MCP Apps support on resume
828+
* @return this config for method chaining
829+
*/
830+
public ResumeSessionConfig setEnableMcpApps(boolean enableMcpApps) {
831+
this.enableMcpApps = enableMcpApps;
832+
return this;
833+
}
834+
809835
/**
810836
* Gets the exit-plan-mode request handler.
811837
*
@@ -963,6 +989,7 @@ public ResumeSessionConfig clone() {
963989
copy.onElicitationRequest = this.onElicitationRequest;
964990
copy.onExitPlanMode = this.onExitPlanMode;
965991
copy.onAutoModeSwitch = this.onAutoModeSwitch;
992+
copy.enableMcpApps = this.enableMcpApps;
966993
copy.gitHubToken = this.gitHubToken;
967994
copy.remoteSession = this.remoteSession;
968995
return copy;

java/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java

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

119+
@JsonProperty("requestMcpApps")
120+
private Boolean requestMcpApps;
121+
119122
@JsonProperty("requestExitPlanMode")
120123
private Boolean requestExitPlanMode;
121124

@@ -512,6 +515,21 @@ public void clearRequestElicitation() {
512515
this.requestElicitation = null;
513516
}
514517

518+
/** Gets the requestMcpApps flag. @return the flag */
519+
public Boolean getRequestMcpApps() {
520+
return requestMcpApps;
521+
}
522+
523+
/** Sets the requestMcpApps flag. @param requestMcpApps the flag */
524+
public void setRequestMcpApps(boolean requestMcpApps) {
525+
this.requestMcpApps = requestMcpApps;
526+
}
527+
528+
/** Clears the requestMcpApps setting, reverting to the default behavior. */
529+
public void clearRequestMcpApps() {
530+
this.requestMcpApps = null;
531+
}
532+
515533
/** Gets the requestExitPlanMode flag. @return the flag */
516534
public Boolean getRequestExitPlanMode() {
517535
return requestExitPlanMode;

java/src/main/java/com/github/copilot/sdk/json/SessionConfig.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public class SessionConfig {
7070
private ElicitationHandler onElicitationRequest;
7171
private ExitPlanModeHandler onExitPlanMode;
7272
private AutoModeSwitchHandler onAutoModeSwitch;
73+
private boolean enableMcpApps;
7374
private String gitHubToken;
7475
private String remoteSession;
7576
private CloudSessionOptions cloud;
@@ -860,6 +861,50 @@ public SessionConfig setOnElicitationRequest(ElicitationHandler onElicitationReq
860861
return this;
861862
}
862863

864+
/**
865+
* Returns whether MCP Apps (SEP-1865) UI passthrough is enabled on this
866+
* session.
867+
*
868+
* @return {@code true} if the consumer has opted into MCP Apps, otherwise
869+
* {@code false}
870+
* @see #setEnableMcpApps(boolean)
871+
*/
872+
public boolean isEnableMcpApps() {
873+
return enableMcpApps;
874+
}
875+
876+
/**
877+
* Enables MCP Apps (SEP-1865) UI passthrough on this session.
878+
* <p>
879+
* When {@code true} <b>and</b> the runtime has MCP Apps enabled (via the
880+
* {@code MCP_APPS} feature flag or {@code COPILOT_MCP_APPS=true} environment
881+
* override), the runtime adds the {@code mcp-apps} capability to the session,
882+
* which causes it to advertise the
883+
* {@code extensions.io.modelcontextprotocol/ui} extension to MCP servers (so
884+
* they expose {@code _meta.ui.resourceUri} on tools) and to expose the
885+
* {@code session.rpc.mcp.apps.{listTools,callTool,readResource,
886+
* setHostContext,getHostContext,diagnose}} JSON-RPC methods.
887+
* <p>
888+
* If the runtime gate is off, the opt-in is silently dropped server-side (the
889+
* runtime logs a warning); the session is created normally but the MCP Apps
890+
* surface is unavailable. Inspect {@link SessionUiCapabilities#getMcpApps()} on
891+
* {@link com.github.copilot.sdk.CopilotSession#getCapabilities()} to detect
892+
* this.
893+
* <p>
894+
* SDK consumers MUST set this to {@code true} only when they have an iframe
895+
* renderer that can display {@code ui://} MCP App bundles. Setting it without
896+
* a renderer will cause MCP servers to register UI-enabled tool variants the
897+
* consumer cannot display.
898+
*
899+
* @param enableMcpApps
900+
* {@code true} to opt into MCP Apps support
901+
* @return this config instance for method chaining
902+
*/
903+
public SessionConfig setEnableMcpApps(boolean enableMcpApps) {
904+
this.enableMcpApps = enableMcpApps;
905+
return this;
906+
}
907+
863908
/**
864909
* Gets the exit-plan-mode request handler.
865910
*
@@ -1057,6 +1102,7 @@ public SessionConfig clone() {
10571102
copy.onElicitationRequest = this.onElicitationRequest;
10581103
copy.onExitPlanMode = this.onExitPlanMode;
10591104
copy.onAutoModeSwitch = this.onAutoModeSwitch;
1105+
copy.enableMcpApps = this.enableMcpApps;
10601106
copy.gitHubToken = this.gitHubToken;
10611107
copy.remoteSession = this.remoteSession;
10621108
copy.cloud = this.cloud;

java/src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public class SessionUiCapabilities {
2121
@JsonProperty("elicitation")
2222
private Boolean elicitation;
2323

24+
@JsonProperty("mcpApps")
25+
private Boolean mcpApps;
26+
2427
/**
2528
* Returns whether the host supports interactive elicitation dialogs.
2629
*
@@ -53,4 +56,41 @@ public SessionUiCapabilities clearElicitation() {
5356
return this;
5457
}
5558

59+
/**
60+
* Returns whether the runtime has accepted the session's MCP Apps (SEP-1865)
61+
* opt-in. Present and {@code true} when the consumer set
62+
* {@code enableMcpApps=true} on create/resume <b>and</b> the runtime's
63+
* {@code MCP_APPS} feature flag (or {@code COPILOT_MCP_APPS=true} env
64+
* override) is on. Otherwise empty or {@code false}, indicating the runtime
65+
* silently dropped the opt-in.
66+
*
67+
* @return an {@link Optional} containing the boolean value, or empty if not set
68+
*/
69+
@JsonIgnore
70+
public Optional<Boolean> getMcpApps() {
71+
return Optional.ofNullable(mcpApps);
72+
}
73+
74+
/**
75+
* Sets whether the runtime has accepted the MCP Apps opt-in.
76+
*
77+
* @param mcpApps
78+
* {@code true} if MCP Apps is enabled for this session
79+
* @return this instance for method chaining
80+
*/
81+
public SessionUiCapabilities setMcpApps(boolean mcpApps) {
82+
this.mcpApps = mcpApps;
83+
return this;
84+
}
85+
86+
/**
87+
* Clears the mcpApps setting.
88+
*
89+
* @return this instance for method chaining
90+
*/
91+
public SessionUiCapabilities clearMcpApps() {
92+
this.mcpApps = null;
93+
return this;
94+
}
95+
5696
}

0 commit comments

Comments
 (0)