fix(doctor): detect MCP server in project-level settings (#1104)
Apply same fix as #1091 to isMCPServerInstalled(): check all three settings locations (user-level, project-level, and project-local). - Extract checkMCPInSettings() helper function - Check ~/.claude/settings.json, .claude/settings.json, and .claude/settings.local.json - Add TestIsMCPServerInstalledProjectLevel with positive and negative cases Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -142,15 +142,39 @@ func checkPluginInSettings(settingsPath string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// isMCPServerInstalled checks if MCP server is configured
|
// isMCPServerInstalled checks if MCP server is configured.
|
||||||
|
// It checks user-level (~/.claude/settings.json) and project-level settings
|
||||||
|
// (.claude/settings.json and .claude/settings.local.json).
|
||||||
func isMCPServerInstalled() bool {
|
func isMCPServerInstalled() bool {
|
||||||
home, err := os.UserHomeDir()
|
home, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsPath := filepath.Join(home, ".claude/settings.json")
|
// Check user-level settings
|
||||||
data, err := os.ReadFile(settingsPath) // #nosec G304 -- settingsPath is constructed from user home dir, not user input
|
userSettings := filepath.Join(home, ".claude", "settings.json")
|
||||||
|
if checkMCPInSettings(userSettings) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check project-level settings
|
||||||
|
projectSettings := filepath.Join(".claude", "settings.json")
|
||||||
|
if checkMCPInSettings(projectSettings) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check project-level local settings (gitignored)
|
||||||
|
projectLocalSettings := filepath.Join(".claude", "settings.local.json")
|
||||||
|
if checkMCPInSettings(projectLocalSettings) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkMCPInSettings checks if beads MCP server is configured in a settings file
|
||||||
|
func checkMCPInSettings(settingsPath string) bool {
|
||||||
|
data, err := os.ReadFile(settingsPath) // #nosec G304 -- settingsPath is constructed from known safe locations, not user input
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,6 +169,64 @@ func TestIsMCPServerInstalled(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsMCPServerInstalledProjectLevel(t *testing.T) {
|
||||||
|
mcpContent := `{"mcpServers":{"beads":{"command":"beads-mcp"}}}`
|
||||||
|
|
||||||
|
// Test that MCP server is detected in each project-level settings file
|
||||||
|
for _, filename := range []string{"settings.json", "settings.local.json"} {
|
||||||
|
t.Run(filename, func(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
t.Chdir(tmpDir)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(".claude", 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(".claude", filename), []byte(mcpContent), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isMCPServerInstalled() {
|
||||||
|
t.Errorf("expected to detect MCP server in .claude/%s", filename)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test negative cases
|
||||||
|
t.Run("no mcpServers section", func(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
t.Chdir(tmpDir)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(".claude", 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
content := `{"hooks":{}}`
|
||||||
|
if err := os.WriteFile(filepath.Join(".claude", "settings.json"), []byte(content), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isMCPServerInstalled() {
|
||||||
|
t.Error("expected NOT to detect MCP server when mcpServers section missing")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("mcpServers but not beads", func(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
t.Chdir(tmpDir)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(".claude", 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
content := `{"mcpServers":{"other-server":{"command":"other"}}}`
|
||||||
|
if err := os.WriteFile(filepath.Join(".claude", "settings.json"), []byte(content), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isMCPServerInstalled() {
|
||||||
|
t.Error("expected NOT to detect MCP server when beads not present")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestIsBeadsPluginInstalled(t *testing.T) {
|
func TestIsBeadsPluginInstalled(t *testing.T) {
|
||||||
// Similar sanity check for plugin detection
|
// Similar sanity check for plugin detection
|
||||||
result := isBeadsPluginInstalled()
|
result := isBeadsPluginInstalled()
|
||||||
|
|||||||
Reference in New Issue
Block a user