From bf6212cae320537baed8a0040811d1182d0ad7e0 Mon Sep 17 00:00:00 2001 From: Shawn Morreau Date: Tue, 18 Nov 2025 20:02:25 -0500 Subject: [PATCH] undo david --- agents/claude.ts | 3 -- agents/codex.ts | 82 +++++++++++++++++++++++------------- agents/cursor.ts | 107 +++++++++++------------------------------------ agents/gemini.ts | 66 +++++++++++++++++------------ agents/shared.ts | 14 +------ main.ts | 14 +------ mcp/config.ts | 7 ++-- 7 files changed, 124 insertions(+), 169 deletions(-) diff --git a/agents/claude.ts b/agents/claude.ts index 91274ef..a00a036 100644 --- a/agents/claude.ts +++ b/agents/claude.ts @@ -15,9 +15,6 @@ export const claude = agent({ executablePath: "cli.js", }); }, - addMcpServer: () => { - // Claude accepts mcpServers directly in the query options, no configuration needed - }, run: async ({ prompt, mcpServers, apiKey, cliPath }) => { process.env.ANTHROPIC_API_KEY = apiKey; diff --git a/agents/codex.ts b/agents/codex.ts index 76c6234..88b99ec 100644 --- a/agents/codex.ts +++ b/agents/codex.ts @@ -1,4 +1,5 @@ import { spawnSync } from "node:child_process"; +import type { McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk"; import { Codex, type CodexOptions, type ThreadEvent } from "@openai/codex-sdk"; import { log } from "../utils/cli.ts"; import { addInstructions } from "./instructions.ts"; @@ -14,35 +15,20 @@ export const codex = agent({ executablePath: "bin/codex.js", }); }, - addMcpServer: ({ serverName, serverConfig, cliPath }) => { - // Codex CLI syntax: codex mcp add --env KEY=value -- [args...] - const command = serverConfig.command; - const args = serverConfig.args || []; - const envVars = serverConfig.env || {}; - - const addArgs = ["mcp", "add", serverName]; - - // Add environment variables as --env flags first - for (const [key, value] of Object.entries(envVars)) { - addArgs.push("--env", `${key}=${value}`); - } - - addArgs.push("--", command, ...args); - - log.info(`Adding MCP server '${serverName}'...`); - const addResult = spawnSync("node", [cliPath, ...addArgs], { - stdio: "pipe", - encoding: "utf-8", - }); - - if (addResult.status !== 0) { - throw new Error( - `codex mcp add failed: ${addResult.stderr || addResult.stdout || "Unknown error"}` - ); - } - log.info(`✓ MCP server '${serverName}' configured`); - }, run: async ({ prompt, mcpServers, apiKey, cliPath, githubInstallationToken }) => { + // Configure MCP servers if provided + if (mcpServers && Object.keys(mcpServers).length > 0) { + // Filter to only stdio servers + const stdioServers: Record = {}; + for (const [serverName, serverConfig] of Object.entries(mcpServers)) { + if ("command" in serverConfig) { + stdioServers[serverName] = serverConfig; + } + } + if (Object.keys(stdioServers).length > 0) { + configureCodexMcpServers({ mcpServers: stdioServers, cliPath }); + } + } process.env.OPENAI_API_KEY = apiKey; process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken; @@ -182,3 +168,43 @@ const messageHandlers: { log.error(`Error: ${event.message}`); }, }; + +/** + * Configure MCP servers for Codex using the CLI. + * Codex CLI syntax: codex mcp add --env KEY=value -- [args...] + */ +function configureCodexMcpServers({ + mcpServers, + cliPath, +}: { + mcpServers: Record; + cliPath: string; +}): void { + for (const [serverName, serverConfig] of Object.entries(mcpServers)) { + const command = serverConfig.command; + const args = serverConfig.args || []; + const envVars = serverConfig.env || {}; + + const addArgs = ["mcp", "add", serverName]; + + // Add environment variables as --env flags first + for (const [key, value] of Object.entries(envVars)) { + addArgs.push("--env", `${key}=${value}`); + } + + addArgs.push("--", command, ...args); + + log.info(`Adding MCP server '${serverName}'...`); + const addResult = spawnSync("node", [cliPath, ...addArgs], { + stdio: "pipe", + encoding: "utf-8", + }); + + if (addResult.status !== 0) { + throw new Error( + `codex mcp add failed: ${addResult.stderr || addResult.stdout || "Unknown error"}` + ); + } + log.info(`✓ MCP server '${serverName}' configured`); + } +} diff --git a/agents/cursor.ts b/agents/cursor.ts index f2ed72c..ad027f1 100644 --- a/agents/cursor.ts +++ b/agents/cursor.ts @@ -1,9 +1,10 @@ import { spawn } from "node:child_process"; -import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; -import { join, resolve } from "node:path"; +import { mkdirSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import type { McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk"; import { log } from "../utils/cli.ts"; import { addInstructions } from "./instructions.ts"; -import { type AddMcpServerParams, agent, installFromCurl } from "./shared.ts"; +import { agent, installFromCurl } from "./shared.ts"; export const cursor = agent({ name: "cursor", @@ -14,88 +15,12 @@ export const cursor = agent({ executableName: "cursor-agent", }); }, - addMcpServer: ({ serverName, serverConfig, cliPath }: AddMcpServerParams) => { - const command = serverConfig.command; - const args = serverConfig.args || []; - const envVars = serverConfig.env || {}; - - // Resolve command to absolute path if it's a relative path - // For commands like "node", keep as-is; for file paths, resolve them - let resolvedCommand = command; - if (!command.includes("/") && !command.includes("\\")) { - // It's a command in PATH (like "node"), keep as-is - resolvedCommand = command; - } else { - // It's a file path, resolve to absolute path - resolvedCommand = resolve(command); - } - - // Resolve args to absolute paths if they look like file paths - const resolvedArgs = args.map((arg) => { - // If arg looks like a file path and is relative, resolve it - if ( - (arg.includes("/") || arg.includes("\\")) && - !arg.startsWith("/") && - !arg.match(/^[A-Z]:\\/) - ) { - return resolve(arg); - } - return arg; - }); - - // Build the server config with resolved paths - const resolvedServerConfig = { - command: resolvedCommand, - args: resolvedArgs, - env: envVars, - }; - - const tempDir = cliPath.split("/.local/bin/")[0]; - const cursorConfigDir = join(tempDir, ".cursor"); - const mcpConfigPath = join(cursorConfigDir, "mcp.json"); - - // Read existing config if it exists - let mcpConfig: { mcpServers: Record } = { mcpServers: {} }; - if (existsSync(mcpConfigPath)) { - try { - const existingConfig = readFileSync(mcpConfigPath, "utf-8"); - mcpConfig = JSON.parse(existingConfig); - if (!mcpConfig.mcpServers || typeof mcpConfig.mcpServers !== "object") { - mcpConfig.mcpServers = {}; - } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - throw new Error(`Failed to read existing MCP config at ${mcpConfigPath}: ${errorMessage}`); - } - } - - // Add the new server - mcpConfig.mcpServers[serverName] = resolvedServerConfig; - log.info(`Adding MCP server '${serverName}' to Cursor configuration...`); - - // Create .cursor directory if it doesn't exist - try { - mkdirSync(cursorConfigDir, { recursive: true }); - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - throw new Error( - `Failed to create Cursor config directory at ${cursorConfigDir}: ${errorMessage}` - ); - } - - // Write MCP configuration file - try { - writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2), "utf-8"); - log.info(`✓ MCP server '${serverName}' added to ${mcpConfigPath}`); - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - throw new Error(`Failed to write MCP config to ${mcpConfigPath}: ${errorMessage}`); - } - }, - run: async ({ prompt, apiKey, cliPath, githubInstallationToken }) => { + run: async ({ prompt, apiKey, cliPath, githubInstallationToken, mcpServers }) => { process.env.CURSOR_API_KEY = apiKey; process.env.GITHUB_INSTALLATION_TOKEN = githubInstallationToken; + configureCursorMcpServers({ mcpServers, cliPath }); + try { // Run cursor-agent in non-interactive mode with the prompt // Using -p flag for prompt, --output-format text for plain text output @@ -192,3 +117,21 @@ export const cursor = agent({ } }, }); + +/** + * Configure MCP servers for Cursor by writing to the Cursor configuration file. + * For cursor, we need to add the MCP servers to the Cursor configuration file manually as there is no CLI command to do this. + */ +function configureCursorMcpServers({ + mcpServers, + cliPath, +}: { + mcpServers: Record; + cliPath: string; +}) { + const tempDir = cliPath.split("/.local/bin/")[0]; + const cursorConfigDir = join(tempDir, ".cursor"); + const mcpConfigPath = join(cursorConfigDir, "mcp.json"); + mkdirSync(cursorConfigDir, { recursive: true }); + writeFileSync(mcpConfigPath, JSON.stringify({ mcpServers }, null, 2), "utf-8"); +} diff --git a/agents/gemini.ts b/agents/gemini.ts index 621ac2c..ba4ea1c 100644 --- a/agents/gemini.ts +++ b/agents/gemini.ts @@ -1,5 +1,5 @@ import { spawnSync } from "node:child_process"; -import type { McpServerConfig } from "@anthropic-ai/claude-agent-sdk"; +import type { McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk"; import { log } from "../utils/cli.ts"; import { spawn } from "../utils/subprocess.ts"; import { addInstructions } from "./instructions.ts"; @@ -16,33 +16,8 @@ export const gemini = agent({ installDependencies: true, }); }, - addMcpServer: ({ serverName, serverConfig, cliPath }) => { - // Gemini CLI syntax: gemini mcp add [args...] --env KEY=value - const command = serverConfig.command; - const args = serverConfig.args || []; - const envVars = serverConfig.env || {}; - - const addArgs = ["mcp", "add", serverName, command, ...args]; - - // Add environment variables as --env flags - for (const [key, value] of Object.entries(envVars)) { - addArgs.push("--env", `${key}=${value}`); - } - - log.info(`Adding MCP server '${serverName}'...`); - const addResult = spawnSync("node", [cliPath, ...addArgs], { - stdio: "pipe", - encoding: "utf-8", - }); - - if (addResult.status !== 0) { - throw new Error( - `gemini mcp add failed: ${addResult.stderr || addResult.stdout || "Unknown error"}` - ); - } - log.info(`✓ MCP server '${serverName}' configured`); - }, run: async ({ prompt, apiKey, mcpServers, githubInstallationToken, cliPath }) => { + configureGeminiMcpServers({ mcpServers, cliPath }); if (!apiKey) { throw new Error("google_api_key or gemini_api_key is required for gemini agent"); } @@ -108,3 +83,40 @@ export const gemini = agent({ }, }); +/** + * Configure MCP servers for Gemini using the CLI. + * Gemini CLI syntax: gemini mcp add [args...] --env KEY=value + */ +function configureGeminiMcpServers({ + mcpServers, + cliPath, +}: { + mcpServers: Record; + cliPath: string; +}): void { + for (const [serverName, serverConfig] of Object.entries(mcpServers)) { + const command = serverConfig.command; + const args = serverConfig.args || []; + const envVars = serverConfig.env || {}; + + const addArgs = ["mcp", "add", serverName, command, ...args]; + + // Add environment variables as --env flags + for (const [key, value] of Object.entries(envVars)) { + addArgs.push("--env", `${key}=${value}`); + } + + log.info(`Adding MCP server '${serverName}'...`); + const addResult = spawnSync("node", [cliPath, ...addArgs], { + stdio: "pipe", + encoding: "utf-8", + }); + + if (addResult.status !== 0) { + throw new Error( + `gemini mcp add failed: ${addResult.stderr || addResult.stdout || "Unknown error"}` + ); + } + log.info(`✓ MCP server '${serverName}' configured`); + } +} diff --git a/agents/shared.ts b/agents/shared.ts index ffc0a6b..ac7c81e 100644 --- a/agents/shared.ts +++ b/agents/shared.ts @@ -4,7 +4,7 @@ import { mkdtemp } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { pipeline } from "node:stream/promises"; -import type { McpServerConfig } from "@anthropic-ai/claude-agent-sdk"; +import type { McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk"; import { log } from "../utils/cli.ts"; /** @@ -24,7 +24,7 @@ export interface AgentConfig { apiKey: string; githubInstallationToken: string; prompt: string; - mcpServers: Record; + mcpServers: Record; cliPath: string; } @@ -223,19 +223,9 @@ export const agent = (agent: agent): agent => { return agent; }; -/** - * Parameters for adding an MCP server to an agent - */ -export interface AddMcpServerParams { - serverName: string; - serverConfig: Extract; - cliPath: string; -} - export type Agent = { name: string; inputKeys: string[]; install: () => Promise; - addMcpServer: (params: AddMcpServerParams) => void; run: (config: AgentConfig) => Promise; }; diff --git a/main.ts b/main.ts index ba761a6..dd09ff4 100644 --- a/main.ts +++ b/main.ts @@ -1,7 +1,7 @@ import { flatMorph } from "@ark/util"; import { type } from "arktype"; import { agents } from "./agents/index.ts"; -import { createMcpConfigs, forEachStdioMcpServer } from "./mcp/config.ts"; +import { createMcpConfigs } from "./mcp/config.ts"; import packageJson from "./package.json" with { type: "json" }; import { fetchRepoSettings } from "./utils/api.ts"; import { log } from "./utils/cli.ts"; @@ -141,18 +141,6 @@ export async function main(inputs: Inputs): Promise { const apiKey = inputs[matchingInputKey]!; - // Configure MCP servers if provided (global config is fine - not part of repo) - if (mcpServers && Object.keys(mcpServers).length > 0) { - log.info(`Configuring MCP servers for ${agentName}...`); - forEachStdioMcpServer(mcpServers, (serverName, serverConfig) => { - agent.addMcpServer({ - serverName, - serverConfig, - cliPath, - }); - }); - } - const result = await agent.run({ prompt: inputs.prompt, mcpServers, diff --git a/mcp/config.ts b/mcp/config.ts index fde1572..469a7a3 100644 --- a/mcp/config.ts +++ b/mcp/config.ts @@ -2,7 +2,7 @@ * Simple MCP configuration helper for adding our minimal GitHub comment server */ -import type { McpServerConfig } from "@anthropic-ai/claude-agent-sdk"; +import type { McpServerConfig, McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk"; import { fromHere } from "@ark/fs"; import { log } from "../utils/cli.ts"; import { parseRepoContext } from "../utils/github.ts"; @@ -38,7 +38,7 @@ export function createMcpConfigs(githubInstallationToken: string): McpConfigs { */ export function forEachStdioMcpServer( mcpServers: Record, - handler: (serverName: string, serverConfig: Extract) => void + handler: (serverName: string, serverConfig: McpStdioServerConfig) => void ): void { for (const [serverName, serverConfig] of Object.entries(mcpServers)) { // Only configure stdio servers (CLIs support stdio MCP servers) @@ -46,7 +46,6 @@ export function forEachStdioMcpServer( log.warning(`MCP server '${serverName}' is not a stdio server, skipping...`); continue; } - handler(serverName, serverConfig as Extract); + handler(serverName, serverConfig); } } -