Hosted MCP transport (SSE & Streamable HTTP)
Overview
The hosted MCP Worker (apps/mcp-worker) exposes two transports to AI clients:
| Transport | Spec | Endpoints | Preferred |
|---|---|---|---|
| Streamable HTTP | MCP 2025-03-26 | `GET | POST /mcp` |
| SSE + messages | MCP 2024-11-05 | GET /sse · POST /messages?session_id=… |
Legacy / streaming-heavy |
Both require a Bearer delegation JWT on connect. Sessions live in Cloudflare KV (~24h TTL). The Worker dispatches JSON-RPC; execution and provenance happen at api.haio.run.
Production: https://mcp.haio.run · Staging: https://mcp-staging.haio.run · Org: https://{slug}.mcp.haio.run.
Why two transports?
- Streamable HTTP — single
/mcpURL;Mcp-Session-Idheader; works where clients cannot hold a long-lived SSE socket - SSE — streams
human.asktokens in real time (canStreamTool+proxyToolCallStreamingindispatcher.ts) - Auto selection —
npx @human/mcptries Streamable HTTP first, falls back to SSE (HUMAN_MCP_TRANSPORT=auto)
Think of it like: HTTP/2 multiplexing vs. a phone line — same tools, different wire protocol.
Shipped routes
From apps/mcp-worker/src/index.ts and README.md:
| Method | Path | Auth | Notes |
|---|---|---|---|
| GET | /health |
none | Liveness |
| GET | /sse |
Bearer | Open SSE session; returns session_id event |
| POST | /messages?session_id=… |
session | JSON-RPC dispatch; mirrors on SSE stream |
| GET | /mcp |
Bearer | Streamable HTTP — SSE keepalive stream |
| POST | /mcp |
Bearer | Streamable HTTP — JSON-RPC request/response |
| GET | /.well-known/oauth-protected-resource |
none | RFC 9728 discovery |
| GET | /auth.md |
none | Human-readable registration guide |
Streaming: only human.ask may return upstream text/event-stream; the SSE transport pipes API tokens to the client without buffering.
Try it
Streamable HTTP (preferred)
Legacy SSE + messages
# 1) Open SSE (long-lived)
curl -N 'https://mcp.haio.run/sse' \
-H "Authorization: Bearer $HUMAN_DELEGATION_TOKEN" \
-H 'Accept: text/event-stream'
# → event: session data: {"session_id":"…"}
# 2) Dispatch JSON-RPC
curl -s -X POST "https://mcp.haio.run/messages?session_id=$SESSION_ID" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"human.ask","arguments":{"message":"Hello"}}}'
For streaming Companion tokens, keep the SSE connection open while calling human.ask — the Worker pipes POST /v1/companion/chat events through.
Claude Code config (Streamable HTTP)
{
"mcpServers": {
"human": {
"url": "https://mcp.haio.run/mcp",
"headers": {
"Authorization": "Bearer <delegation>"
}
}
}
}
Use cases
- Claude Code hosted —
/mcpURL only; no localnpxprocess. - Cursor local — stdio bridge with
HUMAN_MCP_TRANSPORT=autopicks Streamable HTTP against org endpoint. - Streaming doc answers — SSE transport +
human.askfor token-by-token Companion output. - Health checks —
GET /healthfor deploy verification (no auth).
Security considerations
DO
Use HTTPS org hosts; validate Mcp-Session-Id matches your delegation
Rotate delegations; DELETE /__internal/sessions/{id} cascades on revoke (API-initiated)
Prefer Streamable HTTP for new integrations
DON'T
Do not send Bearer tokens in query strings
Do not share session_id across different passport holders
Do not assume SSE session survives beyond KV TTL (~24h) — re-handshake if 401
See also
- Org MCP endpoint
- OAuth PKCE for MCP
- Runbook:
docs/runbooks/mcp-streamable-http-transport.md - Community: MCP Streamable HTTP on HUMΛN
- Code:
apps/mcp-worker/src/streamable-http.ts·transport.ts