UNPKG

mcp-proxy

Version:

A TypeScript SSE proxy for MCP servers that use stdio transport.

202 lines (200 loc) 5.87 kB
// src/proxyServer.ts import { CallToolRequestSchema, CompleteRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, LoggingMessageNotificationSchema, ReadResourceRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, ResourceUpdatedNotificationSchema } from "@modelcontextprotocol/sdk/types.js"; var proxyServer = async ({ server, client, serverCapabilities }) => { if (serverCapabilities?.logging) { server.setNotificationHandler( LoggingMessageNotificationSchema, async (args) => { return client.notification(args); } ); } if (serverCapabilities?.prompts) { server.setRequestHandler(GetPromptRequestSchema, async (args) => { return client.getPrompt(args.params); }); server.setRequestHandler(ListPromptsRequestSchema, async (args) => { return client.listPrompts(args.params); }); } if (serverCapabilities?.resources) { server.setRequestHandler(ListResourcesRequestSchema, async (args) => { return client.listResources(args.params); }); server.setRequestHandler( ListResourceTemplatesRequestSchema, async (args) => { return client.listResourceTemplates(args.params); } ); server.setRequestHandler(ReadResourceRequestSchema, async (args) => { return client.readResource(args.params); }); if (serverCapabilities?.resources.subscribe) { server.setNotificationHandler( ResourceUpdatedNotificationSchema, async (args) => { return client.notification(args); } ); server.setRequestHandler(SubscribeRequestSchema, async (args) => { return client.subscribeResource(args.params); }); server.setRequestHandler(UnsubscribeRequestSchema, async (args) => { return client.unsubscribeResource(args.params); }); } } if (serverCapabilities?.tools) { server.setRequestHandler(CallToolRequestSchema, async (args) => { return client.callTool(args.params); }); server.setRequestHandler(ListToolsRequestSchema, async (args) => { return client.listTools(args.params); }); } server.setRequestHandler(CompleteRequestSchema, async (args) => { return client.complete(args.params); }); }; // src/startSSEServer.ts import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import http from "http"; var startSSEServer = async ({ port, createServer, endpoint, onConnect, onClose, onUnhandledRequest }) => { const activeTransports = {}; const httpServer = http.createServer(async (req, res) => { if (req.headers.origin) { try { const origin = new URL(req.headers.origin); res.setHeader("Access-Control-Allow-Origin", origin.origin); res.setHeader("Access-Control-Allow-Credentials", "true"); res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); res.setHeader("Access-Control-Allow-Headers", "*"); } catch (error) { console.error("Error parsing origin:", error); } } if (req.method === "OPTIONS") { res.writeHead(204); res.end(); return; } if (req.method === "GET" && req.url === `/ping`) { res.writeHead(200).end("pong"); return; } if (req.method === "GET" && req.url === endpoint) { const transport = new SSEServerTransport("/messages", res); let server; try { server = await createServer(req); } catch (error) { if (error instanceof Response) { res.writeHead(error.status).end(error.statusText); return; } res.writeHead(500).end("Error creating server"); return; } activeTransports[transport.sessionId] = transport; let closed = false; res.on("close", async () => { closed = true; try { await server.close(); } catch (error) { console.error("Error closing server:", error); } delete activeTransports[transport.sessionId]; onClose?.(server); }); try { await server.connect(transport); await transport.send({ jsonrpc: "2.0", method: "sse/connection", params: { message: "SSE Connection established" } }); onConnect?.(server); } catch (error) { if (!closed) { console.error("Error connecting to server:", error); res.writeHead(500).end("Error connecting to server"); } } return; } if (req.method === "POST" && req.url?.startsWith("/messages")) { const sessionId = new URL( req.url, "https://example.com" ).searchParams.get("sessionId"); if (!sessionId) { res.writeHead(400).end("No sessionId"); return; } const activeTransport = activeTransports[sessionId]; if (!activeTransport) { res.writeHead(400).end("No active transport"); return; } await activeTransport.handlePostMessage(req, res); return; } if (onUnhandledRequest) { await onUnhandledRequest(req, res); } else { res.writeHead(404).end(); } }); await new Promise((resolve) => { httpServer.listen(port, "::", () => { resolve(void 0); }); }); return { close: async () => { for (const transport of Object.values(activeTransports)) { await transport.close(); } return new Promise((resolve, reject) => { httpServer.close((error) => { if (error) { reject(error); return; } resolve(); }); }); } }; }; export { proxyServer, startSSEServer }; //# sourceMappingURL=chunk-YBSC4ELC.js.map