Skip to main content
Glama
sse.ts6.55 kB
#!/usr/bin/env node /* SSE entrypoint (HTTP-based MCP transport) This file scaffolds an HTTP server and attempts to dynamically load the SSE transport from the MCP SDK. It prints a clear error if the runtime transport wiring needs adjustment for your SDK version. */ import http from "node:http"; import { randomUUID } from "node:crypto"; import { createMcpServer } from "./mcp-server.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; const PORT = Number(process.env.PORT || 3000); const server = createMcpServer(); const stateless = process.env.MCP_STATELESS === "1" || process.env.MCP_STATEFUL === "0"; const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: stateless ? undefined : () => randomUUID(), enableJsonResponse: true }); await server.connect(transport as any); const httpServer = http.createServer(async (req, res) => { // Basic CORS (allow local testing and MCP clients running elsewhere) const origin = (req.headers.origin as string) || "*"; const allowOrigin = process.env.CORS_ORIGIN || origin || "*"; res.setHeader("Access-Control-Allow-Origin", allowOrigin); res.setHeader("Vary", "Origin"); res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); // Echo requested headers if provided; include common defaults const acrh = (req.headers["access-control-request-headers"] as string) || "accept, content-type, authorization"; res.setHeader("Access-Control-Allow-Headers", acrh); // Support Private Network Access preflights from Chromium/Electron const acpnh = req.headers["access-control-request-private-network"]; if (String(acpnh).toLowerCase() === "true") { res.setHeader("Access-Control-Allow-Private-Network", "true"); } if (req.method === "OPTIONS") { res.statusCode = 204; res.end(); return; } const url = new URL(req.url || "/", `http://${req.headers.host}`); const path = url.pathname; // Light request log for debugging connector setup try { const a = String(req.headers["accept"] || ""); const ct = String(req.headers["content-type"] || ""); console.log(`${new Date().toISOString()} ${req.method} ${path} Accept=${a} CT=${ct}`); } catch {} const _start = Date.now(); res.on("finish", () => { try { console.log(`${new Date().toISOString()} ${req.method} ${path} -> ${res.statusCode} in ${Date.now() - _start}ms`); } catch {} }); // MCP Streamable HTTP single endpoint if (path === "/mcp") { // Normalize headers expected by Streamable HTTP transport to reduce client friction const origAccept = String(req.headers["accept"] || ""); const parts = origAccept.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean); const hasJson = parts.some((p) => p.startsWith("application/json")); const hasSse = parts.some((p) => p.startsWith("text/event-stream")); const newParts = new Set(parts); if (!hasJson) newParts.add("application/json"); if (!hasSse) newParts.add("text/event-stream"); req.headers["accept"] = Array.from(newParts).join(", "); if (!req.headers["content-type"]) { req.headers["content-type"] = "application/json"; } // Patch initialize requests missing required fields (protocolVersion/capabilities) const DEFAULT_PROTOCOL = process.env.MCP_PROTOCOL_VERSION || "2025-03-26"; let parsedBody: any | undefined = undefined; if (req.method === "POST") { try { const chunks: Buffer[] = []; await new Promise<void>((resolve, reject) => { req.on("data", (c) => chunks.push(Buffer.from(c))); req.on("end", () => resolve()); req.on("error", (e) => reject(e)); }); const text = Buffer.concat(chunks).toString("utf8"); if (process.env.MCP_LOG_BODY === "1") { const preview = text.length > 4000 ? text.slice(0, 4000) + "…(truncated)" : text; console.log(`[MCP] POST /mcp raw body: ${preview}`); } const candidate = JSON.parse(text); if (candidate && !Array.isArray(candidate) && candidate.method === "initialize") { const params = (candidate.params = candidate.params || {}); if (!params.protocolVersion) params.protocolVersion = DEFAULT_PROTOCOL; if (!params.capabilities) params.capabilities = {}; if (!params.clientInfo) params.clientInfo = { name: "unknown", version: "0.0.0" }; if (!candidate.jsonrpc) candidate.jsonrpc = "2.0"; if (candidate.id === undefined || candidate.id === null) candidate.id = 1; if (process.env.MCP_LOG_BODY === "1") { console.log(`[MCP] initialize (patched) -> ${JSON.stringify(candidate)}`); } } parsedBody = candidate; } catch { parsedBody = undefined; } } try { await (transport as any).handleRequest(req as any, res as any, parsedBody); return; } catch (err) { res.statusCode = 500; res.end(`Streamable HTTP transport error: ${String(err)}`); return; } } // Health and info if (req.method === "GET" && path === "/.well-known/mcp.json") { res.setHeader("content-type", "application/json"); res.end( JSON.stringify({ name: "scryfall-mcp", version: "0.1.0", description: "MCP server for Scryfall + Commander Spellbook", transport: "streamable_http", endpoint: "/mcp" }) ); return; } if (req.method === "GET" && path === "/") { res.setHeader("content-type", "application/json"); res.end( JSON.stringify({ name: "scryfall-mcp", version: "0.1.0", transport: "streamable_http", endpoint: "/mcp" }) ); return; } res.statusCode = 404; res.end("Not Found"); }); httpServer.listen(PORT, () => { console.log(`MCP server (streamable_http) on http://localhost:${PORT} (endpoint: /mcp)`); });

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/latte-chan/scryfall-connector'

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