Skip to content

sdk: Expose githubMcpToolConfig across languages - #2112

Open
connor4312 wants to merge 4 commits into
github:mainfrom
connor4312:copilot/github-mcp-tool-config
Open

sdk: Expose githubMcpToolConfig across languages#2112
connor4312 wants to merge 4 commits into
github:mainfrom
connor4312:copilot/github-mcp-tool-config

Conversation

@connor4312

Copy link
Copy Markdown

Why

The runtime accepts a githubMcpToolConfig option on session.create / session.resume to configure the built-in GitHub MCP server, but no SDK exposed it, so SDK consumers could not reach it.

The motivating case is the new disableFormDeferral control. MCP Apps are useful, but having issue and pull request tools open an interactive form instead of completing the requested operation is disruptive for autonomous SDK workflows, where the caller expects the tool to just run. disableFormDeferral lets those consumers keep MCP Apps result views while form-backed write tools execute directly.

Companion changes:

What

Adds githubMcpToolConfig to session create and resume across all six SDKs, carrying the settings the runtime already accepts:

Field Type
enableAllTools bool
additionalToolsets string[]
additionalTools string[]
enableInsidersMode bool
disableFormDeferral bool

Per language:

  • NodeGitHubMcpToolConfig interface on SessionConfigBase, exported from the package root
  • PythonGitHubMcpToolConfig TypedDict plus a github_mcp_tool_config keyword argument, converted by a _github_mcp_tool_config_to_wire helper matching the existing _memory_to_wire / _tool_search_to_wire convention
  • GoGitHubMCPToolConfig struct on SessionConfig / ResumeSessionConfig
  • RustGitHubMcpToolConfig with builder methods, wired through SessionCreateWire / SessionResumeWire
  • JavaGitHubMcpToolConfig bean threaded through SessionRequestBuilder
  • .NETGitHubMcpToolConfig class on SessionConfigBase, including the copy constructor

disableFormDeferral requires MCP Apps to be enabled for the session; on its own it is inert. The whole option is omitted from the wire payload when unset, so existing consumers see no behavior change.

Testing

Every language gets create/resume/omitted-when-unset coverage.

Run locally:

  • Go — go build, go vet, and TestSessionRequests_GitHubMCPToolConfig pass
  • Rust — 192 lib tests pass; cargo clippy --lib clean
  • Node — typecheck, format:check, lint (0 errors), and both new tests pass

Not run locally (no toolchain on this machine; relying on CI):

  • Python — the _github_mcp_tool_config_to_wire helper was verified in isolation for the full, empty, and explicit-false cases, but pytest needs Python 3.11+ / uv
  • Java and .NET — no JDK or .NET SDK available

Adds the `githubMcpToolConfig` session option to `session.create` and
`session.resume` in all six SDKs, forwarding the built-in GitHub MCP
server settings the runtime already accepts: `enableAllTools`,
`additionalToolsets`, `additionalTools`, `enableInsidersMode`, and the
new `disableFormDeferral`.

`disableFormDeferral` lets autonomous SDK workflows opt out of MCP App
form deferral, so form-backed GitHub write tools execute directly
instead of returning an awaiting-form stub. It requires MCP Apps to be
enabled for the session.

The option is omitted from the wire payload entirely when unset, so
existing consumers see no change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 28, 2026 18:13
@connor4312
connor4312 requested a review from a team as a code owner July 28, 2026 18:13
connor4312 and others added 2 commits July 28, 2026 11:18
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Exposes the runtime’s githubMcpToolConfig option on session create/resume across multiple SDKs so callers can configure the built-in GitHub MCP server (notably disableFormDeferral) without dropping to raw payloads.

Changes:

  • Added a GitHub MCP tool config type in each SDK and threaded it through session.create / session.resume.
  • Ensured the option is omitted from wire payloads when unset (with tests in each language).
  • Added language-specific helpers/builders/serialization to map SDK shapes to the runtime wire format.
