sse-transport.ts•2 kB
/**
* Streamable HTTP Transport for MCP
*
* Implements the modern MCP Streamable HTTP transport that uses:
* - Single /messages endpoint for all communication
* - Optional SSE streaming for server-initiated messages
* - Simpler than the old dual-endpoint HTTP+SSE approach
*/
export interface StreamOptions {
useStreaming?: boolean;
keepAlive?: boolean;
}
/**
* Create an SSE stream response
*/
export function createSSEStream(): {
stream: ReadableStream;
writer: WritableStreamDefaultWriter;
send: (data: any) => Promise<void>;
close: () => Promise<void>;
} {
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
const encoder = new TextEncoder();
const send = async (data: any) => {
const message = `data: ${JSON.stringify(data)}\n\n`;
await writer.write(encoder.encode(message));
};
const close = async () => {
await writer.close();
};
return {
stream: readable,
writer,
send,
close,
};
}
/**
* Create an SSE response with proper headers
*/
export function createSSEResponse(stream: ReadableStream, headers: Record<string, string> = {}): Response {
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
...headers,
},
});
}
/**
* Send a JSON-RPC response via SSE stream
*/
export async function sendJSONRPCMessage(
send: (data: any) => Promise<void>,
message: any
): Promise<void> {
await send({
jsonrpc: '2.0',
...message,
});
}
/**
* Create a standard JSON response (non-streaming)
*/
export function createJSONResponse(
body: any,
status: number = 200,
headers: Record<string, string> = {}
): Response {
return new Response(JSON.stringify(body), {
status,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
...headers,
},
});
}