UNPKG

convex

Version:

Client for the Convex Cloud

150 lines (149 loc) 5.05 kB
"use strict"; import { Command } from "@commander-js/extra-typings"; import { oneoffContext } from "../bundler/context.js"; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { actionDescription } from "./lib/command.js"; import { checkAuthorization } from "./lib/login.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { RequestContext, RequestCrash } from "./lib/mcp/requestContext.js"; import { mcpTool, convexTools } from "./lib/mcp/tools/index.js"; import { Mutex } from "./lib/utils/mutex.js"; import { initializeBigBrainAuth } from "./lib/deploymentSelection.js"; const allToolNames = convexTools.map((t) => t.name).sort(); export const mcp = new Command("mcp").summary("Manage the Model Context Protocol server for Convex [BETA]").description( "Commands to initialize and run a Model Context Protocol server for Convex that can be used with AI tools.\nThis server exposes your Convex codebase to AI tools in a structured way." ).allowExcessArguments(false); mcp.command("start").summary("Start the MCP server").description( "Start the Model Context Protocol server for Convex that can be used with AI tools." ).option( "--project-dir <project-dir>", "Run the MCP server for a single project. By default, the MCP server can run for multiple projects, and each tool call specifies its project directory." ).option( "--disable-tools <tool-names>", `Comma separated list of tool names to disable (options: ${allToolNames.join(", ")})` ).option( "--disable-production-deployments", "Disable the MCP server from accessing production deployments." ).addDeploymentSelectionOptions(actionDescription("Run the MCP server on")).action(async (options) => { const ctx = await oneoffContext(options); try { const server = makeServer(options); const transport = new StdioServerTransport(); await server.connect(transport); await new Promise(() => { }); } catch (error) { await ctx.crash({ exitCode: 1, errorType: "fatal", errForSentry: `Failed to start MCP server: ${error}`, printedMessage: `Failed to start MCP server: ${error}` }); } }); function makeServer(options) { const disabledToolNames = /* @__PURE__ */ new Set(); for (const toolName of options.disableTools?.split(",") ?? []) { const name = toolName.trim(); if (!allToolNames.includes(name)) { throw new Error( `Disabled tool ${name} not found (valid tools: ${allToolNames.join(", ")})` ); } disabledToolNames.add(name); } const enabledToolsByName = {}; for (const tool of convexTools) { if (!disabledToolNames.has(tool.name)) { enabledToolsByName[tool.name] = tool; } } const mutex = new Mutex(); const server = new Server( { name: "Convex MCP Server", version: "0.0.1" }, { capabilities: { tools: {} } } ); server.setRequestHandler( CallToolRequestSchema, async (request) => { const ctx = new RequestContext(options); await initializeBigBrainAuth(ctx, options); try { const authorized = await checkAuthorization(ctx, false); if (!authorized) { await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: "Not Authorized: Run `npx convex dev` to login to your Convex project." }); } if (!request.params.arguments) { await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: "No arguments provided" }); } const convexTool = enabledToolsByName[request.params.name]; if (!convexTool) { await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `Tool ${request.params.name} not found` }); } const input = convexTool.inputSchema.parse(request.params.arguments); const result = await mutex.runExclusive(async () => { return await convexTool.handler(ctx, input); }); return { content: [ { type: "text", text: JSON.stringify(result) } ] }; } catch (error) { let message; if (error instanceof RequestCrash) { message = error.printedMessage; } else if (error instanceof Error) { message = error.message; } else { message = String(error); } return { content: [ { type: "text", text: JSON.stringify({ error: message }) } ], isError: true }; } } ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: Object.values(enabledToolsByName).map(mcpTool) }; }); return server; } //# sourceMappingURL=mcp.js.map