chunk-MXVPEZER.jsā¢14.2 kB
// src/InMemoryEventStore.ts
var InMemoryEventStore = class {
events = /* @__PURE__ */ new Map();
/**
* Replays events that occurred after a specific event ID
* Implements EventStore.replayEventsAfter
*/
async replayEventsAfter(lastEventId, {
send
}) {
if (!lastEventId || !this.events.has(lastEventId)) {
return "";
}
const streamId = this.getStreamIdFromEventId(lastEventId);
if (!streamId) {
return "";
}
let foundLastEvent = false;
const sortedEvents = [...this.events.entries()].sort(
(a, b) => a[0].localeCompare(b[0])
);
for (const [
eventId,
{ message, streamId: eventStreamId }
] of sortedEvents) {
if (eventStreamId !== streamId) {
continue;
}
if (eventId === lastEventId) {
foundLastEvent = true;
continue;
}
if (foundLastEvent) {
await send(eventId, message);
}
}
return streamId;
}
/**
* Stores an event with a generated event ID
* Implements EventStore.storeEvent
*/
async storeEvent(streamId, message) {
const eventId = this.generateEventId(streamId);
this.events.set(eventId, { message, streamId });
return eventId;
}
/**
* Generates a unique event ID for a given stream ID
*/
generateEventId(streamId) {
return `${streamId}_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
}
/**
* Extracts the stream ID from an event ID
*/
getStreamIdFromEventId(eventId) {
const parts = eventId.split("_");
return parts.length > 0 ? parts[0] : "";
}
};
// src/proxyServer.ts
import {
CallToolRequestSchema,
CompleteRequestSchema,
GetPromptRequestSchema,
ListPromptsRequestSchema,
ListResourcesRequestSchema,
ListResourceTemplatesRequestSchema,
ListToolsRequestSchema,
LoggingMessageNotificationSchema,
ReadResourceRequestSchema,
ResourceUpdatedNotificationSchema,
SubscribeRequestSchema,
UnsubscribeRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
var proxyServer = async ({
client,
server,
serverCapabilities
}) => {
if (serverCapabilities?.logging) {
server.setNotificationHandler(
LoggingMessageNotificationSchema,
async (args) => {
return client.notification(args);
}
);
client.setNotificationHandler(
LoggingMessageNotificationSchema,
async (args) => {
return server.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/startHTTPServer.ts
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import {
StreamableHTTPServerTransport
} from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import http from "http";
import { randomUUID } from "crypto";
var getBody = (request) => {
return new Promise((resolve) => {
const bodyParts = [];
let body;
request.on("data", (chunk) => {
bodyParts.push(chunk);
}).on("end", () => {
body = Buffer.concat(bodyParts).toString();
try {
resolve(JSON.parse(body));
} catch (error) {
console.error("[mcp-proxy] error parsing body", error);
resolve(null);
}
});
});
};
var handleStreamRequest = async ({
activeTransports,
createServer,
endpoint,
eventStore,
onClose,
onConnect,
req,
res
}) => {
if (req.method === "POST" && new URL(req.url, "http://localhost").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 = await getBody(req);
if (sessionId) {
const activeTransport = activeTransports[sessionId];
if (!activeTransport) {
res.setHeader("Content-Type", "application/json");
res.writeHead(404).end(
JSON.stringify({
error: {
code: -32001,
message: "Session not found"
},
id: null,
jsonrpc: "2.0"
})
);
return true;
}
transport = activeTransport.transport;
server = activeTransport.server;
} else if (!sessionId && isInitializeRequest(body)) {
transport = new StreamableHTTPServerTransport({
eventStore: eventStore || new InMemoryEventStore(),
onsessioninitialized: (_sessionId) => {
activeTransports[_sessionId] = {
server,
transport
};
},
sessionIdGenerator: randomUUID
});
transport.onclose = async () => {
const sid = transport.sessionId;
if (sid && activeTransports[sid]) {
if (onClose) {
await onClose(server);
}
try {
await server.close();
} catch (error) {
console.error("[mcp-proxy] error closing server", error);
}
delete activeTransports[sid];
}
};
try {
server = await createServer(req);
} catch (error) {
if (error instanceof Response) {
res.writeHead(error.status).end(error.statusText);
return true;
}
res.writeHead(500).end("Error creating server");
return true;
}
server.connect(transport);
if (onConnect) {
await onConnect(server);
}
await transport.handleRequest(req, res, body);
return true;
} else {
res.setHeader("Content-Type", "application/json");
res.writeHead(400).end(
JSON.stringify({
error: {
code: -32e3,
message: "Bad Request: No valid session ID provided"
},
id: null,
jsonrpc: "2.0"
})
);
return true;
}
await transport.handleRequest(req, res, body);
return true;
} catch (error) {
console.error("[mcp-proxy] 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 true;
}
if (req.method === "GET" && new URL(req.url, "http://localhost").pathname === endpoint) {
const sessionId = req.headers["mcp-session-id"];
const activeTransport = sessionId ? activeTransports[sessionId] : void 0;
if (!sessionId) {
res.writeHead(400).end("No sessionId");
return true;
}
if (!activeTransport) {
res.writeHead(400).end("No active transport");
return true;
}
const lastEventId = req.headers["last-event-id"];
if (lastEventId) {
console.log(
`[mcp-proxy] client reconnecting with Last-Event-ID ${lastEventId} for session ID ${sessionId}`
);
} else {
console.log(
`[mcp-proxy] establishing new SSE stream for session ID ${sessionId}`
);
}
await activeTransport.transport.handleRequest(req, res);
return true;
}
if (req.method === "DELETE" && new URL(req.url, "http://localhost").pathname === endpoint) {
console.log("[mcp-proxy] received delete request");
const sessionId = req.headers["mcp-session-id"];
if (!sessionId) {
res.writeHead(400).end("Invalid or missing sessionId");
return true;
}
console.log("[mcp-proxy] received delete request for session", sessionId);
const activeTransport = activeTransports[sessionId];
if (!activeTransport) {
res.writeHead(400).end("No active transport");
return true;
}
try {
await activeTransport.transport.handleRequest(req, res);
if (onClose) {
await onClose(activeTransport.server);
}
} catch (error) {
console.error("[mcp-proxy] error handling delete request", error);
res.writeHead(500).end("Error handling delete request");
}
return true;
}
return false;
};
var handleSSERequest = async ({
activeTransports,
createServer,
endpoint,
onClose,
onConnect,
req,
res
}) => {
if (req.method === "GET" && new URL(req.url, "http://localhost").pathname === 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 true;
}
res.writeHead(500).end("Error creating server");
return true;
}
activeTransports[transport.sessionId] = transport;
let closed = false;
res.on("close", async () => {
closed = true;
try {
await server.close();
} catch (error) {
console.error("[mcp-proxy] error closing server", error);
}
delete activeTransports[transport.sessionId];
await onClose?.(server);
});
try {
await server.connect(transport);
await transport.send({
jsonrpc: "2.0",
method: "sse/connection",
params: { message: "SSE Connection established" }
});
if (onConnect) {
await onConnect(server);
}
} catch (error) {
if (!closed) {
console.error("[mcp-proxy] error connecting to server", error);
res.writeHead(500).end("Error connecting to server");
}
}
return true;
}
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 true;
}
const activeTransport = activeTransports[sessionId];
if (!activeTransport) {
res.writeHead(400).end("No active transport");
return true;
}
await activeTransport.handlePostMessage(req, res);
return true;
}
return false;
};
var startHTTPServer = async ({
createServer,
eventStore,
host = "::",
onClose,
onConnect,
onUnhandledRequest,
port,
sseEndpoint = "/sse",
streamEndpoint = "/mcp"
}) => {
const activeSSETransports = {};
const activeStreamTransports = {};
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", "*");
res.setHeader("Access-Control-Expose-Headers", "mcp-session-id");
} catch (error) {
console.error("[mcp-proxy] 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 (sseEndpoint && await handleSSERequest({
activeTransports: activeSSETransports,
createServer,
endpoint: sseEndpoint,
onClose,
onConnect,
req,
res
})) {
return;
}
if (streamEndpoint && await handleStreamRequest({
activeTransports: activeStreamTransports,
createServer,
endpoint: streamEndpoint,
eventStore,
onClose,
onConnect,
req,
res
})) {
return;
}
if (onUnhandledRequest) {
await onUnhandledRequest(req, res);
} else {
res.writeHead(404).end();
}
});
await new Promise((resolve) => {
httpServer.listen(port, host, () => {
resolve(void 0);
});
});
return {
close: async () => {
for (const transport of Object.values(activeSSETransports)) {
await transport.close();
}
for (const transport of Object.values(activeStreamTransports)) {
await transport.transport.close();
}
return new Promise((resolve, reject) => {
httpServer.close((error) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
}
};
};
export {
InMemoryEventStore,
proxyServer,
startHTTPServer
};
//# sourceMappingURL=chunk-MXVPEZER.js.map