Skip to content

Commit ee98706

Browse files
MCP Tools: change []string to *[]string to preserve nil vs empty
With `json:"tools,omitempty"` on a bare []string, Go collapses both nil and []string{} to "omitted", losing the documented distinction between "all tools" (nil) and "no tools" (empty slice). Switch the field type to *[]string so a non-nil pointer to an empty slice serializes as `tools: []` on the wire, matching TS `tools?: string[]` and C# `IList<string>?` with WhenWritingNull. Callers use the standard Go idiom for pointer-to-slice literals: Tools: &[]string{"*"} // explicit all tools Tools: &[]string{} // no tools Tools: &[]string{"a","b"} // only those tools Tools: nil // (default) all tools, field omitted Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 341a8ea commit ee98706

5 files changed

Lines changed: 20 additions & 16 deletions

File tree

docs/features/mcp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func main() {
120120
"my-local-server": copilot.MCPStdioServerConfig{
121121
Command: "node",
122122
Args: []string{"./mcp-server.js"},
123-
Tools: []string{"*"},
123+
Tools: &[]string{"*"},
124124
},
125125
},
126126
})

go/internal/e2e/mcp_and_agents_e2e_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestMCPServersE2E(t *testing.T) {
2121
"test-server": copilot.MCPStdioServerConfig{
2222
Command: "echo",
2323
Args: []string{"hello"},
24-
Tools: []string{"*"},
24+
Tools: &[]string{"*"},
2525
},
2626
}
2727

@@ -63,7 +63,7 @@ func TestMCPServersE2E(t *testing.T) {
6363
mcpServers := map[string]copilot.MCPServerConfig{
6464
"test-server": copilot.MCPStdioServerConfig{
6565
Command: "echo",
66-
Tools: []string{"*"},
66+
Tools: &[]string{"*"},
6767
},
6868
}
6969

@@ -118,7 +118,7 @@ func TestMCPServersE2E(t *testing.T) {
118118
"test-server": copilot.MCPStdioServerConfig{
119119
Command: "echo",
120120
Args: []string{"hello"},
121-
Tools: []string{"*"},
121+
Tools: &[]string{"*"},
122122
},
123123
}
124124

@@ -159,7 +159,7 @@ func TestMCPServersE2E(t *testing.T) {
159159
"env-echo": copilot.MCPStdioServerConfig{
160160
Command: "node",
161161
Args: []string{mcpServerPath},
162-
Tools: []string{"*"},
162+
Tools: &[]string{"*"},
163163
Env: map[string]string{"TEST_SECRET": "hunter2"},
164164
Cwd: mcpServerDir,
165165
},
@@ -198,12 +198,12 @@ func TestMCPServersE2E(t *testing.T) {
198198
"server1": copilot.MCPStdioServerConfig{
199199
Command: "echo",
200200
Args: []string{"server1"},
201-
Tools: []string{"*"},
201+
Tools: &[]string{"*"},
202202
},
203203
"server2": copilot.MCPStdioServerConfig{
204204
Command: "echo",
205205
Args: []string{"server2"},
206-
Tools: []string{"*"},
206+
Tools: &[]string{"*"},
207207
},
208208
}
209209

@@ -366,7 +366,7 @@ func TestCustomAgentsE2E(t *testing.T) {
366366
"agent-server": copilot.MCPStdioServerConfig{
367367
Command: "echo",
368368
Args: []string{"agent-mcp"},
369-
Tools: []string{"*"},
369+
Tools: &[]string{"*"},
370370
},
371371
},
372372
},
@@ -437,7 +437,7 @@ func TestCombinedConfigurationE2E(t *testing.T) {
437437
"shared-server": copilot.MCPStdioServerConfig{
438438
Command: "echo",
439439
Args: []string{"shared"},
440-
Tools: []string{"*"},
440+
Tools: &[]string{"*"},
441441
},
442442
}
443443

go/internal/e2e/rpc_mcp_and_skills_e2e_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func TestRpcMcpAndSkillsE2E(t *testing.T) {
112112
serverName: copilot.MCPStdioServerConfig{
113113
Command: "echo",
114114
Args: []string{"rpc-list-mcp-server"},
115-
Tools: []string{"*"},
115+
Tools: &[]string{"*"},
116116
},
117117
},
118118
})

go/types.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -650,11 +650,15 @@ type MCPServerConfig interface {
650650
//
651651
// The Tools field controls which tools from the server are exposed:
652652
// - nil (omitted from the wire): all tools (CLI default)
653-
// - []string{"*"}: explicit "all tools"
654-
// - []string{}: no tools
655-
// - []string{"foo","bar"}: only those tools
653+
// - &[]string{"*"}: explicit "all tools"
654+
// - &[]string{}: no tools
655+
// - &[]string{"foo","bar"}: only those tools
656+
//
657+
// The pointer-to-slice form is required to distinguish "omitted" (all tools)
658+
// from "empty list" (no tools) on the wire, matching TypeScript's
659+
// `tools?: string[]` and C#'s `IList<string>?` semantics.
656660
type MCPStdioServerConfig struct {
657-
Tools []string `json:"tools,omitempty"`
661+
Tools *[]string `json:"tools,omitempty"`
658662
Timeout int `json:"timeout,omitempty"`
659663
Command string `json:"command"`
660664
Args []string `json:"args,omitempty"`
@@ -680,7 +684,7 @@ func (c MCPStdioServerConfig) MarshalJSON() ([]byte, error) {
680684
//
681685
// See [MCPStdioServerConfig] for the semantics of the Tools field.
682686
type MCPHTTPServerConfig struct {
683-
Tools []string `json:"tools,omitempty"`
687+
Tools *[]string `json:"tools,omitempty"`
684688
Timeout int `json:"timeout,omitempty"`
685689
URL string `json:"url"`
686690
Headers map[string]string `json:"headers,omitempty"`

test/scenarios/tools/mcp-servers/go/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func main() {
3333
mcpServers["example"] = copilot.MCPStdioServerConfig{
3434
Command: cmd,
3535
Args: args,
36-
Tools: []string{"*"},
36+
Tools: &[]string{"*"},
3737
}
3838
}
3939

0 commit comments

Comments
 (0)