UNPKG

@rockship/apollo-io-mcp

Version:

A powerful Model Context Protocol (MCP) server implementation for seamless Apollo.io API integration, enabling AI assistants to interact with Apollo.io data

104 lines 4.22 kB
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; import express from "express"; import { randomUUID } from "node:crypto"; import { createMcpServer } from "../common/mcp-server.js"; import { getServerConfig, setupExpressApp, setupGracefulShutdown, setupHealthCheck, setupListenServer, } from "../common/server-utils.js"; let server; function createHttpStreamServer() { const app = express(); const transports = {}; setupExpressApp(app); setupHealthCheck(app, transports); // Handle POST requests for client-to-server communication app.post("/mcp", async (req, res) => { const apiKey = req.query.apiKey || process.env.APOLLO_IO_API_KEY; if (!apiKey) { res.status(400).json({ error: "API key is required" }); return; } // Check for existing session ID const sessionId = req.headers["mcp-session-id"]; let transport; if (sessionId && transports[sessionId]) { // Reuse existing transport transport = transports[sessionId]; } else if (!sessionId && isInitializeRequest(req.body)) { // New initialization request transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (sessionId) => { // Store the transport by session ID transports[sessionId] = transport; console.log(`Streamable connection established with sessionId: ${sessionId}`); }, // DNS rebinding protection is disabled by default for backwards compatibility. If you are running this server // locally, make sure to set: // enableDnsRebindingProtection: true, // allowedHosts: ['127.0.0.1'], }); // Clean up transport when closed transport.onclose = () => { if (transport.sessionId) { delete transports[transport.sessionId]; console.log(`Streamable connection closed for sessionId: ${transport.sessionId}`); } }; if (!server) { server = createMcpServer(apiKey); } // Connect to the MCP server await server.connect(transport); } else { // Invalid request res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request: No valid session ID provided", }, id: null, }); return; } // Handle the request await transport.handleRequest(req, res, req.body); }); // Reusable handler for GET and DELETE requests const handleSessionRequest = async (req, res) => { const sessionId = req.headers["mcp-session-id"]; if (!sessionId || !transports[sessionId]) { res.status(400).send("Invalid or missing session ID"); return; } const transport = transports[sessionId]; await transport.handleRequest(req, res); }; // Handle GET requests for server-to-client notifications via SSE app.get("/mcp", handleSessionRequest); // Handle DELETE requests for session termination app.delete("/mcp", handleSessionRequest); return app; } /** * Start the SSE server with proper error handling and graceful shutdown */ async function startServer() { try { const config = getServerConfig("HTTP_PORT", "8081"); const httpStreamServer = createHttpStreamServer(); const server = setupListenServer(httpStreamServer, "HttpStream", config); setupGracefulShutdown(server, "HttpStream"); } catch (error) { console.error("❌ Failed to start server:", error); process.exit(1); } } startServer().catch((error) => { console.error("❌ Server startup failed:", error); process.exit(1); }); //# sourceMappingURL=index.js.map