Skip to main content
Glama

docs-mcp-server

mcp.ts8.39 kB
/** * MCP command - Starts MCP server only. */ import type { Command } from "commander"; import { Option } from "commander"; import { startAppServer } from "../../app"; import { startStdioServer } from "../../mcp/startStdioServer"; import { initializeTools } from "../../mcp/tools"; import type { PipelineOptions } from "../../pipeline"; import { createDocumentManagement } from "../../store"; import type { IDocumentManagement } from "../../store/trpc/interfaces"; import { analytics, TelemetryEvent } from "../../telemetry"; import { DEFAULT_HOST, DEFAULT_HTTP_PORT, DEFAULT_PROTOCOL } from "../../utils/config"; import { LogLevel, logger, setLogLevel } from "../../utils/logger"; import { registerGlobalServices } from "../main"; import { createAppServerConfig, createPipelineWithCallbacks, parseAuthConfig, resolveEmbeddingContext, resolveProtocol, validateAuthConfig, validateHost, validatePort, } from "../utils"; export function createMcpCommand(program: Command): Command { return ( program .command("mcp") .description("Start MCP server only") .addOption( new Option("--protocol <protocol>", "Protocol for MCP server") .env("DOCS_MCP_PROTOCOL") .default(DEFAULT_PROTOCOL) .choices(["auto", "stdio", "http"]), ) .addOption( new Option("--port <number>", "Port for the MCP server") .env("DOCS_MCP_PORT") .env("PORT") .default(DEFAULT_HTTP_PORT.toString()) .argParser((v: string) => { const n = Number(v); if (!Number.isInteger(n) || n < 1 || n > 65535) { throw new Error("Port must be an integer between 1 and 65535"); } return String(n); }), ) .addOption( new Option("--host <host>", "Host to bind the MCP server to") .env("DOCS_MCP_HOST") .env("HOST") .default(DEFAULT_HOST) .argParser(validateHost), ) .addOption( new Option( "--embedding-model <model>", "Embedding model configuration (e.g., 'openai:text-embedding-3-small')", ).env("DOCS_MCP_EMBEDDING_MODEL"), ) .option( "--server-url <url>", "URL of external pipeline worker RPC (e.g., http://localhost:6280/api)", ) .option( "--read-only", "Run in read-only mode (only expose read tools, disable write/job tools)", false, ) // Auth options .addOption( new Option( "--auth-enabled", "Enable OAuth2/OIDC authentication for MCP endpoints", ) .env("DOCS_MCP_AUTH_ENABLED") .argParser((value) => { if (value === undefined) { return ( process.env.DOCS_MCP_AUTH_ENABLED === "true" || process.env.DOCS_MCP_AUTH_ENABLED === "1" ); } return value; }) .default(false), ) .addOption( new Option( "--auth-issuer-url <url>", "Issuer/discovery URL for OAuth2/OIDC provider", ).env("DOCS_MCP_AUTH_ISSUER_URL"), ) .addOption( new Option( "--auth-audience <id>", "JWT audience claim (identifies this protected resource)", ).env("DOCS_MCP_AUTH_AUDIENCE"), ) .action( async (cmdOptions: { protocol: string; port: string; host: string; embeddingModel?: string; serverUrl?: string; readOnly: boolean; authEnabled?: boolean; authIssuerUrl?: string; authAudience?: string; }) => { await analytics.track(TelemetryEvent.CLI_COMMAND, { command: "mcp", protocol: cmdOptions.protocol, port: cmdOptions.port, host: cmdOptions.host, useServerUrl: !!cmdOptions.serverUrl, readOnly: cmdOptions.readOnly, authEnabled: !!cmdOptions.authEnabled, }); const port = validatePort(cmdOptions.port); const host = validateHost(cmdOptions.host); const serverUrl = cmdOptions.serverUrl; // Resolve protocol using same logic as default action const resolvedProtocol = resolveProtocol(cmdOptions.protocol); if (resolvedProtocol === "stdio") { setLogLevel(LogLevel.ERROR); // Force quiet logging in stdio mode } // Parse and validate auth configuration const authConfig = parseAuthConfig({ authEnabled: cmdOptions.authEnabled, authIssuerUrl: cmdOptions.authIssuerUrl, authAudience: cmdOptions.authAudience, }); if (authConfig) { validateAuthConfig(authConfig); } // Get global options from root command (which has resolved storePath in preAction hook) const globalOptions = program.opts(); try { // Resolve embedding configuration for local execution const embeddingConfig = resolveEmbeddingContext(cmdOptions.embeddingModel); if (!serverUrl && !embeddingConfig) { logger.error( "❌ Embedding configuration is required for local mode. Configure an embedding provider with CLI options or environment variables.", ); process.exit(1); } const docService: IDocumentManagement = await createDocumentManagement({ serverUrl, embeddingConfig, storePath: globalOptions.storePath, }); const pipelineOptions: PipelineOptions = { recoverJobs: false, // MCP command doesn't support job recovery serverUrl, concurrency: 3, }; const pipeline = await createPipelineWithCallbacks( serverUrl ? undefined : (docService as unknown as never), pipelineOptions, ); if (resolvedProtocol === "stdio") { // Direct stdio mode - bypass AppServer entirely logger.debug(`Auto-detected stdio protocol (no TTY)`); logger.info("🚀 Starting MCP server (stdio mode)"); await pipeline.start(); // Start pipeline for stdio mode const mcpTools = await initializeTools(docService, pipeline); const mcpServer = await startStdioServer(mcpTools, cmdOptions.readOnly); // Register for graceful shutdown (stdio mode) registerGlobalServices({ mcpStdioServer: mcpServer, docService, pipeline, }); await new Promise(() => {}); // Keep running forever } else { // HTTP mode - use AppServer logger.debug(`Auto-detected http protocol (TTY available)`); logger.info("🚀 Starting MCP server (http mode)"); // Configure MCP-only server const config = createAppServerConfig({ enableWebInterface: false, // Never enable web interface in mcp command enableMcpServer: true, enableApiServer: false, // Never enable API in mcp command enableWorker: !serverUrl, port, host, externalWorkerUrl: serverUrl, readOnly: cmdOptions.readOnly, auth: authConfig, startupContext: { cliCommand: "mcp", mcpProtocol: "http", }, }); const appServer = await startAppServer(docService, pipeline, config); // Register for graceful shutdown (http mode) // Note: pipeline is managed by AppServer, so don't register it globally registerGlobalServices({ appServer, docService, // pipeline is owned by AppServer - don't register globally to avoid double shutdown }); await new Promise(() => {}); // Keep running forever } } catch (error) { logger.error(`❌ Failed to start MCP server: ${error}`); process.exit(1); } }, ) ); }

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/arabold/docs-mcp-server'

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