Add preMcpToolCall hook support to all SDKs - #1366
Conversation
There was a problem hiding this comment.
Pull request overview
Adds cross-SDK support for the new preMcpToolCall runtime hook so consumers can inspect/modify/remove MCP request _meta before tool dispatch, and updates hook payload APIs to expose WorkingDirectory/workingDirectory while preserving the on-the-wire "cwd" key.
Changes:
- Introduces
preMcpToolCallhook handlers and input/output types across Rust, .NET, Go, Node.js, and Python (including runtime dispatch/handler registration). - Renames existing hook input
cwdfields toWorkingDirectory/workingDirectory(with wire-format mapping back to"cwd"). - Adds shared MCP “meta-echo” test server + snapshot-driven E2E tests for set/replace/remove-meta scenarios; adds .NET serialization tests for hook output shapes.
Show a summary per file
| File | Description |
|---|---|
| test/snapshots/pre_mcp_tool_call_hook/should_set_meta_via_premcptoolcall_hook.yaml | New replay snapshot for “set meta” scenario. |
| test/snapshots/pre_mcp_tool_call_hook/should_replace_meta_via_premcptoolcall_hook.yaml | New replay snapshot for “replace meta” scenario. |
| test/snapshots/pre_mcp_tool_call_hook/should_remove_meta_via_premcptoolcall_hook.yaml | New replay snapshot for “remove meta” scenario. |
| test/harness/test-mcp-meta-echo-server.mjs | Adds MCP stdio server that echoes received _meta for E2E validation. |
| test/harness/replayingCapiProxy.ts | Adds optional debug logging to help diagnose snapshot prefix mismatches. |
| rust/tests/e2e/support.rs | Removes now-unneeded dead_code suppression for repo_root(). |
| rust/tests/e2e/pre_mcp_tool_call_hook.rs | Adds Rust E2E tests covering set/replace/remove meta via hook. |
| rust/tests/e2e/hooks_extended.rs | Updates tests to assert working_directory instead of cwd. |
| rust/tests/e2e.rs | Registers new Rust E2E module. |
| rust/src/hooks.rs | Adds PreMcpToolCall hook types + dispatch; renames hook cwd fields to working_directory (wire "cwd"). |
| rust/examples/hooks.rs | Updates example to use working_directory. |
| python/e2e/test_pre_mcp_tool_call_hook_e2e.py | Adds Python E2E tests for preMcpToolCall meta manipulation. |
| python/e2e/test_hooks_extended_e2e.py | Updates extended hook test to expect workingDirectory. |
| python/copilot/session.py | Adds new hook TypedDicts/handler type and dispatch wiring; remaps "cwd" to workingDirectory for hook inputs. |
| nodejs/test/e2e/pre_mcp_tool_call_hook.e2e.test.ts | Adds Node E2E tests for preMcpToolCall meta manipulation. |
| nodejs/test/e2e/hooks_extended.e2e.test.ts | Updates extended hook test assertions to use workingDirectory. |
| nodejs/src/types.ts | Adds new PreMcpToolCall* types and hook registration; renames hook base input cwd to workingDirectory. |
| nodejs/src/session.ts | Remaps raw hook input { cwd } to { workingDirectory } during deserialization. |
| go/types.go | Adds Go PreMcpToolCall hook types + meta tri-state helpers; renames several public Cwd-named fields to WorkingDirectory (wire "cwd"). |
| go/session.go | Wires preMcpToolCall into hook invoke dispatcher. |
| go/internal/e2e/session_e2e_test.go | Updates assertions to use Context.WorkingDirectory. |
| go/internal/e2e/pre_mcp_tool_call_hook_e2e_test.go | Adds Go E2E tests for meta set/replace/remove via hook. |
| go/internal/e2e/mcp_and_agents_e2e_test.go | Updates MCP stdio config field to WorkingDirectory. |
| go/internal/e2e/hooks_extended_e2e_test.go | Updates hook extended test to use WorkingDirectory. |
| go/internal/e2e/client_options_e2e_test.go | Updates captured CLI JSON struct to WorkingDirectory field (wire "cwd"). |
| go/client.go | Updates hasHooks detection to include OnPreMcpToolCall. |
| dotnet/test/Unit/SerializationTests.cs | Adds unit tests validating serialization shapes for PreMcpToolCallHookOutput and boxed JsonElement outputs. |
| dotnet/test/E2E/PreMcpToolCallHookE2ETests.cs | Adds .NET E2E tests for meta set/replace/remove via hook. |
| dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs | Updates hook lifecycle assertions to use WorkingDirectory. |
| dotnet/src/Types.cs | Adds PreMcpToolCallHookInput/Output; renames hook input Cwd properties to WorkingDirectory (wire "cwd"). |
| dotnet/src/Session.cs | Adds hook dispatch for preMcpToolCall and pre-serialization helper for output. |
| dotnet/src/Client.cs | Updates hasHooks detection to include OnPreMcpToolCall. |
Copilot's findings
- Files reviewed: 32/32 changed files
- Comments generated: 4
532d038 to
895ff7d
Compare
This comment has been minimized.
This comment has been minimized.
895ff7d to
c3e0240
Compare
This comment has been minimized.
This comment has been minimized.
c3e0240 to
88c2714
Compare
This comment has been minimized.
This comment has been minimized.
88c2714 to
929528b
Compare
This comment has been minimized.
This comment has been minimized.
929528b to
2020647
Compare
This comment has been minimized.
This comment has been minimized.
2020647 to
ff1c65d
Compare
This comment has been minimized.
This comment has been minimized.
|
Commit pushed:
|
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 #1366 · ● 13.3M
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
581001a to
4a2b45e
Compare
Add the preMcpToolCall hook which fires before an MCP tool call is dispatched to an MCP server. This aligns with copilot-agent-runtime 1.0.51 which added support for this hook type. The hook receives serverName, toolName, arguments, optional toolCallId, and optional _meta as input. The output supports a tri-state metaToUse field: absent (preserve existing _meta), null (remove _meta), or object (replace _meta). Changes per SDK: - Node.js: PreMcpToolCallHookInput/Output types, handler, SessionHooks - Python: PreMcpToolCallHookInput/Output TypedDicts, handler, SessionHooks - Go: PreMcpToolCallHookInput/Output structs, handler, helper functions - .NET: PreMcpToolCallHookInput/Output classes, SessionHooks, JsonElement? - Rust: PreMcpToolCallInput/Output structs, HookEvent/Output variants, trait Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Port the three preMcpToolCall hook test scenarios (set meta, replace meta, remove meta) from the .NET reference implementation to all four remaining SDK test suites. Each test: - Configures an MCP stdio server (meta-echo) that echoes _meta back - Registers a preMcpToolCall hook that sets/replaces/removes metadata - Verifies the tool result reflects the hook's effect - Asserts hook input fields (serverName, toolName, workingDirectory, timestamp) Snapshot files are reused from test/snapshots/pre_mcp_tool_call_hook/. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add OnPreMcpToolCall to hasHooks checks in .NET Client.cs and Go client.go - Add SerializeHookOutput helper for source-gen serialization - Add .NET PreMcpToolCallHookE2ETests (3 tests: set, replace, remove meta) - Add MCP meta-echo test server and snapshot YAML files - Fix Go mcp_and_agents_e2e_test.go (Cwd -> WorkingDirectory) - Remove stale dead_code lint expectation in Rust support.rs - Add serialization unit tests for hook output Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Copilot CLI now returns ISO-8601 timestamp strings instead of numeric epoch milliseconds. Update PingResponse.timestamp from long to String and PingResult.timestamp from Long to String. Update corresponding test assertions accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Auto-committed by java-codegen-check workflow.
The generated PingResult record has timestamp as Long (milliseconds), but tests were passing String values (ISO date format). Update tests to use Long millisecond values instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename the public `cwd` field to `working_directory` on: - ClientOptions (local-only, not serialized) - McpStdioServerConfig (serialized; add #[serde(rename = "cwd")]) - SessionListFilter (serialized; add #[serde(rename = "cwd")]) The wire format remains unchanged (JSON key stays "cwd"). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename all public API fields named 'cwd' to 'working_directory' and 'initial_cwd' to 'initial_working_directory' in the Python SDK while preserving the wire format (JSON sent to/from the Copilot CLI runtime still uses 'cwd' and 'initialCwd'). Fields renamed: - SubprocessConfig.cwd -> working_directory - MCPStdioServerConfig['cwd'] -> working_directory - SessionContext.cwd -> working_directory - SessionListFilter.cwd -> working_directory - SessionFsConfig['initial_cwd'] -> initial_working_directory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename the public-facing 'cwd' field to 'workingDirectory' in: - CopilotClientOptions.cwd - MCPStdioServerConfig.cwd - SessionContext.cwd - SessionListFilter.cwd The wire format (JSON sent to/from the Copilot CLI runtime) is preserved as 'cwd' via transformation layers in client.ts: - Outgoing: workingDirectory -> cwd (mcpServers, customAgents, listSessions filter) - Incoming: cwd -> workingDirectory (session context in toSessionMetadata) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Node.js: Increase CLI server start timeout from 10s to 30s to accommodate slow Windows CI runners - Java: Add missing conversation to mcp_and_agents/should_accept_both_mcp_servers_and_custom_agents snapshot (was empty, causing proxy 500 errors) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Go and .NET
Complete the cwd → workingDirectory rename across all SDKs for consistency.
Wire format (JSON) is preserved via struct tags and JsonPropertyName attributes.
Go:
- ClientOptions.Cwd → WorkingDirectory
- SessionFsConfig.InitialCwd → InitialWorkingDirectory
- SessionContext.Cwd → WorkingDirectory (json:"cwd")
- SessionListFilter.Cwd → WorkingDirectory (json:"cwd,omitempty")
.NET:
- SessionFsConfig.InitialCwd → InitialWorkingDirectory ([JsonPropertyName("initialCwd")])
- SessionContext.Cwd → WorkingDirectory ([JsonPropertyName("cwd")])
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove Java preMcpToolCall hook implementation from this PR to be handled separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4a2b45e to
0ef4ce9
Compare
This comment has been minimized.
This comment has been minimized.
Split dependent SQL tool calls into separate CAPI turns in the session_fs_sqlite snapshot. The CREATE TABLE and INSERT were previously returned as separate choices in a single response, causing the CLI on Windows to execute them concurrently. Since INSERT depends on CREATE TABLE completing first, this produced a 'no such table: items' error. The fix restructures the snapshot into 3 CAPI turns: first executing report_intent + CREATE TABLE, then INSERT, then the final response. This ensures CREATE TABLE always completes before INSERT is attempted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cross-SDK Consistency ReviewThis PR does an excellent job adding The repository has six language SDKs, and Java is not covered by this PR:
What Java needsTo bring Java into alignment, the following would be needed:
This is a suggestion to maintain feature parity — the PR is otherwise well-structured and consistent across the five SDKs it covers.
|
* Add preMcpToolCall hook support to all SDKs
Add the preMcpToolCall hook which fires before an MCP tool call is
dispatched to an MCP server. This aligns with copilot-agent-runtime
1.0.51 which added support for this hook type.
The hook receives serverName, toolName, arguments, optional toolCallId,
and optional _meta as input. The output supports a tri-state metaToUse
field: absent (preserve existing _meta), null (remove _meta), or object
(replace _meta).
Changes per SDK:
- Node.js: PreMcpToolCallHookInput/Output types, handler, SessionHooks
- Python: PreMcpToolCallHookInput/Output TypedDicts, handler, SessionHooks
- Go: PreMcpToolCallHookInput/Output structs, handler, helper functions
- .NET: PreMcpToolCallHookInput/Output classes, SessionHooks, JsonElement?
- Rust: PreMcpToolCallInput/Output structs, HookEvent/Output variants, trait
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add preMcpToolCall hook E2E tests for Node.js, Python, Go, and Rust
Port the three preMcpToolCall hook test scenarios (set meta, replace meta,
remove meta) from the .NET reference implementation to all four remaining
SDK test suites.
Each test:
- Configures an MCP stdio server (meta-echo) that echoes _meta back
- Registers a preMcpToolCall hook that sets/replaces/removes metadata
- Verifies the tool result reflects the hook's effect
- Asserts hook input fields (serverName, toolName, workingDirectory, timestamp)
Snapshot files are reused from test/snapshots/pre_mcp_tool_call_hook/.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix code review feedback and add .NET E2E test infrastructure
- Add OnPreMcpToolCall to hasHooks checks in .NET Client.cs and Go client.go
- Add SerializeHookOutput helper for source-gen serialization
- Add .NET PreMcpToolCallHookE2ETests (3 tests: set, replace, remove meta)
- Add MCP meta-echo test server and snapshot YAML files
- Fix Go mcp_and_agents_e2e_test.go (Cwd -> WorkingDirectory)
- Remove stale dead_code lint expectation in Rust support.rs
- Add serialization unit tests for hook output
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Java PingResponse timestamp deserialization for ISO-8601 format
The Copilot CLI now returns ISO-8601 timestamp strings instead of numeric
epoch milliseconds. Update PingResponse.timestamp from long to String and
PingResult.timestamp from Long to String. Update corresponding test
assertions accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Regenerate Java codegen output
Auto-committed by java-codegen-check workflow.
* Fix Java test type mismatch: PingResult.timestamp is Long not String
The generated PingResult record has timestamp as Long (milliseconds),
but tests were passing String values (ISO date format). Update tests
to use Long millisecond values instead.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename cwd to working_directory in Rust public API
Rename the public `cwd` field to `working_directory` on:
- ClientOptions (local-only, not serialized)
- McpStdioServerConfig (serialized; add #[serde(rename = "cwd")])
- SessionListFilter (serialized; add #[serde(rename = "cwd")])
The wire format remains unchanged (JSON key stays "cwd").
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename cwd to working_directory in Python public API
Rename all public API fields named 'cwd' to 'working_directory' and
'initial_cwd' to 'initial_working_directory' in the Python SDK while
preserving the wire format (JSON sent to/from the Copilot CLI runtime
still uses 'cwd' and 'initialCwd').
Fields renamed:
- SubprocessConfig.cwd -> working_directory
- MCPStdioServerConfig['cwd'] -> working_directory
- SessionContext.cwd -> working_directory
- SessionListFilter.cwd -> working_directory
- SessionFsConfig['initial_cwd'] -> initial_working_directory
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename cwd to workingDirectory in Node.js public API
Rename the public-facing 'cwd' field to 'workingDirectory' in:
- CopilotClientOptions.cwd
- MCPStdioServerConfig.cwd
- SessionContext.cwd
- SessionListFilter.cwd
The wire format (JSON sent to/from the Copilot CLI runtime) is preserved
as 'cwd' via transformation layers in client.ts:
- Outgoing: workingDirectory -> cwd (mcpServers, customAgents, listSessions filter)
- Incoming: cwd -> workingDirectory (session context in toSessionMetadata)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix formatting in Node.js and Python after cwd rename
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Rust E2E test: use working_directory in McpStdioServerConfig
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix flaky tests: increase CLI start timeout and add missing snapshot
- Node.js: Increase CLI server start timeout from 10s to 30s to accommodate
slow Windows CI runners
- Java: Add missing conversation to mcp_and_agents/should_accept_both_mcp_servers_and_custom_agents
snapshot (was empty, causing proxy 500 errors)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update Java .lastmerge to include snapshot fix
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename Cwd/InitialCwd to WorkingDirectory/InitialWorkingDirectory in Go and .NET
Complete the cwd → workingDirectory rename across all SDKs for consistency.
Wire format (JSON) is preserved via struct tags and JsonPropertyName attributes.
Go:
- ClientOptions.Cwd → WorkingDirectory
- SessionFsConfig.InitialCwd → InitialWorkingDirectory
- SessionContext.Cwd → WorkingDirectory (json:"cwd")
- SessionListFilter.Cwd → WorkingDirectory (json:"cwd,omitempty")
.NET:
- SessionFsConfig.InitialCwd → InitialWorkingDirectory ([JsonPropertyName("initialCwd")])
- SessionContext.Cwd → WorkingDirectory ([JsonPropertyName("cwd")])
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix missed Cwd reference and run go fmt
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert Java SDK changes
Remove Java preMcpToolCall hook implementation from this PR to be handled separately.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Node.js Windows CI test failure
Split dependent SQL tool calls into separate CAPI turns in the
session_fs_sqlite snapshot. The CREATE TABLE and INSERT were previously
returned as separate choices in a single response, causing the CLI on
Windows to execute them concurrently. Since INSERT depends on CREATE
TABLE completing first, this produced a 'no such table: items' error.
The fix restructures the snapshot into 3 CAPI turns: first executing
report_intent + CREATE TABLE, then INSERT, then the final response.
This ensures CREATE TABLE always completes before INSERT is attempted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add preMcpToolCall hook support to all SDKs
Add the preMcpToolCall hook which fires before an MCP tool call is
dispatched to an MCP server. This aligns with copilot-agent-runtime
1.0.51 which added support for this hook type.
The hook receives serverName, toolName, arguments, optional toolCallId,
and optional _meta as input. The output supports a tri-state metaToUse
field: absent (preserve existing _meta), null (remove _meta), or object
(replace _meta).
Changes per SDK:
- Node.js: PreMcpToolCallHookInput/Output types, handler, SessionHooks
- Python: PreMcpToolCallHookInput/Output TypedDicts, handler, SessionHooks
- Go: PreMcpToolCallHookInput/Output structs, handler, helper functions
- .NET: PreMcpToolCallHookInput/Output classes, SessionHooks, JsonElement?
- Rust: PreMcpToolCallInput/Output structs, HookEvent/Output variants, trait
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add preMcpToolCall hook E2E tests for Node.js, Python, Go, and Rust
Port the three preMcpToolCall hook test scenarios (set meta, replace meta,
remove meta) from the .NET reference implementation to all four remaining
SDK test suites.
Each test:
- Configures an MCP stdio server (meta-echo) that echoes _meta back
- Registers a preMcpToolCall hook that sets/replaces/removes metadata
- Verifies the tool result reflects the hook's effect
- Asserts hook input fields (serverName, toolName, workingDirectory, timestamp)
Snapshot files are reused from test/snapshots/pre_mcp_tool_call_hook/.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix code review feedback and add .NET E2E test infrastructure
- Add OnPreMcpToolCall to hasHooks checks in .NET Client.cs and Go client.go
- Add SerializeHookOutput helper for source-gen serialization
- Add .NET PreMcpToolCallHookE2ETests (3 tests: set, replace, remove meta)
- Add MCP meta-echo test server and snapshot YAML files
- Fix Go mcp_and_agents_e2e_test.go (Cwd -> WorkingDirectory)
- Remove stale dead_code lint expectation in Rust support.rs
- Add serialization unit tests for hook output
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Java PingResponse timestamp deserialization for ISO-8601 format
The Copilot CLI now returns ISO-8601 timestamp strings instead of numeric
epoch milliseconds. Update PingResponse.timestamp from long to String and
PingResult.timestamp from Long to String. Update corresponding test
assertions accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Regenerate Java codegen output
Auto-committed by java-codegen-check workflow.
* Fix Java test type mismatch: PingResult.timestamp is Long not String
The generated PingResult record has timestamp as Long (milliseconds),
but tests were passing String values (ISO date format). Update tests
to use Long millisecond values instead.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename cwd to working_directory in Rust public API
Rename the public `cwd` field to `working_directory` on:
- ClientOptions (local-only, not serialized)
- McpStdioServerConfig (serialized; add #[serde(rename = "cwd")])
- SessionListFilter (serialized; add #[serde(rename = "cwd")])
The wire format remains unchanged (JSON key stays "cwd").
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename cwd to working_directory in Python public API
Rename all public API fields named 'cwd' to 'working_directory' and
'initial_cwd' to 'initial_working_directory' in the Python SDK while
preserving the wire format (JSON sent to/from the Copilot CLI runtime
still uses 'cwd' and 'initialCwd').
Fields renamed:
- SubprocessConfig.cwd -> working_directory
- MCPStdioServerConfig['cwd'] -> working_directory
- SessionContext.cwd -> working_directory
- SessionListFilter.cwd -> working_directory
- SessionFsConfig['initial_cwd'] -> initial_working_directory
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename cwd to workingDirectory in Node.js public API
Rename the public-facing 'cwd' field to 'workingDirectory' in:
- CopilotClientOptions.cwd
- MCPStdioServerConfig.cwd
- SessionContext.cwd
- SessionListFilter.cwd
The wire format (JSON sent to/from the Copilot CLI runtime) is preserved
as 'cwd' via transformation layers in client.ts:
- Outgoing: workingDirectory -> cwd (mcpServers, customAgents, listSessions filter)
- Incoming: cwd -> workingDirectory (session context in toSessionMetadata)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix formatting in Node.js and Python after cwd rename
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Rust E2E test: use working_directory in McpStdioServerConfig
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix flaky tests: increase CLI start timeout and add missing snapshot
- Node.js: Increase CLI server start timeout from 10s to 30s to accommodate
slow Windows CI runners
- Java: Add missing conversation to mcp_and_agents/should_accept_both_mcp_servers_and_custom_agents
snapshot (was empty, causing proxy 500 errors)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update Java .lastmerge to include snapshot fix
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Rename Cwd/InitialCwd to WorkingDirectory/InitialWorkingDirectory in Go and .NET
Complete the cwd → workingDirectory rename across all SDKs for consistency.
Wire format (JSON) is preserved via struct tags and JsonPropertyName attributes.
Go:
- ClientOptions.Cwd → WorkingDirectory
- SessionFsConfig.InitialCwd → InitialWorkingDirectory
- SessionContext.Cwd → WorkingDirectory (json:"cwd")
- SessionListFilter.Cwd → WorkingDirectory (json:"cwd,omitempty")
.NET:
- SessionFsConfig.InitialCwd → InitialWorkingDirectory ([JsonPropertyName("initialCwd")])
- SessionContext.Cwd → WorkingDirectory ([JsonPropertyName("cwd")])
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix missed Cwd reference and run go fmt
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert Java SDK changes
Remove Java preMcpToolCall hook implementation from this PR to be handled separately.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Node.js Windows CI test failure
Split dependent SQL tool calls into separate CAPI turns in the
session_fs_sqlite snapshot. The CREATE TABLE and INSERT were previously
returned as separate choices in a single response, causing the CLI on
Windows to execute them concurrently. Since INSERT depends on CREATE
TABLE completing first, this produced a 'no such table: items' error.
The fix restructures the snapshot into 3 CAPI turns: first executing
report_intent + CREATE TABLE, then INSERT, then the final response.
This ensures CREATE TABLE always completes before INSERT is attempted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Motivation
The copilot-agent-runtime 1.0.51 added a new
preMcpToolCallhook that lets SDK consumers intercept MCP tool invocations before they execute -- specifically to inspect, set, replace, or remove the_metafield sent to the MCP server. This PR adds support for that hook across all five language SDKs.Approach
Each SDK receives a new hook callback (
OnPreMcpToolCall/on_pre_mcp_tool_call) that is invoked with contextual information about the pending MCP tool call (server name, tool name, arguments, working directory, timestamp, existing meta). The hook returns a tri-state output:_metaunchangedmetaToUseset to an object -- replace_metawith that objectmetaToUseset to null/None -- remove_metaentirelyKey design decisions
"cwd"but the public SDK API exposes it asWorkingDirectory/working_directoryfor consistency with the rest of the SDK surface. All existing hook input types were renamed fromCwdtoWorkingDirectoryas well (with serialization attributes preserving the wire name).SerializeHookOutputhelper to pre-serialize the typed output toJsonElementbefore boxing into the genericHooksInvokeResponse(object? Output)-- required because reflection-based serialization is disabled in the project.hasHookschecks in .NET and Go were updated to includeOnPreMcpToolCallso the runtime knows to invoke hooks.Testing
test/harness/test-mcp-meta-echo-server.mjs) that echoes back the_metait received in the tool resulttest/snapshots/pre_mcp_tool_call_hook/Files of note
test/harness/test-mcp-meta-echo-server.mjs-- shared MCP server for E2E testsdotnet/src/Types.cs--PreMcpToolCallHookInputandPreMcpToolCallHookOutputtypesrust/src/hooks.rs-- Rust hook trait extension and dispatch logictest/harness/replayingCapiProxy.ts-- minor fix to forward hook RPC calls correctly