Skip to main content
Glama

Convex MCP server

Official
by get-convex
mcp.ts5.71 kB
import { Command, Option } 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 { CallToolRequest, CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { McpOptions, RequestContext, RequestCrash, } from "./lib/mcp/requestContext.js"; import { mcpTool, convexTools, ConvexTool } 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.\n" + "This 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( "--dangerously-enable-production-deployments", "DANGEROUSLY allow the MCP server to access production deployments. Defaults to false.", false, ) // Deprecated option, we swapped the default. no-op. .addOption( new Option("--disable-production-deployments") .conflicts("--dangerously-enable-production-deployments") .hideHelp(), ) .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); // Keep the process running await new Promise(() => {}); } catch (error: any) { await ctx.crash({ exitCode: 1, errorType: "fatal", errForSentry: `Failed to start MCP server: ${error}`, printedMessage: `Failed to start MCP server: ${error}`, }); } }); function makeServer(options: McpOptions) { const disabledToolNames = new Set<string>(); for (const toolName of options.disableTools?.split(",") ?? []) { const name = toolName.trim(); if (!allToolNames.includes(name)) { // eslint-disable-next-line no-restricted-syntax throw new Error( `Disabled tool ${name} not found (valid tools: ${allToolNames.join(", ")})`, ); } disabledToolNames.add(name); } const enabledToolsByName: Record<string, ConvexTool<any, any>> = {}; 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: CallToolRequest) => { 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); // Serialize tool handlers since they're mutating the current working directory. const result = await mutex.runExclusive(async () => { return await convexTool.handler(ctx, input); }); return { content: [ { type: "text", text: JSON.stringify(result), }, ], }; } catch (error: any) { let message: string; 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; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server