mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
refactor: extract MCP config handling to use temp files on Windows
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user