refactor: extract MCP config handling to use temp files on Windows

This commit is contained in:
weishu
2025-12-28 11:36:56 +08:00
parent 7922356669
commit 2ef7e0ec5f
3 changed files with 70 additions and 6 deletions
+6 -3
View File
@@ -3,6 +3,7 @@ import { logger } from "@/ui/logger";
import { restoreTerminalState } from "@/ui/terminalState";
import { claudeCheckSession } from "./utils/claudeCheckSession";
import { getProjectPath } from "./utils/path";
import { appendMcpConfigArg } from "./utils/mcpConfig";
import { systemPrompt } from "./utils/systemPrompt";
import { withBunRuntimeEnv } from "@/utils/bunRuntime";
import { spawnWithAbort } from "@/utils/spawnWithAbort";
@@ -41,6 +42,7 @@ export async function claudeLocal(opts: {
}
// Spawn the process
let cleanupMcpConfig: (() => void) | null = null;
try {
// Start the interactive process
process.stdin.pause();
@@ -54,9 +56,9 @@ export async function claudeLocal(opts: {
args.push('--append-system-prompt', systemPrompt);
if (opts.mcpServers && Object.keys(opts.mcpServers).length > 0) {
args.push('--mcp-config', JSON.stringify({ mcpServers: opts.mcpServers }));
}
cleanupMcpConfig = appendMcpConfigArg(args, opts.mcpServers, {
baseDir: projectDir
});
if (opts.allowedTools && opts.allowedTools.length > 0) {
args.push('--allowedTools', opts.allowedTools.join(','));
@@ -96,6 +98,7 @@ export async function claudeLocal(opts: {
}).then(r).catch(reject);
});
} finally {
cleanupMcpConfig?.();
process.stdin.resume();
restoreTerminalState();
}
+6 -3
View File
@@ -27,6 +27,7 @@ import { withBunRuntimeEnv } from '@/utils/bunRuntime'
import { killProcessByChildProcess } from '@/utils/process'
import type { Writable } from 'node:stream'
import { logger } from '@/ui/logger'
import { appendMcpConfigArg } from '../utils/mcpConfig'
/**
* Query class manages Claude Code process interaction
@@ -285,6 +286,7 @@ export function query(config: {
// Build command arguments
const args = ['--output-format', 'stream-json', '--verbose']
let cleanupMcpConfig: (() => void) | null = null
if (customSystemPrompt) args.push('--system-prompt', customSystemPrompt)
if (appendSystemPrompt) args.push('--append-system-prompt', appendSystemPrompt)
@@ -301,9 +303,6 @@ export function query(config: {
if (settingsPath) args.push('--settings', settingsPath)
if (allowedTools.length > 0) args.push('--allowedTools', allowedTools.join(','))
if (disallowedTools.length > 0) args.push('--disallowedTools', disallowedTools.join(','))
if (mcpServers && Object.keys(mcpServers).length > 0) {
args.push('--mcp-config', JSON.stringify({ mcpServers }))
}
if (strictMcpConfig) args.push('--strict-mcp-config')
if (permissionMode) args.push('--permission-mode', permissionMode)
@@ -334,6 +333,8 @@ export function query(config: {
const spawnCommand = pathToClaudeCodeExecutable
const spawnArgs = args
cleanupMcpConfig = appendMcpConfigArg(spawnArgs, mcpServers)
// Spawn Claude Code process
// Use clean env for global claude to avoid local node_modules/.bin taking precedence
const baseEnv = isCommandOnly ? getCleanEnv() : process.env
@@ -394,6 +395,7 @@ export function query(config: {
// Handle process errors
child.on('error', (error) => {
cleanupMcpConfig?.()
if (config.options?.abort?.aborted) {
query.setError(new AbortError('Claude Code process aborted by user'))
} else {
@@ -408,6 +410,7 @@ export function query(config: {
if (process.env.CLAUDE_SDK_MCP_SERVERS) {
delete process.env.CLAUDE_SDK_MCP_SERVERS
}
cleanupMcpConfig?.()
})
return query
+58
View File
@@ -0,0 +1,58 @@
import { mkdirSync, unlinkSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
export type McpConfigArg = {
value: string;
cleanup?: () => void;
};
export type McpConfigOptions = {
useFile?: boolean;
baseDir?: string;
};
export function resolveMcpConfigArg(
mcpServers: Record<string, unknown>,
options?: McpConfigOptions
): McpConfigArg {
const configJson = JSON.stringify({ mcpServers });
const useFile = options?.useFile ?? process.platform === 'win32';
if (!useFile) {
return { value: configJson };
}
const dir = options?.baseDir ?? tmpdir();
mkdirSync(dir, { recursive: true });
const filePath = join(
dir,
`mcp-config-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.json`
);
writeFileSync(filePath, configJson, "utf8");
return {
value: filePath,
cleanup: () => {
try {
unlinkSync(filePath);
} catch {
// Ignore cleanup errors; config file is optional and short-lived.
}
}
};
}
export function appendMcpConfigArg(
args: string[],
mcpServers?: Record<string, unknown>,
options?: McpConfigOptions
): (() => void) | null {
if (!mcpServers || Object.keys(mcpServers).length === 0) {
return null;
}
const { value, cleanup } = resolveMcpConfigArg(mcpServers, options);
args.push('--mcp-config', value);
return cleanup ?? null;
}