/** * Acai Code MCP Server - Stdio Entry Point * * Used when Claude Code launches the MCP server directly via .mcp.json. * Reads credentials from the local Python server on each tool call. */ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { createMcpServer } from "./server.js"; import { registerPrompts } from "./prompts/index.js"; import { registerTools } from "./tools/index.js"; import { registerResources } from "./resources/index.js"; import { sessionCredentials } from "./auth/credentials.js"; import { fetchProjectInfo } from "./auth/localClient.js"; // Create server instance const server = createMcpServer(); registerPrompts(server); registerTools(server); registerResources(server); const projectDir = process.env.ACAI_PROJECT_DIR || ""; // Aplica vars de override de entorno (usado por cronjobs para forzar el // entorno objetivo sin tocar el .acai del proyecto). Modifica creds in-place. function applyEnvironmentOverride(creds) { const modeOverride = process.env.ACAI_MODE_OVERRIDE; if (!modeOverride) return creds; creds.mode = modeOverride; if (process.env.ACAI_WEB_URL_OVERRIDE) creds.web_url = process.env.ACAI_WEB_URL_OVERRIDE; if (process.env.ACAI_API_WEB_URL_OVERRIDE) creds.api_web_url = process.env.ACAI_API_WEB_URL_OVERRIDE; if (process.env.ACAI_FORGE_HOST_OVERRIDE !== undefined) { creds.forge_host = process.env.ACAI_FORGE_HOST_OVERRIDE; } return creds; } async function readFreshCredentials() { if (projectDir) { try { const data = await fetchProjectInfo({ project_dir: projectDir }); if (data?.success) { return applyEnvironmentOverride({ token: data.token || "", tokenHash: data.tokenHash || "", website: data.domain || "", web_url: data.web_url || "", api_web_url: data.api_web_url || data.web_url || "", forge_host: data.forge_host || "", mode: data.mode || "local", profileName: "stdio", role: "developer", }); } } catch (error) { console.error(`[MCP stdio] Failed to resolve project-info: ${error.message}`); } } return applyEnvironmentOverride({ token: process.env.ACAI_TOKEN || "", tokenHash: process.env.ACAI_TOKEN_HASH || "", website: process.env.ACAI_WEBSITE || "", web_url: process.env.ACAI_WEB_URL || "", api_web_url: process.env.ACAI_API_WEB_URL || "", forge_host: process.env.ACAI_FORGE_HOST || "", mode: process.env.ACAI_MODE || "local", profileName: "stdio-fallback", role: "developer", }); } const initialCreds = await readFreshCredentials(); if (!initialCreds.web_url) { console.error("[MCP stdio] WARNING: No ACAI_WEB_URL in environment. Tools will fail."); } // Set initial credentials sessionCredentials.set("_default", initialCreds); // Intercept tool calls to refresh credentials from the Python server before each call const _origSetHandler = server.server.setRequestHandler; server.server.setRequestHandler = (schema, handler) => { return _origSetHandler.call(server.server, schema, async (request, extra) => { const freshCreds = await readFreshCredentials(); sessionCredentials.set("_default", freshCreds); if (extra?.sessionId) { sessionCredentials.set(extra.sessionId, freshCreds); } return handler(request, extra); }); }; // Connect via stdio transport const transport = new StdioServerTransport(); await server.connect(transport); console.error(`[MCP stdio] Connected — ${initialCreds.website} → ${initialCreds.web_url} (project: ${projectDir})`);