diff --git a/.mcp.json b/.mcp.json index 01021df64d9..9ca83b55e03 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,5 +1,5 @@ { - "servers": { + "mcpServers": { "github-agentic-workflows": { "command": "gh", "args": ["aw", "mcp-server"] diff --git a/docs/src/content/docs/reference/gh-aw-as-mcp-server.md b/docs/src/content/docs/reference/gh-aw-as-mcp-server.md index 24c4b0b2255..e4c81f181aa 100644 --- a/docs/src/content/docs/reference/gh-aw-as-mcp-server.md +++ b/docs/src/content/docs/reference/gh-aw-as-mcp-server.md @@ -72,7 +72,7 @@ Alternatively, create `.mcp.json` manually: ```json wrap { - "servers": { + "mcpServers": { "github-agentic-workflows": { "command": "gh", "args": ["aw", "mcp-server"] diff --git a/pkg/cli/init_mcp_test.go b/pkg/cli/init_mcp_test.go index 982dca422af..7eedc8d98e1 100644 --- a/pkg/cli/init_mcp_test.go +++ b/pkg/cli/init_mcp_test.go @@ -93,11 +93,11 @@ func TestInitRepository_WithMCP(t *testing.T) { t.Fatalf("Failed to parse .mcp.json: %v", err) } - if _, exists := config.Servers["github-agentic-workflows"]; !exists { + if _, exists := config.MCPServers["github-agentic-workflows"]; !exists { t.Errorf("Expected .mcp.json to contain github-agentic-workflows server") } - server := config.Servers["github-agentic-workflows"] + server := config.MCPServers["github-agentic-workflows"] if server.Command != "gh" { t.Errorf("Expected command to be 'gh', got %s", server.Command) } @@ -178,7 +178,7 @@ func TestEnsureMCPConfig_RendersInstructions(t *testing.T) { // Create initial .mcp.json with a different server initialConfig := MCPConfig{ - Servers: map[string]VSCodeMCPServer{ + MCPServers: map[string]VSCodeMCPServer{ "other-server": { Command: "other-command", Args: []string{"arg1"}, @@ -208,12 +208,12 @@ func TestEnsureMCPConfig_RendersInstructions(t *testing.T) { } // Check that other-server still exists - if _, exists := config.Servers["other-server"]; !exists { + if _, exists := config.MCPServers["other-server"]; !exists { t.Errorf("Expected existing 'other-server' to be preserved") } // Check that github-agentic-workflows was NOT added (file should not be modified) - if _, exists := config.Servers["github-agentic-workflows"]; exists { + if _, exists := config.MCPServers["github-agentic-workflows"]; exists { t.Errorf("Expected 'github-agentic-workflows' server to NOT be added (should render instructions instead)") } } diff --git a/pkg/cli/mcp_config_file.go b/pkg/cli/mcp_config_file.go index d9ff14b77e1..2ee4f87d864 100644 --- a/pkg/cli/mcp_config_file.go +++ b/pkg/cli/mcp_config_file.go @@ -21,9 +21,11 @@ type VSCodeMCPServer struct { CWD string `json:"cwd,omitempty"` } -// MCPConfig represents the structure of mcp.json +// MCPConfig represents the structure of .mcp.json for Claude Code. type MCPConfig struct { - Servers map[string]VSCodeMCPServer `json:"servers"` + MCPServers map[string]VSCodeMCPServer `json:"mcpServers,omitempty"` + // Servers is a legacy key kept for backward-compatible reads of existing .mcp.json files. + Servers map[string]VSCodeMCPServer `json:"servers,omitempty"` } // ensureMCPConfig creates .mcp.json with gh-aw MCP server configuration @@ -51,7 +53,7 @@ func ensureMCPConfig(verbose bool) error { } // Check if the server is already configured correctly - if existingConfig, exists := config.Servers[ghAwServerName]; exists { + if existingConfig, exists := config.MCPServers[ghAwServerName]; exists { existingJSON, _ := json.Marshal(existingConfig) newJSON, _ := json.Marshal(ghAwConfig) if string(existingJSON) == string(newJSON) { @@ -72,9 +74,9 @@ func ensureMCPConfig(verbose bool) error { // File doesn't exist - create it mcpConfigLog.Print("No existing config found, creating new one") config := MCPConfig{ - Servers: make(map[string]VSCodeMCPServer), + MCPServers: make(map[string]VSCodeMCPServer), } - config.Servers[ghAwServerName] = ghAwConfig + config.MCPServers[ghAwServerName] = ghAwConfig // Write config file with proper indentation data, err := json.MarshalIndent(config, "", " ") @@ -98,7 +100,7 @@ func renderMCPConfigUpdateInstructions(filePath, serverName string, serverConfig "Existing file detected: "+filePath) fmt.Fprintln(os.Stderr) fmt.Fprintln(os.Stderr, "To enable GitHub Copilot Agent MCP server integration, please add the following") - fmt.Fprintln(os.Stderr, "to the \"servers\" section of your .mcp.json file:") + fmt.Fprintln(os.Stderr, "to the \"mcpServers\" section of your .mcp.json file:") fmt.Fprintln(os.Stderr) // Generate the JSON to add diff --git a/pkg/cli/mcp_config_file_test.go b/pkg/cli/mcp_config_file_test.go index 68aa78e2e1a..41089d74282 100644 --- a/pkg/cli/mcp_config_file_test.go +++ b/pkg/cli/mcp_config_file_test.go @@ -23,10 +23,10 @@ func TestEnsureMCPConfig(t *testing.T) { verbose: false, wantErr: false, validateContent: func(t *testing.T, config *MCPConfig) { - if config.Servers == nil { - t.Error("Expected servers map to be initialized") + if config.MCPServers == nil { + t.Error("Expected mcpServers map to be initialized") } - server, exists := config.Servers["github-agentic-workflows"] + server, exists := config.MCPServers["github-agentic-workflows"] if !exists { t.Error("Expected github-agentic-workflows server to exist") } @@ -41,7 +41,7 @@ func TestEnsureMCPConfig(t *testing.T) { { name: "renders instructions for existing config without gh-aw server", existingConfig: &MCPConfig{ - Servers: map[string]VSCodeMCPServer{ + MCPServers: map[string]VSCodeMCPServer{ "other-server": { Command: "node", Args: []string{"server.js"}, @@ -52,14 +52,14 @@ func TestEnsureMCPConfig(t *testing.T) { wantErr: false, validateContent: func(t *testing.T, config *MCPConfig) { // File should NOT be modified - should remain with only 1 server - if len(config.Servers) != 1 { - t.Errorf("Expected 1 server (file should not be modified), got %d", len(config.Servers)) + if len(config.MCPServers) != 1 { + t.Errorf("Expected 1 server (file should not be modified), got %d", len(config.MCPServers)) } - if _, exists := config.Servers["other-server"]; !exists { + if _, exists := config.MCPServers["other-server"]; !exists { t.Error("Expected existing other-server to be preserved") } // gh-aw server should NOT be added (instructions rendered instead) - if _, exists := config.Servers["github-agentic-workflows"]; exists { + if _, exists := config.MCPServers["github-agentic-workflows"]; exists { t.Error("Expected github-agentic-workflows server to NOT be added (instructions should be rendered)") } }, @@ -67,7 +67,7 @@ func TestEnsureMCPConfig(t *testing.T) { { name: "skips update when config is identical", existingConfig: &MCPConfig{ - Servers: map[string]VSCodeMCPServer{ + MCPServers: map[string]VSCodeMCPServer{ "github-agentic-workflows": { Command: "gh", Args: []string{"aw", "mcp-server"}, @@ -77,15 +77,15 @@ func TestEnsureMCPConfig(t *testing.T) { verbose: false, wantErr: false, validateContent: func(t *testing.T, config *MCPConfig) { - if len(config.Servers) != 1 { - t.Errorf("Expected 1 server, got %d", len(config.Servers)) + if len(config.MCPServers) != 1 { + t.Errorf("Expected 1 server, got %d", len(config.MCPServers)) } }, }, { name: "renders instructions for existing config with different settings", existingConfig: &MCPConfig{ - Servers: map[string]VSCodeMCPServer{ + MCPServers: map[string]VSCodeMCPServer{ "github-agentic-workflows": { Command: "old-command", Args: []string{"old-arg"}, @@ -96,7 +96,7 @@ func TestEnsureMCPConfig(t *testing.T) { wantErr: false, validateContent: func(t *testing.T, config *MCPConfig) { // File should NOT be modified - old settings should remain - server := config.Servers["github-agentic-workflows"] + server := config.MCPServers["github-agentic-workflows"] if server.Command != "old-command" { t.Errorf("Expected command to remain 'old-command' (file should not be modified), got %q", server.Command) } @@ -189,7 +189,7 @@ func TestMCPConfigParsing(t *testing.T) { { name: "valid config with single server", jsonData: `{ - "servers": { + "mcpServers": { "test-server": { "command": "node", "args": ["server.js"] @@ -202,7 +202,7 @@ func TestMCPConfigParsing(t *testing.T) { { name: "valid config with CWD", jsonData: `{ - "servers": { + "mcpServers": { "test-server": { "command": "gh", "args": ["aw", "mcp-server"], @@ -215,14 +215,27 @@ func TestMCPConfigParsing(t *testing.T) { }, { name: "invalid JSON", - jsonData: `{"servers": invalid}`, + jsonData: `{"mcpServers": invalid}`, wantErr: true, wantValid: false, }, { name: "empty config", jsonData: `{ - "servers": {} + "mcpServers": {} + }`, + wantErr: false, + wantValid: true, + }, + { + name: "legacy config key", + jsonData: `{ + "servers": { + "test-server": { + "command": "node", + "args": ["server.js"] + } + } }`, wantErr: false, wantValid: true, @@ -240,8 +253,15 @@ func TestMCPConfigParsing(t *testing.T) { } if !tt.wantErr && tt.wantValid { - if config.Servers == nil { - t.Error("Expected servers map to be initialized") + if tt.name == "legacy config key" { + if config.Servers == nil { + t.Error("Expected legacy servers map to be initialized") + } + if config.MCPServers != nil { + t.Error("Expected mcpServers map to be nil for legacy-only config") + } + } else if config.MCPServers == nil { + t.Error("Expected mcpServers map to be initialized") } } }) @@ -252,7 +272,7 @@ func TestMCPConfigJSONMarshaling(t *testing.T) { t.Parallel() config := MCPConfig{ - Servers: map[string]VSCodeMCPServer{ + MCPServers: map[string]VSCodeMCPServer{ "github-agentic-workflows": { Command: "gh", Args: []string{"aw", "mcp-server"}, @@ -273,11 +293,11 @@ func TestMCPConfigJSONMarshaling(t *testing.T) { } // Verify structure - if len(unmarshaledConfig.Servers) != 1 { - t.Errorf("Expected 1 server, got %d", len(unmarshaledConfig.Servers)) + if len(unmarshaledConfig.MCPServers) != 1 { + t.Errorf("Expected 1 server, got %d", len(unmarshaledConfig.MCPServers)) } - server, exists := unmarshaledConfig.Servers["github-agentic-workflows"] + server, exists := unmarshaledConfig.MCPServers["github-agentic-workflows"] if !exists { t.Fatal("Expected github-agentic-workflows server to exist") }