Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.
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
8 changes: 4 additions & 4 deletions internal/mcp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ func (c *MCPConfig) UnmarshalJSON(data []byte) error {
}{
Alias: (*Alias)(c),
}
if err := json.Unmarshal(data, &aux); err != nil {
if err := jsonc.Unmarshal(data, &aux); err != nil {
return err
}
// Unmarshal into a map to find extra fields
var all map[string]json.RawMessage
if err := json.Unmarshal(data, &all); err != nil {
if err := jsonc.Unmarshal(data, &all); err != nil {
return err
}
// Remove known fields
Expand All @@ -82,7 +82,7 @@ func (c *MCPConfig) UnmarshalJSON(data []byte) error {
c.Extra = make(map[string]interface{})
for k, v := range all {
var val interface{}
if err := json.Unmarshal(v, &val); err != nil {
if err := jsonc.Unmarshal(v, &val); err != nil {
c.Extra[k] = string(v) // fallback to raw string
} else {
c.Extra[k] = val
Expand All @@ -105,7 +105,7 @@ func (c *MCPConfig) MarshalJSON() ([]byte, error) {
}
// Unmarshal back into a map to merge with Extra
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
if err := jsonc.Unmarshal(data, &m); err != nil {
return nil, err
}
for k, v := range c.Extra {
Expand Down
252 changes: 252 additions & 0 deletions internal/mcp/config_jsonc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
package mcp

import (
"os"
"path/filepath"
"testing"
)

func TestJSONCCommentSupport(t *testing.T) {
tests := []struct {
name string
jsonContent string
expectError bool
}{
{
name: "User's exact case: /* test */ at top",
jsonContent: `/* test */
{
"editor.fontSize": 14,
"workbench.colorTheme": "Default Dark+",
"mcpServers": {
"agentuity": {
"command": "npx",
"args": ["-y", "@agentuity/mcp-server"],
"env": {
"AGENTUITY_API_KEY": "${AGENTUITY_API_KEY}"
}
}
}
}`,
expectError: false,
},
{
name: "Single line comments",
jsonContent: `{
"editor.fontSize": 14,
"mcpServers": {
"agentuity": {
"command": "npx", // inline comment
"args": ["-y", "@agentuity/mcp-server"]
}
}
}`,
expectError: false,
},
{
name: "Multi-line comments",
jsonContent: `{
/* This is a
multi-line comment */
"mcpServers": {
"test": {
"command": "test"
}
},
/* Another comment */
"ampMcpServers": {
"amp": {
"command": "amp"
}
}
}`,
expectError: false,
},
{
name: "Mixed comments comprehensive",
jsonContent: `{
/* test comment at start */
"editor.fontSize": 14,
"workbench.colorTheme": "Default Dark+",
"mcpServers": {
"agentuity": {
"command": "npx", // inline comment
"args": ["-y", "@agentuity/mcp-server"],
"env": {
"AGENTUITY_API_KEY": "${AGENTUITY_API_KEY}" /* env var comment */
}
}
},
/* Extension settings */
"extensions.autoUpdate": false,
"ampMcpServers": {
"test": {
"command": "test"
}
}
}`,
expectError: false,
},
{
name: "Empty JSON with comment",
jsonContent: `/* test */
{
}`,
expectError: false,
},
{
name: "Comment at end",
jsonContent: `{
"mcpServers": {
"test": {
"command": "test"
}
}
}
/* end comment */`,
expectError: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, "test_config.json")

err := os.WriteFile(configPath, []byte(tt.jsonContent), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}

config, err := loadConfig(configPath)
if tt.expectError {
if err == nil {
t.Errorf("Expected error but got none")
}
return
}

if err != nil {
t.Errorf("Unexpected error: %v", err)
return
}

if config == nil {
t.Errorf("Config should not be nil")
return
}

t.Logf("Successfully parsed config with %d mcpServers, %d ampMcpServers, %d extra fields",
len(config.MCPServers), len(config.AMPMCPServers), len(config.Extra))
})
}
}

func TestCompleteLoadSaveCycleWithComments(t *testing.T) {
jsonWithComments := `/* test comment */
{
"editor.fontSize": 14,
"workbench.colorTheme": "Default Dark+",
"mcpServers": {
"agentuity": {
"command": "npx",
"args": ["-y", "@agentuity/mcp-server"],
"env": {
"AGENTUITY_API_KEY": "${AGENTUITY_API_KEY}"
}
}
},
/* Extension settings */
"extensions.autoUpdate": false
}`

tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, "test_config.json")

err := os.WriteFile(configPath, []byte(jsonWithComments), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}

config, err := loadConfig(configPath)
if err != nil {
t.Fatalf("Failed to load config: %v", err)
}

if len(config.MCPServers) != 1 {
t.Errorf("Expected 1 mcpServer, got %d", len(config.MCPServers))
}

if len(config.Extra) != 3 {
t.Errorf("Expected 3 extra fields, got %d", len(config.Extra))
}

marshaledData, err := config.MarshalJSON()
if err != nil {
t.Fatalf("Failed to marshal config: %v", err)
}

if len(marshaledData) == 0 {
t.Errorf("Marshaled data should not be empty")
}

t.Logf("Successfully completed load-save cycle with comments")
}

func TestCursorSettingsWithComments(t *testing.T) {
cursorSettings := `/* test */
{
"editor.fontSize": 14,
"workbench.colorTheme": "Default Dark+",
"mcpServers": {
"agentuity": {
"command": "npx",
"args": ["-y", "@agentuity/mcp-server"],
"env": {
"AGENTUITY_API_KEY": "${AGENTUITY_API_KEY}"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"]
}
},
"extensions.autoUpdate": false
}`

tmpDir := t.TempDir()
settingsPath := filepath.Join(tmpDir, "settings.json")

err := os.WriteFile(settingsPath, []byte(cursorSettings), 0644)
if err != nil {
t.Fatalf("Failed to write Cursor settings file: %v", err)
}

config, err := loadConfig(settingsPath)
if err != nil {
t.Fatalf("Failed to load Cursor settings with comments: %v", err)
}

if len(config.MCPServers) != 2 {
t.Errorf("Expected 2 mcpServers in Cursor settings, got %d", len(config.MCPServers))
}

if _, exists := config.MCPServers["agentuity"]; !exists {
t.Errorf("Expected 'agentuity' mcpServer in Cursor settings")
}

if _, exists := config.MCPServers["filesystem"]; !exists {
t.Errorf("Expected 'filesystem' mcpServer in Cursor settings")
}

marshaledData, err := config.MarshalJSON()
if err != nil {
t.Fatalf("Failed to marshal Cursor settings: %v", err)
}

if len(marshaledData) == 0 {
t.Errorf("Marshaled Cursor settings should not be empty")
}

t.Logf("Successfully processed Cursor settings with /* test */ comment")
}
Loading