core/packages/mcp-proxy/src/utils/mcp-transport.ts
2025-07-15 22:02:41 +05:30

88 lines
2.2 KiB
TypeScript

import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
/**
* MCP Transport that adapts HTTP Request/Response to MCP Transport interface
* for use in Remix API routes
*/
export class RemixMCPTransport implements Transport {
private _closed = false;
private _started = false;
constructor(
private request: Request,
private sendResponse: (response: Response) => void
) {}
sessionId?: string;
setProtocolVersion?: (version: string) => void;
async start(): Promise<void> {
if (this._started) return;
this._started = true;
// Parse the incoming MCP message from the request
try {
const body = await this.request.text();
if (!body.trim()) {
throw new Error("Empty request body");
}
const message = JSON.parse(body);
// Validate basic MCP message structure
if (!message.jsonrpc || message.jsonrpc !== "2.0") {
throw new Error("Invalid JSON-RPC message");
}
// Emit the message to handler
if (this.onmessage) {
try {
this.onmessage(message);
} catch (error) {
if (this.onerror) {
this.onerror(error as Error);
}
}
}
} catch (error) {
if (this.onerror) {
this.onerror(error as Error);
}
}
}
async send(message: any): Promise<void> {
if (this._closed) {
throw new Error("Transport is closed");
}
// Send the MCP response back as HTTP response
const response = new Response(JSON.stringify(message), {
status: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
},
});
this.sendResponse(response);
}
async close(): Promise<void> {
if (this._closed) return;
this._closed = true;
if (this.onclose) {
try {
this.onclose();
} catch (error) {
console.error("Error in close handler:", error);
}
}
}
onmessage: (message: any) => void = () => {};
onclose: () => void = () => {};
onerror: (error: Error) => void = () => {};
}