@tmcp/transport-sse
v0.5.3
Published
Transport for TMCP using Server-Sent Events
Downloads
273
Maintainers
Readme
@tmcp/transport-sse
A Server-Sent Events (SSE) transport implementation for TMCP (TypeScript Model Context Protocol) servers. This package provides SSE-based communication for MCP servers, enabling efficient real-time bidirectional communication between clients and servers through standard HTTP with Server-Sent Events.
Installation
pnpm add @tmcp/transport-sse tmcpUsage
Basic Setup
import { McpServer } from 'tmcp';
import { SseTransport } from '@tmcp/transport-sse';
// Create your MCP server
const server = new McpServer(
{
name: 'my-sse-server',
version: '1.0.0',
description: 'My SSE MCP server',
},
{
adapter: new YourSchemaAdapter(),
capabilities: {
tools: { listChanged: true },
prompts: { listChanged: true },
resources: { listChanged: true },
},
},
);
// Add your tools, prompts, and resources
server.tool(
{
name: 'example_tool',
description: 'An example tool',
},
async () => {
return {
content: [{ type: 'text', text: 'Hello from SSE!' }],
};
},
);
// Create the SSE transport (defaults to '/sse' path for events, '/message' for messages)
const transport = new SseTransport(server);
// Use with your preferred HTTP server
// Example with Node.js built-in server:
import * as http from 'node:http';
import { createRequestListener } from '@remix-run/node-fetch-server';
const httpServer = http.createServer((request)=>{
const response = await transport.respond(request);
if(response){
return response;
}
return new Response(null, { status: 404 });
}));
httpServer.listen(3000, () => {
console.log('MCP SSE server listening on port 3000');
});With Custom Configuration
const transport = new SseTransport(server, {
// Custom SSE endpoint path (default: '/sse', use null to respond on every path)
path: '/api/events',
// Custom message endpoint path (default: '/message')
endpoint: '/api/message',
// Custom session ID generation
getSessionId: () => {
return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
},
oauth: OAuth; // an oauth provider generated from @tmcp/auth
});
> [!NOTE]
> In development you'll see a warning when the `path` option is omitted. Upcoming releases will interpret an `undefined` path as "respond on every path", so set the field explicitly (for example `path: '/sse'` or `path: null`) to keep the behaviour you expect.With Custom Context
You can pass custom context data to your MCP server for each request. This is useful for authentication, user information, database connections, etc.
// Define your custom context type
interface MyContext {
userId: string;
permissions: string[];
database: DatabaseConnection;
}
// Create server with custom context
const server = new McpServer(serverInfo, options).withContext<MyContext>();
server.tool(
{
name: 'get-user-profile',
description: 'Get the current user profile',
},
async () => {
// Access custom context in your handler
const { userId, database } = server.ctx.custom!;
const profile = await database.users.findById(userId);
return {
content: [
{ type: 'text', text: `User profile: ${JSON.stringify(profile)}` }
],
};
},
);
// Create transport (it will be typed to accept your custom context)
const transport = new SseTransport(server);
// Create transport (it will be typed to accept your custom context)
const transport = new HttpTransport(server);
// then in the handler
const response = await transport.respond(req, {
userId,
permissions,
database: req.locals.db,
});Session Management
The SSE transport supports custom session managers for different deployment scenarios:
In-Memory Sessions (Default)
import {
InMemoryStreamSessionManager,
InMemoryInfoSessionManager,
} from '@tmcp/session-manager';
const transport = new SseTransport(server, {
sessionManager: {
streams: new InMemoryStreamSessionManager(),
info: new InMemoryInfoSessionManager(),
},
});Redis Sessions (Multi-Server/Serverless)
For deployments across multiple servers or serverless environments where sessions need to be shared:
import {
RedisStreamSessionManager,
RedisInfoSessionManager,
} from '@tmcp/session-manager-redis';
const transport = new SseTransport(server, {
sessionManager: {
streams: new RedisStreamSessionManager('redis://localhost:6379'),
info: new RedisInfoSessionManager('redis://localhost:6379'),
},
});When to use Redis sessions:
- Multi-server deployments: When your application runs on multiple servers and clients might connect to different instances
- Serverless deployments: When your transport is deployed on serverless platforms where instances are ephemeral (attention, serverless environment generally kills SSE request after a not-so-long amount of time, it's generally preferable to use streaming-http)
- Load balancing: When using load balancers that might route requests to different server instances
No matter which backend you choose, the transport stores client capabilities, client info, and log level so you can access them later via server.ctx.sessionInfo.
API
SseTransport
Constructor
new SseTransport(server: McpServer, options?: SseTransportOptions)Creates a new SSE transport instance.
Parameters:
server- A TMCP server instance to handle incoming requestsoptions- Optional configuration for the transport
Options:
interface SseTransportOptions {
getSessionId?: () => string; // Custom session ID generator
path?: string | null; // SSE endpoint path (default: '/sse', null responds on every path)
endpoint?: string; // Message endpoint path (default: '/message')
oauth?: OAuth; // an oauth provider generated from @tmcp/auth
sessionManager?: {
streams?: StreamSessionManager;
info?: InfoSessionManager;
}; // Provide custom managers; defaults to in-memory implementations
}By default the transport instantiates the in-memory managers. You can mix different backends (for example, Durable Objects for streaming with Redis for metadata) by supplying only the field you want to override.
Methods
respond(request: Request, customContext?: T): Promise<Response | null>
Processes an HTTP request and returns a Response with Server-Sent Events, or null if the request path doesn't match the configured SSE paths.
Parameters:
request- A Web API Request object containing the JSON-RPC messagecustomContext- Optional custom context data to pass to the MCP server for this request
Returns:
- A Response object with SSE stream for ongoing communication, or null if the request path doesn't match the SSE endpoints
HTTP Methods:
- GET: Establishes SSE connection and returns event stream with endpoint information
- POST: Processes MCP messages and sends responses through the SSE stream
- DELETE: Disconnects sessions and cleans up resources
- OPTIONS: Handles CORS preflight requests
close(): void
Closes all active SSE sessions and cleans up resources.
Protocol Details
HTTP Methods
The transport supports four HTTP methods across two endpoints:
GET - SSE Event Stream (path: /sse)
Establishes a Server-Sent Events connection:
GET /sse?session_id=optional-session-id HTTP/1.1
mcp-session-id: optional-session-idResponse: Long-lived SSE stream with endpoint information:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Access-Control-Allow-Origin: *
mcp-session-id: generated-or-provided-session-id
event: endpoint
data: /message?session_id=session-id
data: {"jsonrpc":"2.0","method":"notifications/initialized","params":{}}
POST - Message Processing (path: /message)
Clients send JSON-RPC messages via HTTP POST requests:
POST /message?session_id=session-id HTTP/1.1
Content-Type: application/json
mcp-session-id: session-id
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}Response: Acknowledgment (response sent through SSE stream):
HTTP/1.1 202 Accepted
Content-Type: application/json
mcp-session-id: session-idDELETE - Session Disconnect
Disconnects a session and cleans up resources:
DELETE /sse?session_id=session-id HTTP/1.1
mcp-session-id: session-to-disconnectResponse:
HTTP/1.1 204 No Content
mcp-session-id: session-to-disconnectSession Management
- Session ID: Can be provided via query parameter or
mcp-session-idheader - Automatic Generation: If no session ID is provided, one is generated automatically
- Session Persistence: Sessions persist across multiple requests until the client disconnects
- Server Notifications: Server can send notifications to active sessions through SSE
Framework Examples
Bun
import { McpServer } from 'tmcp';
import { SseTransport } from '@tmcp/transport-sse';
const server = new McpServer(/* ... */);
const transport = new SseTransport(server);
Bun.serve({
port: 3000,
async fetch(req) {
const response = await transport.respond(req);
if (response === null) {
return new Response('Not Found', { status: 404 });
}
return response;
},
});Deno
import { McpServer } from 'tmcp';
import { SseTransport } from '@tmcp/transport-sse';
const server = new McpServer(/* ... */);
const transport = new SseTransport(server);
Deno.serve({ port: 3000 }, async (req) => {
const response = await transport.respond(req);
if (response === null) {
return new Response('Not Found', { status: 404 });
}
return response;
});srvx
If you want the same experience across Deno, Bun, and Node.js, you can use srvx.
import { McpServer } from 'tmcp';
import { SseTransport } from '@tmcp/transport-sse';
import { serve } from 'srvx';
const server = new McpServer(/* ... */);
const transport = new SseTransport(server);
serve({
async fetch(req) {
const response = await transport.respond(req);
if (response === null) {
return new Response('Not Found', { status: 404 });
}
return response;
},
});Error Handling
The transport includes comprehensive error handling:
- Malformed JSON: Invalid JSON requests return appropriate error responses
- Content-Type Validation: Ensures proper
application/jsoncontent type - Session Management: Automatic cleanup of disconnected sessions
- CORS Handling: Built-in CORS support for cross-origin requests
- Server Errors: Server processing errors are propagated to clients
Development
# Install dependencies
pnpm install
# Generate TypeScript declarations
pnpm generate:types
# Lint the code
pnpm lintRequirements
- Node.js 16+ (for native ES modules and Web API support)
- A TMCP server instance
- An HTTP server framework or runtime
- A schema adapter (Zod, Valibot, etc.)
Related Packages
tmcp- Core TMCP server implementation@tmcp/transport-http- HTTP transport with SSE streaming@tmcp/transport-stdio- Standard I/O transport@tmcp/adapter-zod- Zod schema adapter@tmcp/adapter-valibot- Valibot schema adapter
Acknowledgments
Huge thanks to Sean O'Bannon that provided us with the @tmcp scope on npm.
License
MIT