Show a summary per file
File Description
rust/src/wire.rs Adds github_mcp_tool_config to create/resume wire structs with omit-when-None behavior.
rust/src/types.rs Introduces GitHubMcpToolConfig + builders; wires it through session configs and adds serde tests.
python/test_client.py Adds pytest coverage verifying create/resume forwarding of githubMcpToolConfig.
python/copilot/session.py Adds GitHubMcpToolConfig TypedDict for the public Python API.
python/copilot/client.py Adds github_mcp_tool_config kwargs + wire conversion helper and forwards in payload.
python/copilot/init.py Re-exports GitHubMcpToolConfig from the package root.
nodejs/test/client.test.ts Adds tests for forwarding and omitting githubMcpToolConfig.
nodejs/src/types.ts Adds GitHubMcpToolConfig interface and exposes it on SessionConfigBase.
nodejs/src/index.ts Exports GitHubMcpToolConfig from package root.
nodejs/src/client.ts Forwards githubMcpToolConfig in create/resume requests.
java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java Adds create/resume + omission serialization coverage for githubMcpToolConfig.
java/src/main/java/com/github/copilot/rpc/SessionConfig.java Adds GitHubMcpToolConfig to session config + accessors + clone propagation.
java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java Adds githubMcpToolConfig JSON field to resume request.
java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java Adds GitHubMcpToolConfig to resume config + accessors + clone propagation.
java/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java New bean defining the GitHub MCP tool config wire shape.
java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java Adds githubMcpToolConfig JSON field to create request.
java/src/main/java/com/github/copilot/SessionRequestBuilder.java Threads config into create/resume request builders.
go/types.go Adds GitHubMCPToolConfig and threads it into session config + request wire structs.
go/client_test.go Adds JSON serialization tests for create/resume/omitted behavior.
go/client.go Passes config through to create/resume request payloads.
dotnet/test/Unit/SerializationTests.cs Adds serialization coverage for create/resume + omit-when-unset.
dotnet/src/Types.cs Adds GitHubMcpToolConfig and deep-copies it in SessionConfigBase copy ctor.
dotnet/src/Client.cs Threads config into internal create/resume request records.

Review details

  • Files reviewed: 23/23 changed files
  • Comments generated: 6
  • Review effort level: Low

Comment thread python/copilot/client.py
Comment thread python/copilot/client.py
Comment thread nodejs/src/client.ts Outdated
Comment thread nodejs/src/client.ts Outdated
Comment thread go/client_test.go
Comment thread dotnet/src/Types.cs
Copilot AI review requested due to automatic review settings July 28, 2026 18:31
- node: omit githubMcpToolConfig for null as well as undefined
- python: document github_mcp_tool_config on create_session/resume_session
- go: assert json.Unmarshal succeeds in the omitted-when-unset subtest
- java: call the non-deprecated buildCreateRequest(config, sessionId)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (4)

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

  • Both builders unconditionally call setGitHubMcpToolConfig(...), including when the config value is null. This relies on global Jackson settings (or class-level @JsonInclude) to keep the field omitted; if serialization settings change, this could start emitting \"githubMcpToolConfig\": null and violate the API contract of omitting when unset. Prefer only calling the setter when non-null (or calling an explicit clear.../leaving it untouched) to make omission behavior robust.
        request.setGitHubMcpToolConfig(config.getGitHubMcpToolConfig());

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

  • Both builders unconditionally call setGitHubMcpToolConfig(...), including when the config value is null. This relies on global Jackson settings (or class-level @JsonInclude) to keep the field omitted; if serialization settings change, this could start emitting \"githubMcpToolConfig\": null and violate the API contract of omitting when unset. Prefer only calling the setter when non-null (or calling an explicit clear.../leaving it untouched) to make omission behavior robust.
        request.setGitHubMcpToolConfig(config.getGitHubMcpToolConfig());

python/copilot/client.py:351

  • The helper includes a field whenever the key exists, even if the value is None, which would serialize as null on the wire. Since the intent is to omit unset fields while still preserving explicit False, consider checking value is not None before setting each output key (this still forwards False correctly). This avoids sending null values that may be rejected or interpreted differently by the runtime.
def _github_mcp_tool_config_to_wire(config: Mapping[str, Any]) -> dict[str, Any]:
    """Convert a ``GitHubMcpToolConfig`` mapping to wire format."""
    wire: dict[str, Any] = {}
    if "enable_all_tools" in config:
        wire["enableAllTools"] = config["enable_all_tools"]
    if "additional_toolsets" in config:
        wire["additionalToolsets"] = config["additional_toolsets"]
    if "additional_tools" in config:
        wire["additionalTools"] = config["additional_tools"]
    if "enable_insiders_mode" in config:
        wire["enableInsidersMode"] = config["enable_insiders_mode"]
    if "disable_form_deferral" in config:
        wire["disableFormDeferral"] = config["disable_form_deferral"]
    return wire

python/test_client.py:533

  • Python adds forwarding coverage for github_mcp_tool_config, but there’s no test asserting the key is omitted when unset/None (which is part of the PR’s stated contract and is covered in the other SDKs). Add a pytest case that calls create_session() / resume_session() without github_mcp_tool_config and asserts \"githubMcpToolConfig\" is absent from the captured params.
    @pytest.mark.asyncio
    async def test_create_and_resume_session_forward_github_mcp_tool_config(self):
        client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH))
        await client.start()
        try:
            captured = {}

            async def mock_request(method, params, **kwargs):
                captured[method] = params
                if method in ("session.create", "session.resume"):
                    result = {"sessionId": params.get("sessionId") or "session-1"}
                    callback = kwargs.get("on_response_inline")
                    if callback is not None:
                        callback(result)
                    return result
                return {}

            client._client.request = mock_request
            config = {
                "enable_all_tools": True,
                "additional_toolsets": ["repos"],
                "additional_tools": ["get_issue"],
                "enable_insiders_mode": True,
                "disable_form_deferral": True,
            }
            session = await client.create_session(github_mcp_tool_config=config)
            await client.resume_session(session.session_id, github_mcp_tool_config=config)
  • Files reviewed: 23/23 changed files
  • Comments generated: 0 new
  • Review effort level: Low

