Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .mcp.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"servers": {
"mcpServers": {
"github-agentic-workflows": {
"command": "gh",
"args": ["aw", "mcp-server"]
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/reference/gh-aw-as-mcp-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Alternatively, create `.mcp.json` manually:

```json wrap
{
"servers": {
"mcpServers": {
"github-agentic-workflows": {
"command": "gh",
"args": ["aw", "mcp-server"]
Expand Down
10 changes: 5 additions & 5 deletions pkg/cli/init_mcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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"},
Expand Down Expand Up @@ -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)")
}
}
Expand Down
14 changes: 8 additions & 6 deletions pkg/cli/mcp_config_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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, "", " ")
Expand All @@ -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
Expand Down
66 changes: 43 additions & 23 deletions pkg/cli/mcp_config_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand All @@ -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"},
Expand All @@ -52,22 +52,22 @@ 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)")
}
},
},
{
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"},
Expand All @@ -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"},
Expand All @@ -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)
}
Expand Down Expand Up @@ -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"]
Expand All @@ -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"],
Expand All @@ -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,
Expand All @@ -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")
}
Comment on lines 255 to 265
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

This test branches on tt.name == "legacy config key" to decide which assertions to run. That makes the test fragile (renaming the case can silently change behavior). Consider adding an explicit boolean/enum field on the table (e.g., isLegacyKey) to drive the assertions instead of string-matching the test name.

Copilot uses AI. Check for mistakes.
}
})
Expand All @@ -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"},
Expand All @@ -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")
}
Expand Down
Loading