import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
import { JSONRPCMessage, JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
import { Context } from 'hono';
import { SSEStreamingApi } from 'hono/streaming';
export class HonoSSETransport implements Transport {
private _sessionId: string;
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
private _stream?: SSEStreamingApi;
constructor(sessionId: string, stream: SSEStreamingApi) {
this._sessionId = sessionId;
this._stream = stream;
this._stream.onAbort(() => {
this.onclose?.();
});
}
async start(): Promise<void> {
// Send the endpoint event pointing to the message handler
// We assume the message endpoint is /messages?sessionId=...
const endpoint = `/messages?sessionId=${this._sessionId}`;
await this._stream?.writeSSE({
event: 'endpoint',
data: endpoint,
});
}
async close(): Promise<void> {
this._stream?.close();
this.onclose?.();
}
async send(message: JSONRPCMessage): Promise<void> {
await this._stream?.writeSSE({
event: 'message',
data: JSON.stringify(message),
});
}
// Helper to handle incoming POST messages
async handlePostMessage(c: Context): Promise<void> {
try {
const body = await c.req.json();
const message = JSONRPCMessageSchema.parse(body);
this.onmessage?.(message);
} catch (error) {
this.onerror?.(error as Error);
throw error;
}
}
get sessionId(): string {
return this._sessionId;
}
}