Transport
air supports three MCP transport types. Set via the transport config.
stdio
Standard input/output. Claude Desktop launches your server as a child process and communicates via stdin/stdout.
defineServer({
transport: { type: 'stdio' },
});No port needed. Logs are sent to stderr (stdout is reserved for MCP protocol).
SSE (Server-Sent Events)
HTTP-based remote connection. A separate MCP server instance is created per session. Since v0.4.0, air uses its own AirSSETransport with built-in heartbeat, reconnection recovery, and idle session cleanup.
defineServer({
transport: { type: 'sse', port: 3510 },
});SSE options (v0.4.0+)
| Option | Default | Description |
|---|---|---|
sseHeartbeatMs | 30000 | Ping interval to detect dead connections. 0 to disable |
sseReplayBufferSize | 100 | Messages kept for Last-Event-ID reconnection |
sseReplayTtlMs | 300000 | Replay buffer TTL (5 minutes) |
sseIdleTimeoutMs | 600000 | Auto-close sessions idle for 10 minutes |
maxSseSessions | 200 | Max concurrent SSE sessions (503 when exceeded) |
defineServer({
transport: { type: 'sse', port: 3510 },
sseHeartbeatMs: 30_000,
sseReplayBufferSize: 100,
sseReplayTtlMs: 300_000,
sseIdleTimeoutMs: 600_000,
maxSseSessions: 200,
});SSE endpoints
| Method | Path | Description |
|---|---|---|
GET | /sse | Open SSE stream (creates new session) |
POST | /message?sessionId=xxx | Send message |
GET | / | Server status JSON |
GET | /health | Health check with session count (v0.4.0+) |
OPTIONS | * | CORS preflight (auto-handled) |
Client connection flow:
GET /sse→ establishes SSE connection, assigns session IDPOST /message?sessionId=xxx→ sends MCP messages- On disconnect, session is automatically cleaned up
Reconnection (v0.4.0+)
Every SSE message includes an id: field. When a client reconnects with Last-Event-ID header, missed messages are automatically replayed from the buffer.
// Client reconnects with header:
// Last-Event-ID: 42
// → Server replays messages 43, 44, 45, ...Heartbeat (v0.4.0+)
A :ping comment is sent every sseHeartbeatMs to detect broken connections early. If the write fails, the session is immediately closed and cleaned up.
Terminal output:
[air] SSE server listening on port 3510
[air] SSE client connected (session: a1b2c3d4-...)
[air] SSE client disconnected (session: a1b2c3d4-...)CORS
SSE transport sets CORS headers automatically:
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, OPTIONSAccess-Control-Allow-Headers: Content-Type
Use corsPlugin for custom configuration.
Streamable HTTP
Latest MCP transport spec. Single endpoint handles all communication.
defineServer({
transport: { type: 'http', port: 3510 },
});| Method | Description |
|---|---|
POST | MCP message handling |
GET | Server status JSON |
DELETE | Session termination |
Each session gets a unique ID via crypto.randomUUID().
Cloudflare Workers
Edge deployment transport. No Node.js http server — instead, air exposes a fetch() handler compatible with the Workers runtime. MCP protocol is handled as JSON-RPC 2.0 directly (no SDK dependency needed at runtime).
import { defineServer, defineTool } from '@airmcp-dev/core';
const server = defineServer({
name: 'my-edge-server',
transport: { type: 'workers' },
tools: [
defineTool('hello', {
params: { name: 'string' },
handler: async ({ name }) => `Hello, ${name}!`,
}),
],
});
export default {
async fetch(request: Request, env: any): Promise<Response> {
const url = new URL(request.url);
// MCP endpoint
if (request.method === 'POST' && url.pathname === '/') {
return server.fetch!(request);
}
// Status
if (request.method === 'GET' && url.pathname === '/') {
return Response.json(server.status());
}
return new Response('Not Found', { status: 404 });
},
};server.fetch handles:
initialize→ server info + capabilities (tools, resources, prompts)tools/list→ tool schemas with annotationstools/call→ executes through the full middleware chainresources/list→ list registered resourcesresources/read→ read resource content by URI (v0.4.0+)prompts/list→ list registered promptsprompts/get→ get prompt messages by name (v0.4.0+)notifications/initialized→ 204 No Content
TIP
Workers transport doesn't call server.start(). The server is ready immediately — just call server.fetch(request) in your Workers fetch handler.
WARNING
stdio and sse transports are not available in Workers. Only workers (and http for Node.js) support the Streamable HTTP protocol.
auto (default)
Omit type or set 'auto' to auto-detect based on environment:
defineServer({
transport: { type: 'auto', port: 3510 },
});Detection order:
MCP_TRANSPORTenv variable overrides everything (stdio,http,sse,workers)- Workers environment detected (globalThis.caches exists, no process.stdin) →
workers - stdin is not TTY →
stdio(MCP client spawned the process) - stdin is TTY →
http(developer running directly)
# Force via env variable
MCP_TRANSPORT=sse node dist/index.js
# Piped → auto-detects stdio
echo '{}' | node dist/index.js
# Direct terminal → auto-detects http
node dist/index.jsTransportConfig
interface TransportConfig {
type?: 'stdio' | 'sse' | 'http' | 'workers' | 'auto'; // Default: 'auto'
port?: number; // HTTP/SSE port
host?: string; // Default: 'localhost'
basePath?: string; // Default: '/'
}Port resolution order
Port is determined by:
transport.port(explicit)dev.port(dev mode setting)3100(hardcoded default)
// transport.port takes priority
defineServer({ transport: { type: 'sse', port: 3510 } }); // → 3510
// Falls back to dev.port
defineServer({ transport: { type: 'sse' }, dev: { port: 4000 } }); // → 4000
// Falls back to 3100
defineServer({ transport: { type: 'sse' } }); // → 3100Which to use?
| Transport | Use case | Client |
|---|---|---|
stdio | Local tools, Claude Desktop direct | Claude Desktop, mcp-cli |
sse | Remote servers, existing MCP infra, mcp-proxy compat | Cursor, VS Code, mcp-proxy |
http | New deployments, Streamable HTTP spec, behind reverse proxy | Latest MCP clients |
workers | Cloudflare Workers edge deployment, serverless | PlayMCP, any MCP client via remote URL |
TIP
When deploying behind a reverse proxy (Nginx, Cloudflare), use http transport. SSE requires special proxy config for long-lived connections (proxy_read_timeout 86400).
INFO
With stdio transport, all console.log output can mix into the MCP protocol stream. air's builtin logger automatically redirects to stderr.