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 | ✅ Yes — Claude Code, modern clients |
| SSE + messages | MCP 2024-11-05 | GET /sse · POST /messages?session_id=… | Previous / streaming-heavy |
api.haio.run.Production: https://mcp.haio.run · Staging: https://mcp-staging.haio.run · Org: https://{slug}.mcp.haio.run.
Why two transports?
/mcp URL; Mcp-Session-Id header; works where clients cannot hold a long-lived SSE sockethuman.ask tokens in real time (canStreamTool + proxyToolCallStreaming in dispatcher.ts)npx @human/mcp tries 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 |
human.ask may return upstream text/event-stream; the SSE transport pipes API tokens to the client without buffering.Try it
Streamable HTTP (preferred)
Previous 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 "
}
}
}
} Use cases
/mcp URL only; no local npx process.HUMAN_MCP_TRANSPORT=auto picks Streamable HTTP against org endpoint.human.ask for token-by-token Companion output.GET /health for 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
docs/runbooks/mcp-streamable-http-transport.mdapps/mcp-worker/src/streamable-http.ts · transport.tsSame route in the other SDKs
The TypeScript tab above is the contract. These calls hit that same route.