Merge pull request #1616 from code-yeongyu/fix/814-user-mcp-config
fix(mcp-loader): read user-level MCP config from ~/.claude.json (#814)
This commit is contained in:
commit
2c394cd497
@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach } from "bun:test"
|
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test"
|
||||||
import { mkdirSync, writeFileSync, rmSync } from "fs"
|
import { mkdirSync, writeFileSync, rmSync } from "fs"
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
import { tmpdir } from "os"
|
import { tmpdir } from "os"
|
||||||
@ -126,37 +126,70 @@ describe("getSystemMcpServerNames", () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it("merges server names from multiple .mcp.json files", async () => {
|
it("merges server names from multiple .mcp.json files", async () => {
|
||||||
// given
|
// given
|
||||||
mkdirSync(join(TEST_DIR, ".claude"), { recursive: true })
|
mkdirSync(join(TEST_DIR, ".claude"), { recursive: true })
|
||||||
|
|
||||||
const projectMcp = {
|
const projectMcp = {
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
playwright: { command: "npx", args: ["@playwright/mcp@latest"] },
|
playwright: { command: "npx", args: ["@playwright/mcp@latest"] },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const localMcp = {
|
const localMcp = {
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
memory: { command: "npx", args: ["-y", "@anthropic-ai/mcp-server-memory"] },
|
memory: { command: "npx", args: ["-y", "@anthropic-ai/mcp-server-memory"] },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFileSync(join(TEST_DIR, ".mcp.json"), JSON.stringify(projectMcp))
|
writeFileSync(join(TEST_DIR, ".mcp.json"), JSON.stringify(projectMcp))
|
||||||
writeFileSync(join(TEST_DIR, ".claude", ".mcp.json"), JSON.stringify(localMcp))
|
writeFileSync(join(TEST_DIR, ".claude", ".mcp.json"), JSON.stringify(localMcp))
|
||||||
|
|
||||||
const originalCwd = process.cwd()
|
const originalCwd = process.cwd()
|
||||||
process.chdir(TEST_DIR)
|
process.chdir(TEST_DIR)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// when
|
// when
|
||||||
const { getSystemMcpServerNames } = await import("./loader")
|
const { getSystemMcpServerNames } = await import("./loader")
|
||||||
const names = getSystemMcpServerNames()
|
const names = getSystemMcpServerNames()
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(names.has("playwright")).toBe(true)
|
expect(names.has("playwright")).toBe(true)
|
||||||
expect(names.has("memory")).toBe(true)
|
expect(names.has("memory")).toBe(true)
|
||||||
} finally {
|
} finally {
|
||||||
process.chdir(originalCwd)
|
process.chdir(originalCwd)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("reads user-level MCP config from ~/.claude.json", async () => {
|
||||||
|
// given
|
||||||
|
const userConfigPath = join(TEST_DIR, ".claude.json")
|
||||||
|
const userMcpConfig = {
|
||||||
|
mcpServers: {
|
||||||
|
"user-server": {
|
||||||
|
command: "npx",
|
||||||
|
args: ["user-mcp-server"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalCwd = process.cwd()
|
||||||
|
process.chdir(TEST_DIR)
|
||||||
|
|
||||||
|
try {
|
||||||
|
mock.module("os", () => ({
|
||||||
|
homedir: () => TEST_DIR,
|
||||||
|
tmpdir,
|
||||||
|
}))
|
||||||
|
|
||||||
|
writeFileSync(userConfigPath, JSON.stringify(userMcpConfig))
|
||||||
|
|
||||||
|
const { getSystemMcpServerNames } = await import("./loader")
|
||||||
|
const names = getSystemMcpServerNames()
|
||||||
|
|
||||||
|
expect(names.has("user-server")).toBe(true)
|
||||||
|
} finally {
|
||||||
|
process.chdir(originalCwd)
|
||||||
|
rmSync(userConfigPath, { force: true })
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { existsSync, readFileSync } from "fs"
|
import { existsSync, readFileSync } from "fs"
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
|
import { homedir } from "os"
|
||||||
import { getClaudeConfigDir } from "../../shared"
|
import { getClaudeConfigDir } from "../../shared"
|
||||||
import type {
|
import type {
|
||||||
ClaudeCodeMcpConfig,
|
ClaudeCodeMcpConfig,
|
||||||
@ -16,11 +17,10 @@ interface McpConfigPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getMcpConfigPaths(): McpConfigPath[] {
|
function getMcpConfigPaths(): McpConfigPath[] {
|
||||||
const claudeConfigDir = getClaudeConfigDir()
|
|
||||||
const cwd = process.cwd()
|
const cwd = process.cwd()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ path: join(claudeConfigDir, ".mcp.json"), scope: "user" },
|
{ path: join(homedir(), ".claude.json"), scope: "user" },
|
||||||
{ path: join(cwd, ".mcp.json"), scope: "project" },
|
{ path: join(cwd, ".mcp.json"), scope: "project" },
|
||||||
{ path: join(cwd, ".claude", ".mcp.json"), scope: "local" },
|
{ path: join(cwd, ".claude", ".mcp.json"), scope: "local" },
|
||||||
]
|
]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user