diff options
Diffstat (limited to 'src/main/claude/index.ts')
| -rw-r--r-- | src/main/claude/index.ts | 59 |
1 files changed, 47 insertions, 12 deletions
diff --git a/src/main/claude/index.ts b/src/main/claude/index.ts index ca54164..8cf512c 100644 --- a/src/main/claude/index.ts +++ b/src/main/claude/index.ts | |||
| @@ -53,7 +53,38 @@ export async function sendMessage({ | |||
| 53 | 53 | ||
| 54 | // Load MCP servers config (JSON string → object, or undefined if not set) | 54 | // Load MCP servers config (JSON string → object, or undefined if not set) |
| 55 | const mcpServersJson = getSetting("mcpServers"); | 55 | const mcpServersJson = getSetting("mcpServers"); |
| 56 | const mcpServers = mcpServersJson ? JSON.parse(mcpServersJson) : undefined; | 56 | const mcpServersConfig = mcpServersJson ? JSON.parse(mcpServersJson) : undefined; |
| 57 | |||
| 58 | // Build allowedTools list from enabled MCP tools | ||
| 59 | // Format: mcp__servername__toolname | ||
| 60 | const mcpAllowedTools: string[] = []; | ||
| 61 | if (mcpServersConfig) { | ||
| 62 | for (const [serverName, config] of Object.entries(mcpServersConfig)) { | ||
| 63 | const serverConfig = config as { | ||
| 64 | enabledTools?: string[]; | ||
| 65 | discoveredTools?: Array<{ name: string }>; | ||
| 66 | }; | ||
| 67 | // Use enabledTools if available, otherwise allow all discovered tools | ||
| 68 | const enabledTools = serverConfig.enabledTools || | ||
| 69 | serverConfig.discoveredTools?.map((t) => t.name) || | ||
| 70 | []; | ||
| 71 | for (const toolName of enabledTools) { | ||
| 72 | mcpAllowedTools.push(`mcp__${serverName}__${toolName}`); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | // Strip tool management fields from config before passing to SDK | ||
| 78 | // SDK only needs: type, command, args, env, url, headers | ||
| 79 | let mcpServers: Record<string, unknown> | undefined; | ||
| 80 | if (mcpServersConfig && Object.keys(mcpServersConfig).length > 0) { | ||
| 81 | mcpServers = Object.fromEntries( | ||
| 82 | Object.entries(mcpServersConfig).map(([name, config]) => { | ||
| 83 | const { discoveredTools, enabledTools, ...sdkConfig } = config as Record<string, unknown>; | ||
| 84 | return [name, sdkConfig]; | ||
| 85 | }) | ||
| 86 | ); | ||
| 87 | } | ||
| 57 | 88 | ||
| 58 | const phaseConfig = getPhaseConfig( | 89 | const phaseConfig = getPhaseConfig( |
| 59 | session.phase as Phase, | 90 | session.phase as Phase, |
| @@ -62,28 +93,32 @@ export async function sendMessage({ | |||
| 62 | customSystemPrompt | 93 | customSystemPrompt |
| 63 | ); | 94 | ); |
| 64 | 95 | ||
| 96 | // Build allowedTools for this phase | ||
| 97 | const allowedTools: string[] = []; | ||
| 98 | if (session.phase === "implement") { | ||
| 99 | // Allow git inspection in implement phase | ||
| 100 | allowedTools.push("Bash(git status*)", "Bash(git log*)", "Bash(git diff*)"); | ||
| 101 | } | ||
| 102 | if (mcpAllowedTools.length > 0) { | ||
| 103 | // Add enabled MCP tools | ||
| 104 | allowedTools.push(...mcpAllowedTools); | ||
| 105 | } | ||
| 106 | |||
| 65 | const q = query({ | 107 | const q = query({ |
| 66 | prompt: message, | 108 | prompt: message, |
| 67 | options: { | 109 | options: { |
| 68 | cwd: project.path, | 110 | cwd: project.path, |
| 69 | model: configuredModel, | 111 | model: configuredModel, |
| 70 | mcpServers, | 112 | // eslint-disable-next-line @typescript-eslint/no-explicit-any |
| 113 | mcpServers: mcpServers as any, | ||
| 71 | resume: session.claude_session_id ?? undefined, | 114 | resume: session.claude_session_id ?? undefined, |
| 72 | tools: phaseConfig.tools, | 115 | tools: phaseConfig.tools, |
| 73 | permissionMode: phaseConfig.permissionMode, | 116 | permissionMode: phaseConfig.permissionMode, |
| 74 | // Required companion flag when bypassPermissions is active | 117 | // Required companion flag when bypassPermissions is active |
| 75 | allowDangerouslySkipPermissions: phaseConfig.permissionMode === "bypassPermissions", | 118 | allowDangerouslySkipPermissions: phaseConfig.permissionMode === "bypassPermissions", |
| 76 | systemPrompt: phaseConfig.systemPrompt, | 119 | systemPrompt: phaseConfig.systemPrompt, |
| 77 | // Allow Claude to inspect git state during implementation without prompts. | 120 | // Pre-approve specific tools to avoid permission prompts |
| 78 | // git add/commit intentionally omitted — the app handles those. | 121 | ...(allowedTools.length > 0 && { allowedTools }), |
| 79 | ...(session.phase === "implement" && { | ||
| 80 | allowedTools: ["Bash(git status*)", "Bash(git log*)", "Bash(git diff*)"], | ||
| 81 | }), | ||
| 82 | // When MCPs are configured in research phase, bypass permissions to allow MCP tools | ||
| 83 | ...(session.phase === "research" && mcpServers && { | ||
| 84 | permissionMode: "bypassPermissions" as const, | ||
| 85 | allowDangerouslySkipPermissions: true, | ||
| 86 | }), | ||
| 87 | }, | 122 | }, |
| 88 | }); | 123 | }); |
| 89 | 124 | ||
