@danainnovations/cortex-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../bin/cli.ts","../src/cli/setup.ts","../src/constants.ts","../src/config/storage.ts","../src/utils/platform.ts","../src/config/clients.ts","../src/utils/validation.ts","../src/cli/configure.ts","../src/proxy/stdio-server.ts","../src/proxy/http-client.ts","../src/cli/serve.ts","../src/cli/status.ts","../src/cli/reset.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { runSetup } from \"../src/cli/setup.js\";\nimport { runConfigure } from \"../src/cli/configure.js\";\nimport { runServe } from \"../src/cli/serve.js\";\nimport { runStatus } from \"../src/cli/status.js\";\nimport { runReset } from \"../src/cli/reset.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"cortex-mcp\")\n .description(\n \"Connect your AI tools to GitHub, Vercel, Supabase & Sonance Brand via Cortex\"\n )\n .version(\"1.0.0\");\n\nprogram\n .command(\"setup\")\n .description(\"Interactive setup wizard — configure API key, MCPs, and clients\")\n .action(async () => {\n try {\n await runSetup();\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ERR_USE_AFTER_CLOSE\") {\n // readline closed — user pressed Ctrl+C\n process.exit(0);\n }\n console.error(\"Setup failed:\", err);\n process.exit(1);\n }\n });\n\nprogram\n .command(\"configure\")\n .description(\"Non-interactive configuration for a specific client\")\n .requiredOption(\"--key <key>\", \"Cortex API key (ctx_...)\")\n .requiredOption(\n \"--client <client>\",\n \"Client to configure (claude-desktop, claude-code, stdio)\"\n )\n .option(\n \"--mcps <mcps>\",\n \"Comma-separated MCP names (default: all)\",\n )\n .action(async (options) => {\n await runConfigure(options);\n });\n\nprogram\n .command(\"serve\")\n .description(\n \"Start stdio MCP proxy server (for OpenClaw and other stdio clients)\"\n )\n .action(async () => {\n await runServe();\n });\n\nprogram\n .command(\"status\")\n .description(\"Show current Cortex MCP configuration\")\n .action(() => {\n runStatus();\n });\n\nprogram\n .command(\"reset\")\n .description(\"Remove all Cortex MCP entries from AI clients\")\n .action(() => {\n runReset();\n });\n\nprogram.parse();\n","import * as readline from \"node:readline\";\nimport { AVAILABLE_MCPS, DEFAULT_MCPS, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { createConfig, writeConfig } from \"../config/storage.js\";\nimport {\n configureClaudeCode,\n configureClaudeDesktop,\n detectClients,\n generateStdioSnippet,\n} from \"../config/clients.js\";\nimport { isValidApiKey, validateApiKeyRemote } from \"../utils/validation.js\";\nimport type { ClientType } from \"../config/types.js\";\n\nconst rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n});\n\nfunction ask(question: string): Promise<string> {\n return new Promise((resolve) => {\n rl.question(question, (answer) => resolve(answer.trim()));\n });\n}\n\nfunction log(msg: string): void {\n process.stderr.write(msg + \"\\n\");\n}\n\n/**\n * Interactive setup wizard. Guides the user through:\n * 1. Entering their API key\n * 2. Selecting MCPs\n * 3. Detecting and configuring AI clients\n */\nexport async function runSetup(): Promise<void> {\n log(\"\");\n log(\" Cortex MCP Setup\");\n log(\" Connect your AI tools to GitHub, Vercel, Supabase & Sonance Brand\");\n log(\"\");\n\n // Step 1: API key\n log(\" Step 1: API Key\");\n log(\" Get one at https://aidentity.app/settings/keys\");\n log(\"\");\n\n let apiKey = \"\";\n while (true) {\n apiKey = await ask(\" Enter your Cortex API key: \");\n\n if (!isValidApiKey(apiKey)) {\n log(\n \" That doesn't look like a Cortex API key. It should start with 'ctx_'.\"\n );\n continue;\n }\n\n log(\" Validating...\");\n const { valid, error } = await validateApiKeyRemote(\n DEFAULT_SERVER_URL,\n apiKey\n );\n if (!valid) {\n log(` Key validation failed: ${error}`);\n log(\" Please try again.\\n\");\n continue;\n }\n\n log(\" Key validated successfully.\\n\");\n break;\n }\n\n // Step 2: Select MCPs\n log(\" Step 2: Select MCPs\");\n log(\" Available MCPs (all enabled by default):\\n\");\n\n for (const mcp of AVAILABLE_MCPS) {\n log(` - ${mcp.displayName.padEnd(15)} ${mcp.description}`);\n }\n log(\"\");\n\n const mcpInput = await ask(\n ` MCPs to enable (comma-separated, or press Enter for all): `\n );\n\n let selectedMcps: string[];\n if (!mcpInput) {\n selectedMcps = [...DEFAULT_MCPS];\n log(\" All MCPs enabled.\\n\");\n } else {\n selectedMcps = mcpInput\n .split(\",\")\n .map((s) => s.trim().toLowerCase())\n .filter((s) =>\n AVAILABLE_MCPS.some(\n (m) => m.name === s || m.displayName.toLowerCase() === s\n )\n );\n\n // Map display names to internal names\n selectedMcps = selectedMcps.map((s) => {\n const found = AVAILABLE_MCPS.find(\n (m) => m.displayName.toLowerCase() === s\n );\n return found ? found.name : s;\n });\n\n if (selectedMcps.length === 0) {\n selectedMcps = [...DEFAULT_MCPS];\n log(\" No valid MCPs recognized. Enabling all.\\n\");\n } else {\n log(` Enabled: ${selectedMcps.join(\", \")}\\n`);\n }\n }\n\n // Step 3: Detect and configure clients\n log(\" Step 3: Configure AI Clients\\n\");\n\n const clients = detectClients();\n const configuredClients: ClientType[] = [];\n\n for (const client of clients) {\n if (!client.detected) {\n log(` ${client.name}: not detected, skipping`);\n continue;\n }\n\n const answer = await ask(` Configure ${client.name}? (Y/n): `);\n if (answer.toLowerCase() === \"n\") {\n log(` Skipping ${client.name}`);\n continue;\n }\n\n try {\n if (client.type === \"claude-desktop\") {\n configureClaudeDesktop(DEFAULT_SERVER_URL, apiKey, selectedMcps);\n log(` ${client.name}: configured`);\n configuredClients.push(client.type);\n } else if (client.type === \"claude-code\") {\n configureClaudeCode(DEFAULT_SERVER_URL, apiKey, selectedMcps);\n log(` ${client.name}: configured`);\n configuredClients.push(client.type);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n log(` ${client.name}: failed — ${msg}`);\n }\n }\n\n // Ask about stdio\n log(\"\");\n const stdioAnswer = await ask(\n \" Do you also need a config snippet for OpenClaw or other stdio clients? (y/N): \"\n );\n if (stdioAnswer.toLowerCase() === \"y\") {\n log(\"\\n Add this to your client's MCP config:\\n\");\n log(generateStdioSnippet(apiKey));\n log(\"\");\n configuredClients.push(\"stdio\");\n }\n\n // Save config\n const config = createConfig(apiKey, selectedMcps);\n config.configuredClients = configuredClients;\n writeConfig(config);\n\n log(\"\");\n log(\" Setup complete! Restart your AI clients to see the new tools.\");\n log(` Config saved to ~/.cortex-mcp/config.json`);\n log(\"\");\n\n rl.close();\n}\n","/** Default Cortex server URL */\nexport const DEFAULT_SERVER_URL = \"https://cortex-bice.vercel.app\";\n\n/** MCP protocol version supported by Cortex */\nexport const PROTOCOL_VERSION = \"2024-11-05\";\n\n/** Available MCPs with their metadata */\nexport const AVAILABLE_MCPS = [\n {\n name: \"github\",\n displayName: \"GitHub\",\n description: \"Repos, PRs, issues, branches, code review (30 tools)\",\n serverName: \"cortex-github\",\n },\n {\n name: \"vercel\",\n displayName: \"Vercel\",\n description: \"Deployments, projects, env vars (15 tools)\",\n serverName: \"cortex-vercel\",\n },\n {\n name: \"supabase\",\n displayName: \"Supabase\",\n description: \"Database, migrations, edge functions (20+ tools)\",\n serverName: \"cortex-supabase\",\n },\n {\n name: \"sonance_brand\",\n displayName: \"Sonance Brand\",\n description: \"Brand design system, product data\",\n serverName: \"cortex-sonance-brand\",\n },\n] as const;\n\n/** All available MCP names */\nexport const MCP_NAMES: string[] = AVAILABLE_MCPS.map((m) => m.name);\n\n/** Default MCPs enabled on fresh setup */\nexport const DEFAULT_MCPS: string[] = [...MCP_NAMES];\n\n/** Config directory name */\nexport const CONFIG_DIR_NAME = \".cortex-mcp\";\n\n/** Config file name */\nexport const CONFIG_FILE_NAME = \"config.json\";\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME, CONFIG_FILE_NAME, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { getHomeDir } from \"../utils/platform.js\";\nimport type { CortexMcpConfig } from \"./types.js\";\n\n/** Get the config directory path */\nexport function getConfigDir(): string {\n return join(getHomeDir(), CONFIG_DIR_NAME);\n}\n\n/** Get the config file path */\nexport function getConfigPath(): string {\n return join(getConfigDir(), CONFIG_FILE_NAME);\n}\n\n/** Read the current config, or return null if none exists */\nexport function readConfig(): CortexMcpConfig | null {\n const path = getConfigPath();\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n return JSON.parse(raw) as CortexMcpConfig;\n } catch {\n return null;\n }\n}\n\n/** Write config to disk (creates directory with 700 permissions, file with 600) */\nexport function writeConfig(config: CortexMcpConfig): void {\n const dir = getConfigDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const path = getConfigPath();\n writeFileSync(path, JSON.stringify(config, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Create a default config with the given API key and MCPs */\nexport function createConfig(\n apiKey: string,\n mcps: string[]\n): CortexMcpConfig {\n return {\n version: 1,\n server: DEFAULT_SERVER_URL,\n apiKey,\n mcps,\n configuredClients: [],\n };\n}\n","import { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Get the user's home directory */\nexport function getHomeDir(): string {\n return homedir();\n}\n\n/** Get the current platform */\nexport function getPlatform(): \"macos\" | \"windows\" | \"linux\" {\n const p = platform();\n if (p === \"darwin\") return \"macos\";\n if (p === \"win32\") return \"windows\";\n return \"linux\";\n}\n\n/**\n * Get the Claude Desktop config file path for the current platform.\n */\nexport function getClaudeDesktopConfigPath(): string {\n const home = getHomeDir();\n const p = getPlatform();\n\n switch (p) {\n case \"macos\":\n return join(\n home,\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\"\n );\n case \"windows\":\n return join(\n process.env.APPDATA || join(home, \"AppData\", \"Roaming\"),\n \"Claude\",\n \"claude_desktop_config.json\"\n );\n case \"linux\":\n return join(home, \".config\", \"Claude\", \"claude_desktop_config.json\");\n }\n}\n\n/**\n * Get the Cursor MCP config file path for the current platform.\n */\nexport function getCursorConfigPath(): string {\n const home = getHomeDir();\n return join(home, \".cursor\", \"mcp.json\");\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\nimport { execSync } from \"node:child_process\";\nimport { AVAILABLE_MCPS } from \"../constants.js\";\nimport {\n getClaudeDesktopConfigPath,\n getCursorConfigPath,\n} from \"../utils/platform.js\";\nimport type { ClientType, DetectedClient, HttpMcpEntry } from \"./types.js\";\n\n/**\n * Detect which AI clients are installed on this machine.\n */\nexport function detectClients(): DetectedClient[] {\n const clients: DetectedClient[] = [];\n\n // Claude Desktop\n const desktopPath = getClaudeDesktopConfigPath();\n const desktopDir = dirname(desktopPath);\n clients.push({\n type: \"claude-desktop\",\n name: \"Claude Desktop\",\n configPath: desktopPath,\n detected: existsSync(desktopDir),\n });\n\n // Claude Code\n let claudeCodeDetected = false;\n try {\n execSync(\"which claude\", { stdio: \"pipe\" });\n claudeCodeDetected = true;\n } catch {\n // claude CLI not in PATH\n }\n clients.push({\n type: \"claude-code\",\n name: \"Claude Code\",\n configPath: null,\n detected: claudeCodeDetected,\n });\n\n // Cursor\n const cursorPath = getCursorConfigPath();\n const cursorDir = dirname(cursorPath);\n clients.push({\n type: \"stdio\",\n name: \"Cursor\",\n configPath: cursorPath,\n detected: existsSync(cursorDir),\n });\n\n return clients;\n}\n\n/**\n * Build per-MCP HTTP entries for a given server URL and API key.\n */\nexport function buildHttpEntries(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): Record<string, HttpMcpEntry> {\n const entries: Record<string, HttpMcpEntry> = {};\n\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n entries[mcp.serverName] = {\n url: `${serverUrl}/mcp/${mcp.name}`,\n headers: { \"x-api-key\": apiKey },\n };\n }\n\n return entries;\n}\n\n/**\n * Configure Claude Desktop by merging MCP entries into its config file.\n * Preserves existing non-Cortex entries.\n */\nexport function configureClaudeDesktop(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getClaudeDesktopConfigPath();\n const dir = dirname(configPath);\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Read existing config or start fresh\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n // Ensure mcpServers key exists\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Remove existing cortex-* entries\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\")) {\n delete servers[key];\n }\n }\n\n // Add new entries\n const entries = buildHttpEntries(serverUrl, apiKey, mcps);\n Object.assign(servers, entries);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Configure Claude Code by running `claude mcp add` commands.\n */\nexport function configureClaudeCode(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n\n const url = `${serverUrl}/mcp/${mcp.name}`;\n\n // Remove existing entry first (ignore errors if it doesn't exist)\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n } catch {\n // Entry didn't exist — fine\n }\n\n execSync(\n `claude mcp add ${mcp.serverName} --transport http --url \"${url}\" --header \"x-api-key: ${apiKey}\"`,\n { stdio: \"pipe\" }\n );\n }\n}\n\n/**\n * Generate a stdio config snippet for clients that need it (OpenClaw, etc.)\n */\nexport function generateStdioSnippet(apiKey: string): string {\n const config = {\n mcpServers: {\n cortex: {\n command: \"npx\",\n args: [\"-y\", \"@danainnovations/cortex-mcp\", \"serve\"],\n env: { CORTEX_API_KEY: apiKey },\n },\n },\n };\n return JSON.stringify(config, null, 2);\n}\n\n/**\n * Remove all cortex-* MCP entries from Claude Desktop config.\n */\nexport function resetClaudeDesktop(): boolean {\n const configPath = getClaudeDesktopConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Claude Code.\n */\nexport function resetClaudeCode(): boolean {\n let removed = false;\n for (const mcp of AVAILABLE_MCPS) {\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n removed = true;\n } catch {\n // Entry didn't exist\n }\n }\n return removed;\n}\n\n/**\n * Configure a specific client type.\n */\nexport function configureClient(\n clientType: ClientType,\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): string {\n switch (clientType) {\n case \"claude-desktop\":\n configureClaudeDesktop(serverUrl, apiKey, mcps);\n return \"Claude Desktop configured\";\n case \"claude-code\":\n configureClaudeCode(serverUrl, apiKey, mcps);\n return \"Claude Code configured\";\n case \"stdio\":\n return (\n \"Add this to your client config:\\n\\n\" +\n generateStdioSnippet(apiKey)\n );\n }\n}\n","/**\n * Validate that a string looks like a Cortex API key.\n * Format: ctx_{id}_{secret}\n */\nexport function isValidApiKey(key: string): boolean {\n return /^ctx_[a-zA-Z0-9]+_[a-zA-Z0-9]+$/.test(key.trim());\n}\n\n/**\n * Validate an API key against the Cortex server by sending an initialize request.\n * Returns true if the server responds with 200, false otherwise.\n */\nexport async function validateApiKeyRemote(\n serverUrl: string,\n apiKey: string\n): Promise<{ valid: boolean; error?: string }> {\n try {\n const response = await fetch(`${serverUrl}/mcp/github`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n \"mcp-protocol-version\": \"2024-11-05\",\n },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n method: \"initialize\",\n params: {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: \"cortex-mcp-setup\", version: \"1.0.0\" },\n },\n id: \"validate\",\n }),\n });\n\n if (response.status === 401 || response.status === 403) {\n return { valid: false, error: \"Invalid or expired API key\" };\n }\n if (!response.ok) {\n return { valid: false, error: `Server returned ${response.status}` };\n }\n return { valid: true };\n } catch {\n return { valid: false, error: \"Could not reach the Cortex server\" };\n }\n}\n","import { DEFAULT_MCPS, DEFAULT_SERVER_URL, MCP_NAMES } from \"../constants.js\";\nimport { createConfig, writeConfig } from \"../config/storage.js\";\nimport { configureClient } from \"../config/clients.js\";\nimport { isValidApiKey } from \"../utils/validation.js\";\nimport type { ClientType } from \"../config/types.js\";\n\ninterface ConfigureOptions {\n key: string;\n mcps?: string;\n client: string;\n}\n\nconst VALID_CLIENTS: Record<string, ClientType> = {\n \"claude-desktop\": \"claude-desktop\",\n \"claude-code\": \"claude-code\",\n stdio: \"stdio\",\n};\n\n/**\n * Non-interactive configure command.\n * Writes config to a specific client.\n */\nexport async function runConfigure(options: ConfigureOptions): Promise<void> {\n const { key, client } = options;\n\n // Validate API key\n if (!isValidApiKey(key)) {\n console.error(\n \"Invalid API key format. Must start with 'ctx_'. Get one at https://aidentity.app/settings/keys\"\n );\n process.exit(1);\n }\n\n // Validate client\n const clientType = VALID_CLIENTS[client];\n if (!clientType) {\n console.error(\n `Unknown client: ${client}. Valid options: ${Object.keys(VALID_CLIENTS).join(\", \")}`\n );\n process.exit(1);\n }\n\n // Parse MCPs\n let selectedMcps: string[];\n if (options.mcps) {\n selectedMcps = options.mcps\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => MCP_NAMES.includes(s));\n\n if (selectedMcps.length === 0) {\n console.error(\n `No valid MCPs. Available: ${MCP_NAMES.join(\", \")}`\n );\n process.exit(1);\n }\n } else {\n selectedMcps = [...DEFAULT_MCPS];\n }\n\n // Configure\n try {\n const result = configureClient(\n clientType,\n DEFAULT_SERVER_URL,\n key,\n selectedMcps\n );\n console.log(result);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Failed to configure ${client}: ${msg}`);\n process.exit(1);\n }\n\n // Save config\n const config = createConfig(key, selectedMcps);\n config.configuredClients = [clientType];\n writeConfig(config);\n\n console.log(`Config saved to ~/.cortex-mcp/config.json`);\n}\n","import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { CortexHttpClient } from \"./http-client.js\";\n\ninterface StdioServerOptions {\n serverUrl: string;\n apiKey: string;\n /** Use \"cortex\" for composite endpoint, or a specific MCP name */\n endpoint?: string;\n}\n\n/**\n * Start a stdio MCP server that proxies all requests to the hosted Cortex server.\n * This is used by clients that only support stdio transport (e.g., OpenClaw).\n */\nexport async function startStdioServer(\n options: StdioServerOptions\n): Promise<void> {\n const { serverUrl, apiKey, endpoint = \"cortex\" } = options;\n\n const cortex = new CortexHttpClient(serverUrl, apiKey, endpoint);\n\n const server = new Server(\n { name: \"cortex-mcp\", version: \"1.0.0\" },\n { capabilities: { tools: { listChanged: false } } }\n );\n\n // Forward tools/list to Cortex\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n const response = await cortex.listTools();\n\n if (response.error) {\n throw new Error(response.error.message);\n }\n\n const result = response.result as { tools: unknown[] };\n return { tools: result.tools || [] };\n });\n\n // Forward tools/call to Cortex\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n const response = await cortex.callTool(name, (args ?? {}) as Record<string, unknown>);\n\n if (response.error) {\n return {\n content: [{ type: \"text\" as const, text: response.error.message }],\n isError: true,\n };\n }\n\n const result = response.result as {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n };\n\n return result;\n });\n\n // Initialize the Cortex session before accepting client connections\n await cortex.initialize();\n\n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","import { PROTOCOL_VERSION } from \"../constants.js\";\n\n/** JSON-RPC 2.0 request */\ninterface JsonRpcRequest {\n jsonrpc: \"2.0\";\n method: string;\n params?: Record<string, unknown>;\n id: string | number;\n}\n\n/** JSON-RPC 2.0 response */\ninterface JsonRpcResponse {\n jsonrpc: \"2.0\";\n result?: unknown;\n error?: { code: number; message: string };\n id: string | number | null;\n}\n\n/**\n * HTTP client that forwards JSON-RPC 2.0 requests to the Cortex server.\n */\nexport class CortexHttpClient {\n private sessionId: string | null = null;\n private requestId = 0;\n\n constructor(\n private serverUrl: string,\n private apiKey: string,\n private endpoint: string = \"cortex\"\n ) {}\n\n /** Send an initialize request to establish a session */\n async initialize(): Promise<JsonRpcResponse> {\n const response = await this.sendRequest(\"initialize\", {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: \"cortex-mcp-proxy\", version: \"1.0.0\" },\n });\n\n // Send initialized notification (no id = notification, no response expected)\n await this.sendNotification(\"notifications/initialized\", {});\n\n return response;\n }\n\n /** List available tools */\n async listTools(): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/list\", {});\n }\n\n /** Call a tool */\n async callTool(\n name: string,\n args: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/call\", { name, arguments: args });\n }\n\n /** Send a JSON-RPC request and return the response */\n private async sendRequest(\n method: string,\n params: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n const id = String(++this.requestId);\n const body: JsonRpcRequest = { jsonrpc: \"2.0\", method, params, id };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n // Capture session ID from initialize\n const newSessionId = response.headers.get(\"mcp-session-id\");\n if (newSessionId) {\n this.sessionId = newSessionId;\n }\n\n if (!response.ok) {\n const text = await response.text();\n return {\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: this.humanReadableError(response.status, text),\n },\n id,\n };\n }\n\n return (await response.json()) as JsonRpcResponse;\n }\n\n /** Send a JSON-RPC notification (no response expected) */\n private async sendNotification(\n method: string,\n params: Record<string, unknown>\n ): Promise<void> {\n const body = { jsonrpc: \"2.0\", method, params };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n\n try {\n await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n } catch {\n // Notifications are fire-and-forget\n }\n }\n\n /** Convert HTTP status codes to user-friendly messages */\n private humanReadableError(status: number, body: string): string {\n switch (status) {\n case 401:\n return \"Invalid API key. Check your key at https://aidentity.app/settings/keys\";\n case 403:\n return \"API key lacks MCP permissions. Create a new key with MCP access.\";\n case 404:\n return \"MCP endpoint not found. The server may be misconfigured.\";\n case 503:\n return \"MCP protocol is disabled on the server.\";\n default:\n return `Server error (${status}): ${body.slice(0, 200)}`;\n }\n }\n}\n","import { DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { startStdioServer } from \"../proxy/stdio-server.js\";\n\n/**\n * Start the stdio proxy server.\n * Reads API key from CORTEX_API_KEY env var or ~/.cortex-mcp/config.json.\n */\nexport async function runServe(): Promise<void> {\n // Resolve API key: env var first, then config file\n let apiKey = process.env.CORTEX_API_KEY;\n let serverUrl = DEFAULT_SERVER_URL;\n\n if (!apiKey) {\n const config = readConfig();\n if (config) {\n apiKey = config.apiKey;\n serverUrl = config.server || DEFAULT_SERVER_URL;\n }\n }\n\n if (!apiKey) {\n // Write to stderr so it doesn't interfere with stdio protocol on stdout\n process.stderr.write(\n \"No API key found. Set CORTEX_API_KEY env var or run: npx @danainnovations/cortex-mcp setup\\n\"\n );\n process.exit(1);\n }\n\n // Start the stdio server (this blocks until the client disconnects)\n await startStdioServer({ serverUrl, apiKey });\n}\n","import { AVAILABLE_MCPS } from \"../constants.js\";\nimport { getConfigPath, readConfig } from \"../config/storage.js\";\n\n/**\n * Show current Cortex MCP configuration.\n */\nexport function runStatus(): void {\n const config = readConfig();\n\n if (!config) {\n console.log(\"No configuration found. Run: npx @danainnovations/cortex-mcp setup\");\n return;\n }\n\n const maskedKey =\n config.apiKey.slice(0, 8) + \"****\" + config.apiKey.slice(-4);\n\n console.log(\"\");\n console.log(\" Cortex MCP Status\");\n console.log(\" -----------------\");\n console.log(` Config: ${getConfigPath()}`);\n console.log(` Server: ${config.server}`);\n console.log(` API Key: ${maskedKey}`);\n console.log(\"\");\n console.log(\" Enabled MCPs:\");\n\n for (const mcpName of config.mcps) {\n const mcp = AVAILABLE_MCPS.find((m) => m.name === mcpName);\n const display = mcp ? mcp.displayName : mcpName;\n console.log(` ${display.padEnd(15)} ${config.server}/mcp/${mcpName}`);\n }\n\n console.log(\"\");\n console.log(\" Configured Clients:\");\n\n if (config.configuredClients.length === 0) {\n console.log(\" (none)\");\n } else {\n for (const client of config.configuredClients) {\n console.log(` ${client}`);\n }\n }\n\n console.log(\"\");\n}\n","import { existsSync, unlinkSync, rmdirSync } from \"node:fs\";\nimport { resetClaudeDesktop, resetClaudeCode } from \"../config/clients.js\";\nimport { getConfigDir, getConfigPath } from \"../config/storage.js\";\n\n/**\n * Remove all Cortex MCP entries from configured clients and delete local config.\n */\nexport function runReset(): void {\n console.log(\"\");\n console.log(\" Resetting Cortex MCP configuration...\");\n\n // Reset Claude Desktop\n const desktopReset = resetClaudeDesktop();\n console.log(\n ` Claude Desktop: ${desktopReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Reset Claude Code\n const codeReset = resetClaudeCode();\n console.log(\n ` Claude Code: ${codeReset ? \"entries removed\" : \"no entries found\"}`\n );\n\n // Delete local config\n const configPath = getConfigPath();\n if (existsSync(configPath)) {\n unlinkSync(configPath);\n console.log(` Config file: removed`);\n }\n\n const configDir = getConfigDir();\n try {\n rmdirSync(configDir);\n } catch {\n // Directory not empty or doesn't exist — fine\n }\n\n console.log(\"\");\n console.log(\" Done. Restart your AI clients to apply changes.\");\n console.log(\"\");\n}\n"],"mappings":"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","names":["join","join","existsSync","readFileSync","writeFileSync","mkdirSync","existsSync","mkdirSync","readFileSync","writeFileSync","existsSync","existsSync"]}
@@ -0,0 +1,135 @@
1
+ /** JSON-RPC 2.0 response */
2
+ interface JsonRpcResponse {
3
+ jsonrpc: "2.0";
4
+ result?: unknown;
5
+ error?: {
6
+ code: number;
7
+ message: string;
8
+ };
9
+ id: string | number | null;
10
+ }
11
+ /**
12
+ * HTTP client that forwards JSON-RPC 2.0 requests to the Cortex server.
13
+ */
14
+ declare class CortexHttpClient {
15
+ private serverUrl;
16
+ private apiKey;
17
+ private endpoint;
18
+ private sessionId;
19
+ private requestId;
20
+ constructor(serverUrl: string, apiKey: string, endpoint?: string);
21
+ /** Send an initialize request to establish a session */
22
+ initialize(): Promise<JsonRpcResponse>;
23
+ /** List available tools */
24
+ listTools(): Promise<JsonRpcResponse>;
25
+ /** Call a tool */
26
+ callTool(name: string, args: Record<string, unknown>): Promise<JsonRpcResponse>;
27
+ /** Send a JSON-RPC request and return the response */
28
+ private sendRequest;
29
+ /** Send a JSON-RPC notification (no response expected) */
30
+ private sendNotification;
31
+ /** Convert HTTP status codes to user-friendly messages */
32
+ private humanReadableError;
33
+ }
34
+
35
+ interface StdioServerOptions {
36
+ serverUrl: string;
37
+ apiKey: string;
38
+ /** Use "cortex" for composite endpoint, or a specific MCP name */
39
+ endpoint?: string;
40
+ }
41
+ /**
42
+ * Start a stdio MCP server that proxies all requests to the hosted Cortex server.
43
+ * This is used by clients that only support stdio transport (e.g., OpenClaw).
44
+ */
45
+ declare function startStdioServer(options: StdioServerOptions): Promise<void>;
46
+
47
+ /** Supported AI client types */
48
+ type ClientType = "claude-desktop" | "claude-code" | "stdio";
49
+ /** Stored configuration */
50
+ interface CortexMcpConfig {
51
+ version: number;
52
+ server: string;
53
+ apiKey: string;
54
+ mcps: string[];
55
+ configuredClients: ClientType[];
56
+ }
57
+ /** Result from detecting a client */
58
+ interface DetectedClient {
59
+ type: ClientType;
60
+ name: string;
61
+ configPath: string | null;
62
+ detected: boolean;
63
+ }
64
+
65
+ /** Read the current config, or return null if none exists */
66
+ declare function readConfig(): CortexMcpConfig | null;
67
+ /** Write config to disk (creates directory with 700 permissions, file with 600) */
68
+ declare function writeConfig(config: CortexMcpConfig): void;
69
+ /** Create a default config with the given API key and MCPs */
70
+ declare function createConfig(apiKey: string, mcps: string[]): CortexMcpConfig;
71
+
72
+ /**
73
+ * Detect which AI clients are installed on this machine.
74
+ */
75
+ declare function detectClients(): DetectedClient[];
76
+ /**
77
+ * Configure Claude Desktop by merging MCP entries into its config file.
78
+ * Preserves existing non-Cortex entries.
79
+ */
80
+ declare function configureClaudeDesktop(serverUrl: string, apiKey: string, mcps: string[]): void;
81
+ /**
82
+ * Configure Claude Code by running `claude mcp add` commands.
83
+ */
84
+ declare function configureClaudeCode(serverUrl: string, apiKey: string, mcps: string[]): void;
85
+ /**
86
+ * Generate a stdio config snippet for clients that need it (OpenClaw, etc.)
87
+ */
88
+ declare function generateStdioSnippet(apiKey: string): string;
89
+ /**
90
+ * Configure a specific client type.
91
+ */
92
+ declare function configureClient(clientType: ClientType, serverUrl: string, apiKey: string, mcps: string[]): string;
93
+
94
+ /**
95
+ * Validate that a string looks like a Cortex API key.
96
+ * Format: ctx_{id}_{secret}
97
+ */
98
+ declare function isValidApiKey(key: string): boolean;
99
+ /**
100
+ * Validate an API key against the Cortex server by sending an initialize request.
101
+ * Returns true if the server responds with 200, false otherwise.
102
+ */
103
+ declare function validateApiKeyRemote(serverUrl: string, apiKey: string): Promise<{
104
+ valid: boolean;
105
+ error?: string;
106
+ }>;
107
+
108
+ /** Default Cortex server URL */
109
+ declare const DEFAULT_SERVER_URL = "https://cortex-bice.vercel.app";
110
+ /** Available MCPs with their metadata */
111
+ declare const AVAILABLE_MCPS: readonly [{
112
+ readonly name: "github";
113
+ readonly displayName: "GitHub";
114
+ readonly description: "Repos, PRs, issues, branches, code review (30 tools)";
115
+ readonly serverName: "cortex-github";
116
+ }, {
117
+ readonly name: "vercel";
118
+ readonly displayName: "Vercel";
119
+ readonly description: "Deployments, projects, env vars (15 tools)";
120
+ readonly serverName: "cortex-vercel";
121
+ }, {
122
+ readonly name: "supabase";
123
+ readonly displayName: "Supabase";
124
+ readonly description: "Database, migrations, edge functions (20+ tools)";
125
+ readonly serverName: "cortex-supabase";
126
+ }, {
127
+ readonly name: "sonance_brand";
128
+ readonly displayName: "Sonance Brand";
129
+ readonly description: "Brand design system, product data";
130
+ readonly serverName: "cortex-sonance-brand";
131
+ }];
132
+ /** All available MCP names */
133
+ declare const MCP_NAMES: string[];
134
+
135
+ export { AVAILABLE_MCPS, type ClientType, CortexHttpClient, type CortexMcpConfig, DEFAULT_SERVER_URL, type DetectedClient, MCP_NAMES, configureClaudeCode, configureClaudeDesktop, configureClient, createConfig, detectClients, generateStdioSnippet, isValidApiKey, readConfig, startStdioServer, validateApiKeyRemote, writeConfig };
package/dist/index.js ADDED
@@ -0,0 +1,419 @@
1
+ // src/constants.ts
2
+ var DEFAULT_SERVER_URL = "https://cortex-bice.vercel.app";
3
+ var PROTOCOL_VERSION = "2024-11-05";
4
+ var AVAILABLE_MCPS = [
5
+ {
6
+ name: "github",
7
+ displayName: "GitHub",
8
+ description: "Repos, PRs, issues, branches, code review (30 tools)",
9
+ serverName: "cortex-github"
10
+ },
11
+ {
12
+ name: "vercel",
13
+ displayName: "Vercel",
14
+ description: "Deployments, projects, env vars (15 tools)",
15
+ serverName: "cortex-vercel"
16
+ },
17
+ {
18
+ name: "supabase",
19
+ displayName: "Supabase",
20
+ description: "Database, migrations, edge functions (20+ tools)",
21
+ serverName: "cortex-supabase"
22
+ },
23
+ {
24
+ name: "sonance_brand",
25
+ displayName: "Sonance Brand",
26
+ description: "Brand design system, product data",
27
+ serverName: "cortex-sonance-brand"
28
+ }
29
+ ];
30
+ var MCP_NAMES = AVAILABLE_MCPS.map((m) => m.name);
31
+ var DEFAULT_MCPS = [...MCP_NAMES];
32
+ var CONFIG_DIR_NAME = ".cortex-mcp";
33
+ var CONFIG_FILE_NAME = "config.json";
34
+
35
+ // src/proxy/http-client.ts
36
+ var CortexHttpClient = class {
37
+ constructor(serverUrl, apiKey, endpoint = "cortex") {
38
+ this.serverUrl = serverUrl;
39
+ this.apiKey = apiKey;
40
+ this.endpoint = endpoint;
41
+ }
42
+ sessionId = null;
43
+ requestId = 0;
44
+ /** Send an initialize request to establish a session */
45
+ async initialize() {
46
+ const response = await this.sendRequest("initialize", {
47
+ protocolVersion: PROTOCOL_VERSION,
48
+ capabilities: {},
49
+ clientInfo: { name: "cortex-mcp-proxy", version: "1.0.0" }
50
+ });
51
+ await this.sendNotification("notifications/initialized", {});
52
+ return response;
53
+ }
54
+ /** List available tools */
55
+ async listTools() {
56
+ return this.sendRequest("tools/list", {});
57
+ }
58
+ /** Call a tool */
59
+ async callTool(name, args) {
60
+ return this.sendRequest("tools/call", { name, arguments: args });
61
+ }
62
+ /** Send a JSON-RPC request and return the response */
63
+ async sendRequest(method, params) {
64
+ const id = String(++this.requestId);
65
+ const body = { jsonrpc: "2.0", method, params, id };
66
+ const headers = {
67
+ "Content-Type": "application/json",
68
+ "x-api-key": this.apiKey,
69
+ "mcp-protocol-version": PROTOCOL_VERSION
70
+ };
71
+ if (this.sessionId) {
72
+ headers["mcp-session-id"] = this.sessionId;
73
+ }
74
+ const url = `${this.serverUrl}/mcp/${this.endpoint}`;
75
+ const response = await fetch(url, {
76
+ method: "POST",
77
+ headers,
78
+ body: JSON.stringify(body)
79
+ });
80
+ const newSessionId = response.headers.get("mcp-session-id");
81
+ if (newSessionId) {
82
+ this.sessionId = newSessionId;
83
+ }
84
+ if (!response.ok) {
85
+ const text = await response.text();
86
+ return {
87
+ jsonrpc: "2.0",
88
+ error: {
89
+ code: -32e3,
90
+ message: this.humanReadableError(response.status, text)
91
+ },
92
+ id
93
+ };
94
+ }
95
+ return await response.json();
96
+ }
97
+ /** Send a JSON-RPC notification (no response expected) */
98
+ async sendNotification(method, params) {
99
+ const body = { jsonrpc: "2.0", method, params };
100
+ const headers = {
101
+ "Content-Type": "application/json",
102
+ "x-api-key": this.apiKey,
103
+ "mcp-protocol-version": PROTOCOL_VERSION
104
+ };
105
+ if (this.sessionId) {
106
+ headers["mcp-session-id"] = this.sessionId;
107
+ }
108
+ const url = `${this.serverUrl}/mcp/${this.endpoint}`;
109
+ try {
110
+ await fetch(url, {
111
+ method: "POST",
112
+ headers,
113
+ body: JSON.stringify(body)
114
+ });
115
+ } catch {
116
+ }
117
+ }
118
+ /** Convert HTTP status codes to user-friendly messages */
119
+ humanReadableError(status, body) {
120
+ switch (status) {
121
+ case 401:
122
+ return "Invalid API key. Check your key at https://aidentity.app/settings/keys";
123
+ case 403:
124
+ return "API key lacks MCP permissions. Create a new key with MCP access.";
125
+ case 404:
126
+ return "MCP endpoint not found. The server may be misconfigured.";
127
+ case 503:
128
+ return "MCP protocol is disabled on the server.";
129
+ default:
130
+ return `Server error (${status}): ${body.slice(0, 200)}`;
131
+ }
132
+ }
133
+ };
134
+
135
+ // src/proxy/stdio-server.ts
136
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
137
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
138
+ import {
139
+ CallToolRequestSchema,
140
+ ListToolsRequestSchema
141
+ } from "@modelcontextprotocol/sdk/types.js";
142
+ async function startStdioServer(options) {
143
+ const { serverUrl, apiKey, endpoint = "cortex" } = options;
144
+ const cortex = new CortexHttpClient(serverUrl, apiKey, endpoint);
145
+ const server = new Server(
146
+ { name: "cortex-mcp", version: "1.0.0" },
147
+ { capabilities: { tools: { listChanged: false } } }
148
+ );
149
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
150
+ const response = await cortex.listTools();
151
+ if (response.error) {
152
+ throw new Error(response.error.message);
153
+ }
154
+ const result = response.result;
155
+ return { tools: result.tools || [] };
156
+ });
157
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
158
+ const { name, arguments: args } = request.params;
159
+ const response = await cortex.callTool(name, args ?? {});
160
+ if (response.error) {
161
+ return {
162
+ content: [{ type: "text", text: response.error.message }],
163
+ isError: true
164
+ };
165
+ }
166
+ const result = response.result;
167
+ return result;
168
+ });
169
+ await cortex.initialize();
170
+ const transport = new StdioServerTransport();
171
+ await server.connect(transport);
172
+ }
173
+
174
+ // src/config/storage.ts
175
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
176
+ import { join as join2 } from "path";
177
+
178
+ // src/utils/platform.ts
179
+ import { homedir, platform } from "os";
180
+ import { join } from "path";
181
+ function getHomeDir() {
182
+ return homedir();
183
+ }
184
+ function getPlatform() {
185
+ const p = platform();
186
+ if (p === "darwin") return "macos";
187
+ if (p === "win32") return "windows";
188
+ return "linux";
189
+ }
190
+ function getClaudeDesktopConfigPath() {
191
+ const home = getHomeDir();
192
+ const p = getPlatform();
193
+ switch (p) {
194
+ case "macos":
195
+ return join(
196
+ home,
197
+ "Library",
198
+ "Application Support",
199
+ "Claude",
200
+ "claude_desktop_config.json"
201
+ );
202
+ case "windows":
203
+ return join(
204
+ process.env.APPDATA || join(home, "AppData", "Roaming"),
205
+ "Claude",
206
+ "claude_desktop_config.json"
207
+ );
208
+ case "linux":
209
+ return join(home, ".config", "Claude", "claude_desktop_config.json");
210
+ }
211
+ }
212
+ function getCursorConfigPath() {
213
+ const home = getHomeDir();
214
+ return join(home, ".cursor", "mcp.json");
215
+ }
216
+
217
+ // src/config/storage.ts
218
+ function getConfigDir() {
219
+ return join2(getHomeDir(), CONFIG_DIR_NAME);
220
+ }
221
+ function getConfigPath() {
222
+ return join2(getConfigDir(), CONFIG_FILE_NAME);
223
+ }
224
+ function readConfig() {
225
+ const path = getConfigPath();
226
+ if (!existsSync(path)) return null;
227
+ try {
228
+ const raw = readFileSync(path, "utf-8");
229
+ return JSON.parse(raw);
230
+ } catch {
231
+ return null;
232
+ }
233
+ }
234
+ function writeConfig(config) {
235
+ const dir = getConfigDir();
236
+ if (!existsSync(dir)) {
237
+ mkdirSync(dir, { recursive: true, mode: 448 });
238
+ }
239
+ const path = getConfigPath();
240
+ writeFileSync(path, JSON.stringify(config, null, 2) + "\n", {
241
+ mode: 384
242
+ });
243
+ }
244
+ function createConfig(apiKey, mcps) {
245
+ return {
246
+ version: 1,
247
+ server: DEFAULT_SERVER_URL,
248
+ apiKey,
249
+ mcps,
250
+ configuredClients: []
251
+ };
252
+ }
253
+
254
+ // src/config/clients.ts
255
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
256
+ import { dirname } from "path";
257
+ import { mkdirSync as mkdirSync2 } from "fs";
258
+ import { execSync } from "child_process";
259
+ function detectClients() {
260
+ const clients = [];
261
+ const desktopPath = getClaudeDesktopConfigPath();
262
+ const desktopDir = dirname(desktopPath);
263
+ clients.push({
264
+ type: "claude-desktop",
265
+ name: "Claude Desktop",
266
+ configPath: desktopPath,
267
+ detected: existsSync2(desktopDir)
268
+ });
269
+ let claudeCodeDetected = false;
270
+ try {
271
+ execSync("which claude", { stdio: "pipe" });
272
+ claudeCodeDetected = true;
273
+ } catch {
274
+ }
275
+ clients.push({
276
+ type: "claude-code",
277
+ name: "Claude Code",
278
+ configPath: null,
279
+ detected: claudeCodeDetected
280
+ });
281
+ const cursorPath = getCursorConfigPath();
282
+ const cursorDir = dirname(cursorPath);
283
+ clients.push({
284
+ type: "stdio",
285
+ name: "Cursor",
286
+ configPath: cursorPath,
287
+ detected: existsSync2(cursorDir)
288
+ });
289
+ return clients;
290
+ }
291
+ function buildHttpEntries(serverUrl, apiKey, mcps) {
292
+ const entries = {};
293
+ for (const mcp of AVAILABLE_MCPS) {
294
+ if (!mcps.includes(mcp.name)) continue;
295
+ entries[mcp.serverName] = {
296
+ url: `${serverUrl}/mcp/${mcp.name}`,
297
+ headers: { "x-api-key": apiKey }
298
+ };
299
+ }
300
+ return entries;
301
+ }
302
+ function configureClaudeDesktop(serverUrl, apiKey, mcps) {
303
+ const configPath = getClaudeDesktopConfigPath();
304
+ const dir = dirname(configPath);
305
+ if (!existsSync2(dir)) {
306
+ mkdirSync2(dir, { recursive: true });
307
+ }
308
+ let config = {};
309
+ if (existsSync2(configPath)) {
310
+ try {
311
+ config = JSON.parse(readFileSync2(configPath, "utf-8"));
312
+ } catch {
313
+ }
314
+ }
315
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
316
+ config.mcpServers = {};
317
+ }
318
+ const servers = config.mcpServers;
319
+ for (const key of Object.keys(servers)) {
320
+ if (key.startsWith("cortex-")) {
321
+ delete servers[key];
322
+ }
323
+ }
324
+ const entries = buildHttpEntries(serverUrl, apiKey, mcps);
325
+ Object.assign(servers, entries);
326
+ writeFileSync2(configPath, JSON.stringify(config, null, 2) + "\n");
327
+ }
328
+ function configureClaudeCode(serverUrl, apiKey, mcps) {
329
+ for (const mcp of AVAILABLE_MCPS) {
330
+ if (!mcps.includes(mcp.name)) continue;
331
+ const url = `${serverUrl}/mcp/${mcp.name}`;
332
+ try {
333
+ execSync(`claude mcp remove ${mcp.serverName}`, { stdio: "pipe" });
334
+ } catch {
335
+ }
336
+ execSync(
337
+ `claude mcp add ${mcp.serverName} --transport http --url "${url}" --header "x-api-key: ${apiKey}"`,
338
+ { stdio: "pipe" }
339
+ );
340
+ }
341
+ }
342
+ function generateStdioSnippet(apiKey) {
343
+ const config = {
344
+ mcpServers: {
345
+ cortex: {
346
+ command: "npx",
347
+ args: ["-y", "@danainnovations/cortex-mcp", "serve"],
348
+ env: { CORTEX_API_KEY: apiKey }
349
+ }
350
+ }
351
+ };
352
+ return JSON.stringify(config, null, 2);
353
+ }
354
+ function configureClient(clientType, serverUrl, apiKey, mcps) {
355
+ switch (clientType) {
356
+ case "claude-desktop":
357
+ configureClaudeDesktop(serverUrl, apiKey, mcps);
358
+ return "Claude Desktop configured";
359
+ case "claude-code":
360
+ configureClaudeCode(serverUrl, apiKey, mcps);
361
+ return "Claude Code configured";
362
+ case "stdio":
363
+ return "Add this to your client config:\n\n" + generateStdioSnippet(apiKey);
364
+ }
365
+ }
366
+
367
+ // src/utils/validation.ts
368
+ function isValidApiKey(key) {
369
+ return /^ctx_[a-zA-Z0-9]+_[a-zA-Z0-9]+$/.test(key.trim());
370
+ }
371
+ async function validateApiKeyRemote(serverUrl, apiKey) {
372
+ try {
373
+ const response = await fetch(`${serverUrl}/mcp/github`, {
374
+ method: "POST",
375
+ headers: {
376
+ "Content-Type": "application/json",
377
+ "x-api-key": apiKey,
378
+ "mcp-protocol-version": "2024-11-05"
379
+ },
380
+ body: JSON.stringify({
381
+ jsonrpc: "2.0",
382
+ method: "initialize",
383
+ params: {
384
+ protocolVersion: "2024-11-05",
385
+ capabilities: {},
386
+ clientInfo: { name: "cortex-mcp-setup", version: "1.0.0" }
387
+ },
388
+ id: "validate"
389
+ })
390
+ });
391
+ if (response.status === 401 || response.status === 403) {
392
+ return { valid: false, error: "Invalid or expired API key" };
393
+ }
394
+ if (!response.ok) {
395
+ return { valid: false, error: `Server returned ${response.status}` };
396
+ }
397
+ return { valid: true };
398
+ } catch {
399
+ return { valid: false, error: "Could not reach the Cortex server" };
400
+ }
401
+ }
402
+ export {
403
+ AVAILABLE_MCPS,
404
+ CortexHttpClient,
405
+ DEFAULT_SERVER_URL,
406
+ MCP_NAMES,
407
+ configureClaudeCode,
408
+ configureClaudeDesktop,
409
+ configureClient,
410
+ createConfig,
411
+ detectClients,
412
+ generateStdioSnippet,
413
+ isValidApiKey,
414
+ readConfig,
415
+ startStdioServer,
416
+ validateApiKeyRemote,
417
+ writeConfig
418
+ };
419
+ //# sourceMappingURL=index.js.map