Copilot AI review requested due to automatic review settings July 28, 2026 18:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (4)

python/test_client.py:545

  • The new coverage validates forwarding on create/resume, but it doesn’t cover the “omitted-when-unset” behavior for Python (which the PR description claims for every language). Add a test that calls create_session() / resume_session() without github_mcp_tool_config and asserts githubMcpToolConfig is absent from the outgoing params.
    @pytest.mark.asyncio
    async def test_create_and_resume_session_forward_github_mcp_tool_config(self):
        client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH))
        await client.start()
        try:
            captured = {}

            async def mock_request(method, params, **kwargs):
                captured[method] = params
                if method in ("session.create", "session.resume"):
                    result = {"sessionId": params.get("sessionId") or "session-1"}
                    callback = kwargs.get("on_response_inline")
                    if callback is not None:
                        callback(result)
                    return result
                return {}

            client._client.request = mock_request
            config = {
                "enable_all_tools": True,
                "additional_toolsets": ["repos"],
                "additional_tools": ["get_issue"],
                "enable_insiders_mode": True,
                "disable_form_deferral": True,
            }
            session = await client.create_session(github_mcp_tool_config=config)
            await client.resume_session(session.session_id, github_mcp_tool_config=config)

            expected = {
                "enableAllTools": True,
                "additionalToolsets": ["repos"],
                "additionalTools": ["get_issue"],
                "enableInsidersMode": True,
                "disableFormDeferral": True,
            }
            assert captured["session.create"]["githubMcpToolConfig"] == expected
            assert captured["session.resume"]["githubMcpToolConfig"] == expected
        finally:
            await client.force_stop()

rust/src/types.rs:5941

  • The omission assertion only covers the create wire path. To match the “omitted-when-unset” behavior on both create and resume, add the equivalent assertion for ResumeSessionConfig when github_mcp_tool_config is not set (i.e., ensure githubMcpToolConfig is absent in the resume wire JSON too).
        let (unset_wire, _) = SessionConfig::default()
            .into_wire(Some(SessionId::from("github-mcp-unset")))
            .expect("default config has no duplicate handlers");
        assert!(
            serde_json::to_value(&unset_wire)
                .unwrap()
                .get("githubMcpToolConfig")
                .is_none()
        );

go/client_test.go:2425

  • The “unset is omitted” test only checks createSessionRequest. Add the same omission check for resumeSessionRequest (with just SessionID set) to ensure githubMcpToolConfig also omits correctly on the resume payload.
	t.Run("unset is omitted", func(t *testing.T) {
		data, err := json.Marshal(createSessionRequest{})
		if err != nil {
			t.Fatalf("Failed to marshal: %v", err)
		}
		var payload map[string]any
		if err := json.Unmarshal(data, &payload); err != nil {
			t.Fatalf("Failed to unmarshal: %v", err)
		}
		if _, ok := payload["githubMcpToolConfig"]; ok {
			t.Fatal("Expected githubMcpToolConfig to be omitted")
		}
	})

java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java:980

  • This test checks omission when unset for create only. Add an assertion that buildResumeRequest("session-2", new ResumeSessionConfig()) also serializes without "githubMcpToolConfig" to fully cover the resume omission path.
    @Test
    void githubMcpToolConfigIsMappedAndSerializedForCreateAndResume() throws Exception {
        var config = new GitHubMcpToolConfig().setEnableAllTools(true).setAdditionalToolsets(List.of("repos"))
                .setAdditionalTools(List.of("get_issue")).setEnableInsidersMode(true).setDisableFormDeferral(true);
        var createRequest = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setGitHubMcpToolConfig(config),
                "session-1");
        var resumeRequest = SessionRequestBuilder.buildResumeRequest("session-1",
                new ResumeSessionConfig().setGitHubMcpToolConfig(config));

        assertSame(config, createRequest.getGitHubMcpToolConfig());
        assertSame(config, resumeRequest.getGitHubMcpToolConfig());
        var mapper = JsonRpcClient.getObjectMapper();
        assertTrue(mapper.writeValueAsString(createRequest).contains("\"githubMcpToolConfig\""));
        assertTrue(mapper.writeValueAsString(resumeRequest).contains("\"githubMcpToolConfig\""));
        assertFalse(
                mapper.writeValueAsString(SessionRequestBuilder.buildCreateRequest(new SessionConfig(), "session-2"))
                        .contains("\"githubMcpToolConfig\""));
    }
  • Files reviewed: 23/23 changed files
  • Comments generated: 0 new
  • Review effort level: Low

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants