UNPKG

@antv/mcp-server-chart

Version:

A Model Context Protocol server for generating charts using AntV. This is a TypeScript-based MCP server that provides chart generation capabilities. It allows you to create various types of charts through MCP tools.

179 lines (178 loc) 8.78 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.startHTTPStreamableServer = void 0; const node_crypto_1 = require("node:crypto"); const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js"); const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); const utils_1 = require("../utils/index.js"); const startHTTPStreamableServer = (createServer_1, ...args_1) => __awaiter(void 0, [createServer_1, ...args_1], void 0, function* (createServer, endpoint = "/mcp", port = 1122, eventStore = new utils_1.InMemoryEventStore()) { const activeTransports = {}; // Define the request handler for streamable-specific logic const handleRequest = (req, res) => __awaiter(void 0, void 0, void 0, function* () { var _a; if (!req.url) { res.writeHead(400).end("No URL"); return; } const reqUrl = new URL(req.url, "http://localhost"); // Handle POST requests to endpoint if (req.method === "POST" && reqUrl.pathname === endpoint) { try { const sessionId = Array.isArray(req.headers["mcp-session-id"]) ? req.headers["mcp-session-id"][0] : req.headers["mcp-session-id"]; let transport; let server; const body = yield (0, utils_1.getBody)(req); /** * diagram: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#sequence-diagram. */ // 1. If the sessionId is provided and the server is already created, use the existing transport and server. if (sessionId && activeTransports[sessionId]) { transport = activeTransports[sessionId].transport; server = activeTransports[sessionId].server; // 2. If the sessionId is not provided and the request is an initialize request, create a new transport for the session. } else if (!sessionId && (0, types_js_1.isInitializeRequest)(body)) { transport = new streamableHttp_js_1.StreamableHTTPServerTransport({ // use the event store to store the events to replay on reconnect. // more details: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#resumability-and-redelivery. eventStore: eventStore || new utils_1.InMemoryEventStore(), onsessioninitialized: (_sessionId) => { // add only when the id Sesison id is generated. activeTransports[_sessionId] = { server, transport, }; }, sessionIdGenerator: node_crypto_1.randomUUID, }); // Handle the server close event. transport.onclose = () => __awaiter(void 0, void 0, void 0, function* () { const sid = transport.sessionId; if (sid && activeTransports[sid]) { try { yield (server === null || server === void 0 ? void 0 : server.close()); } catch (error) { console.error("Error closing server:", error); } // delete used transport and server to avoid memory leak. delete activeTransports[sid]; } }); // Create the server try { server = createServer(); } catch (error) { if (error instanceof Response) { res.writeHead(error.status).end(error.statusText); return; } res.writeHead(500).end("Error creating server"); return; } server.connect(transport); yield transport.handleRequest(req, res, body); return; } else { // Error if the server is not created but the request is not an initialize request. res.setHeader("Content-Type", "application/json"); res.writeHead(400).end(JSON.stringify({ error: { code: -32000, message: "Bad Request: No valid session ID provided", }, id: null, jsonrpc: "2.0", })); return; } // Handle the request if the server is already created. yield transport.handleRequest(req, res, body); } catch (error) { console.error("Error handling request:", error); res.setHeader("Content-Type", "application/json"); res.writeHead(500).end(JSON.stringify({ error: { code: -32603, message: "Internal Server Error" }, id: null, jsonrpc: "2.0", })); } return; } // Handle GET requests to endpoint if (req.method === "GET" && reqUrl.pathname === endpoint) { const sessionId = req.headers["mcp-session-id"]; const activeTransport = sessionId ? activeTransports[sessionId] : undefined; if (!sessionId) { res.writeHead(400).end("No sessionId"); return; } if (!activeTransport) { res.writeHead(400).end("No active transport"); return; } const lastEventId = req.headers["last-event-id"]; if (lastEventId) { console.log(`Client reconnecting with Last-Event-ID: ${lastEventId}`); } else { console.log(`Establishing new SSE stream for session ${sessionId}`); } yield activeTransport.transport.handleRequest(req, res); return; } // Handle DELETE requests to endpoint if (req.method === "DELETE" && reqUrl.pathname === endpoint) { console.log("received delete request"); const sessionId = req.headers["mcp-session-id"]; if (!sessionId) { res.writeHead(400).end("Invalid or missing sessionId"); return; } console.log("received delete request for session", sessionId); const transport = (_a = activeTransports[sessionId]) === null || _a === void 0 ? void 0 : _a.transport; if (!transport) { res.writeHead(400).end("No active transport"); return; } try { yield transport.handleRequest(req, res); } catch (error) { console.error("Error handling delete request:", error); res.writeHead(500).end("Error handling delete request"); } return; } // If we reach here, no handler matched res.writeHead(404).end("Not found"); }); // Custom cleanup for streamable server const cleanup = () => { for (const { server, transport } of Object.values(activeTransports)) { transport.close(); server.close(); } }; // Create the HTTP server using our factory (0, utils_1.createBaseHttpServer)(port, endpoint, { handleRequest, cleanup, serverType: "HTTP Streamable Server", }); }); exports.startHTTPStreamableServer = startHTTPStreamableServer;