fix: read MCP servers from both ~/.claude.json and ~/.claude/settings.json
Claude CLI stores user-scoped MCP servers in ~/.claude.json, while CodePilot was only reading from ~/.claude/settings.json. Now both the MCP list API and streamClaude merge configs from both sources. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
d3a7fd745e
commit
a5cff792e3
2 files changed
+40
-7
No files matched your search
@@ -13,16 +13,24 @@ function getSettingsPath(): string {
|
||||
return path.join(os.homedir(), '.claude', 'settings.json');
|
||||
}
|
||||
|
||||
function readSettings(): Record<string, unknown> {
|
||||
const settingsPath = getSettingsPath();
|
||||
if (!fs.existsSync(settingsPath)) return {};
|
||||
// ~/.claude.json — Claude CLI stores user-scoped MCP servers here
|
||||
function getUserConfigPath(): string {
|
||||
return path.join(os.homedir(), '.claude.json');
|
||||
}
|
||||
|
||||
function readJsonFile(filePath: string): Record<string, unknown> {
|
||||
if (!fs.existsSync(filePath)) return {};
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function readSettings(): Record<string, unknown> {
|
||||
return readJsonFile(getSettingsPath());
|
||||
}
|
||||
|
||||
function writeSettings(settings: Record<string, unknown>): void {
|
||||
const settingsPath = getSettingsPath();
|
||||
const dir = path.dirname(settingsPath);
|
||||
@@ -35,7 +43,12 @@ function writeSettings(settings: Record<string, unknown>): void {
|
||||
export async function GET(): Promise<NextResponse<MCPConfigResponse | ErrorResponse>> {
|
||||
try {
|
||||
const settings = readSettings();
|
||||
const mcpServers = (settings.mcpServers || {}) as Record<string, MCPServerConfig>;
|
||||
const userConfig = readJsonFile(getUserConfigPath());
|
||||
// Merge: ~/.claude/settings.json takes precedence over ~/.claude.json
|
||||
const mcpServers = {
|
||||
...((userConfig.mcpServers || {}) as Record<string, MCPServerConfig>),
|
||||
...((settings.mcpServers || {}) as Record<string, MCPServerConfig>),
|
||||
};
|
||||
return NextResponse.json({ mcpServers });
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -283,8 +283,28 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream<strin
|
||||
};
|
||||
}
|
||||
|
||||
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
||||
queryOptions.mcpServers = toSdkMcpConfig(mcpServers);
|
||||
// Load MCP servers: use passed-in config, or auto-read from config files
|
||||
let effectiveMcpServers = mcpServers;
|
||||
if (!effectiveMcpServers || Object.keys(effectiveMcpServers).length === 0) {
|
||||
try {
|
||||
const userConfigPath = path.join(os.homedir(), '.claude.json');
|
||||
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
||||
let merged: Record<string, MCPServerConfig> = {};
|
||||
if (fs.existsSync(userConfigPath)) {
|
||||
const userConfig = JSON.parse(fs.readFileSync(userConfigPath, 'utf-8'));
|
||||
if (userConfig.mcpServers) merged = { ...merged, ...userConfig.mcpServers };
|
||||
}
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
||||
if (settings.mcpServers) merged = { ...merged, ...settings.mcpServers };
|
||||
}
|
||||
if (Object.keys(merged).length > 0) effectiveMcpServers = merged;
|
||||
} catch {
|
||||
// ignore config read errors
|
||||
}
|
||||
}
|
||||
if (effectiveMcpServers && Object.keys(effectiveMcpServers).length > 0) {
|
||||
queryOptions.mcpServers = toSdkMcpConfig(effectiveMcpServers);
|
||||
}
|
||||
|
||||
// Resume session if we have an SDK session ID from a previous conversation turn
|
||||
|
||||
Reference in new issue
Block a user