@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
JavaScript
;
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;