Skip to main content
Glama
index.ts4.93 kB
#!/usr/bin/env node import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { Command, Option } from "commander"; import cors from "cors"; import express, { type Request, type Response } from "express"; import helmet from "helmet"; import { PvpcMcpServer } from "./mcp.js"; import { PvpcApiClient } from "./pvpc.js"; import { extractBearerToken, extractHeaderValue } from "./utils.js"; const program = new Command() .addOption( new Option("--transport <type>", "transport type") .choices(["stdio", "http"]) .default("stdio"), ) .addOption( new Option("--port <number>", "port for HTTP transport") .implies({ transport: "http" }) .env("PORT") .default("8080"), ) .addOption( new Option("--api-key <key>", "ESIOS API key for authentication") .implies({ transport: "stdio" }) .conflicts("port"), ) .allowUnknownOption() // let MCP Inspector / other wrappers pass through extra flags .parse(process.argv); const options = program.opts<{ transport: "stdio" | "http"; port: string; apiKey?: string; [option: string]: unknown; }>(); async function main() { if (options.transport === "http") { const defaultPort = 8080; const port = parseInt(options.port, 10); await runHttpServer(isNaN(port) ? defaultPort : port); } else { await runStdioServer(options.apiKey); } } main().catch((error) => { console.error("Server error:", error); process.exit(1); }); process.on("unhandledRejection", (reason, promise) => { console.error("Unhandled Rejection at:", promise, "reason:", reason); process.exit(1); }); process.on("uncaughtException", (error) => { console.error("Uncaught Exception:", error); process.exit(1); }); async function runHttpServer(port: number) { const app = express(); app.use(express.json()); app.use(helmet()); app.use( cors({ origin: "*", methods: ["GET", "POST", "DELETE"], exposedHeaders: ["Mcp-Session-Id"], allowedHeaders: [ "Content-Type", "mcp-session-id", "mcp-protocol-version", ], }), ); app.get("/ping", (_req: Request, res: Response) => { res.status(200).send("pong"); }); app.post("/mcp", async (req: Request, res: Response) => { // Check headers in order of preference const apiKey = extractBearerToken(req.headers.authorization) || extractHeaderValue(req.headers["Esios-API-Key"]) || extractHeaderValue(req.headers["X-API-Key"]) || extractHeaderValue(req.headers["esios-api-key"]) || extractHeaderValue(req.headers["x-api-key"]) || extractHeaderValue(req.headers["Esios_API_Key"]) || extractHeaderValue(req.headers["X_API_Key"]) || extractHeaderValue(req.headers["esios_api_key"]) || extractHeaderValue(req.headers["x_api_key"]); const apiClient = new PvpcApiClient(apiKey); const mcpServer = new PvpcMcpServer(apiClient); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); try { res.on("close", async () => { await transport.close(); await mcpServer.stop(); }); await mcpServer.start(transport); await transport.handleRequest(req, res, req.body); } catch (error) { console.error("Error handling MCP request:", error); if (!res.headersSent) { res.status(500).json({ jsonrpc: "2.0", id: null, error: { code: -32603, message: "Internal server error.", }, }); } } }); app.get("/mcp", (_req: Request, res: Response) => { res.status(405).json({ jsonrpc: "2.0", id: null, error: { code: -32000, message: "Method not allowed.", }, }); }); app.delete("/mcp", (_req: Request, res: Response) => { res.status(405).json({ jsonrpc: "2.0", id: null, error: { code: -32000, message: "Method not allowed.", }, }); }); const httpServer = app.listen(port, (error) => { if (error) { console.error("Error starting HTTP server:", error); process.exit(1); } console.log(`Server is running on http://localhost:${port}/mcp`); }); process.on("SIGINT", () => { httpServer.close(); process.exit(0); }); process.on("SIGTERM", () => { httpServer.close((err) => { if (err) { console.error("Error closing HTTP server:", err); process.exit(1); } process.exit(0); }); }); } async function runStdioServer(apiKey?: string) { const apiClient = new PvpcApiClient(apiKey); const mcpServer = new PvpcMcpServer(apiClient); const transport = new StdioServerTransport(); await mcpServer.start(transport); console.error("Server running on stdio"); process.on("SIGINT", async () => { await mcpServer.stop(); process.exit(0); }); process.on("SIGTERM", async () => { try { await mcpServer.stop(); } catch (error) { console.error("Error stopping server:", error); process.exit(1); } process.exit(0); }); }

Latest Blog Posts

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/rfdez/pvpc-mcp-server'

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