← Back to Patterns

Hosted MCP transport (SSE & Streamable HTTP)

Availablemcp•intermediate

Overview

The hosted MCP Worker (apps/mcp-worker) exposes two transports to AI clients:

TransportSpecEndpointsPreferred
Streamable HTTPMCP 2025-03-26GETPOST /mcp✅ Yes — Claude Code, modern clients
SSE + messagesMCP 2024-11-05GET /sse · POST /messages?session_id=…Previous / 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 /mcp URL; Mcp-Session-Id header; works where clients cannot hold a long-lived SSE socket
  • SSE — streams human.ask tokens in real time (canStreamTool + proxyToolCallStreaming in dispatcher.ts)
  • Auto selection — 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:

    MethodPathAuthNotes
    GET/healthnoneLiveness
    GET/sseBearerOpen SSE session; returns session_id event
    POST/messages?session_id=…sessionJSON-RPC dispatch; mirrors on SSE stream
    GET/mcpBearerStreamable HTTP — SSE keepalive stream
    POST/mcpBearerStreamable HTTP — JSON-RPC request/response
    GET/.well-known/oauth-protected-resourcenoneRFC 9728 discovery
    GET/auth.mdnoneHuman-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)

    >
    SDK:

    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

  • Claude Code hosted — /mcp URL only; no local npx process.
  • Cursor local — stdio bridge with HUMAN_MCP_TRANSPORT=auto picks Streamable HTTP against org endpoint.
  • Streaming doc answers — SSE transport + human.ask for token-by-token Companion output.
  • Health checks — 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

  • 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
  • Same route in the other SDKs

    The TypeScript tab above is the contract. These calls hit that same route.

    >
    SDK: