added sse http support

This commit is contained in:
buggyhunter 2025-05-28 00:34:49 +03:00
parent 2628a868c2
commit c2f695d377

View File

@ -6,6 +6,10 @@ import { z } from "zod";
import { searchLibraries, fetchLibraryDocumentation } from "./lib/api.js";
import { formatSearchResults } from "./lib/utils.js";
import dotenv from "dotenv";
import { createServer } from "http";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { parse } from "url";
// Load environment variables from .env file if present
dotenv.config();
@ -23,19 +27,23 @@ if (process.env.DEFAULT_MINIMUM_TOKENS) {
}
}
// Create server instance
const server = new McpServer({
// Store SSE transports by session ID
const sseTransports: Record<string, SSEServerTransport> = {};
// Function to create a new server instance with all tools registered
function createServerInstance() {
const server = new McpServer({
name: "Context7",
description: "Retrieves up-to-date documentation and code examples for any library.",
version: "1.0.6",
version: "1.0.7",
capabilities: {
resources: {},
tools: {},
},
});
});
// Register Context7 tools
server.tool(
// Register Context7 tools
server.tool(
"resolve-library-id",
`Resolves a package/product name to a Context7-compatible library ID and returns a list of matching libraries.
@ -111,9 +119,9 @@ ${resultsText}`,
],
};
}
);
);
server.tool(
server.tool(
"get-library-docs",
"Fetches up-to-date documentation for a library. You must call 'resolve-library-id' first to obtain the exact Context7-compatible library ID required to use this tool, UNLESS the user explicitly provides a library ID in the format '/org/project' or '/org/project/version' in their query.",
{
@ -160,12 +168,98 @@ server.tool(
],
};
}
);
);
return server;
}
async function main() {
const transportType = process.env.MCP_TRANSPORT || "stdio";
if (transportType === "http" || transportType === "sse") {
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000;
const httpServer = createServer(async (req, res) => {
const url = parse(req.url || "").pathname;
// Set CORS headers for all responses
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, MCP-Session-Id, mcp-session-id');
// Handle preflight OPTIONS requests
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
return;
}
try {
// Create new server instance for each request
const requestServer = createServerInstance();
if (url === "/mcp") {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined
});
await requestServer.connect(transport);
await transport.handleRequest(req, res);
} else if (url === "/sse" && req.method === "GET") {
// Create new SSE transport for GET request
const sseTransport = new SSEServerTransport("/messages", res);
// Store the transport by session ID
sseTransports[sseTransport.sessionId] = sseTransport;
// Clean up transport when connection closes
res.on("close", () => {
delete sseTransports[sseTransport.sessionId];
});
await requestServer.connect(sseTransport);
} else if (url === "/messages" && req.method === "POST") {
// Get session ID from query parameters
const parsedUrl = parse(req.url || "", true);
const sessionId = parsedUrl.query.sessionId as string;
if (!sessionId) {
res.writeHead(400);
res.end("Missing sessionId parameter");
return;
}
// Get existing transport for this session
const sseTransport = sseTransports[sessionId];
if (!sseTransport) {
res.writeHead(400);
res.end(`No transport found for sessionId: ${sessionId}`);
return;
}
// Handle the POST message with the existing transport
await sseTransport.handlePostMessage(req, res);
} else if (url === "/ping") {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('pong');
} else {
res.writeHead(404);
res.end("Not found");
}
} catch (error) {
console.error("Error handling request:", error);
if (!res.headersSent) {
res.writeHead(500);
res.end("Internal Server Error");
}
}
});
httpServer.listen(port, () => {
console.error(`Context7 Documentation MCP Server running on ${transportType.toUpperCase()} at http://localhost:${port}/mcp and legacy SSE at /sse`);
});
} else {
// Stdio transport - this is already stateless by nature
const server = createServerInstance();
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Context7 Documentation MCP Server running on stdio");
}
}
main().catch((error) => {