Streamable HTTP Transport
MCP v1.0 introduced Streamable HTTP as the standard remote transport. This document covers setup, configuration, and usage of the Streamable HTTP listener in loomd.
For current shipped/in-progress status, see docs/IMPLEMENTATION_STATUS.md.
Architecture
Current (local):
IDE -> stdio -> [loom proxy] -> unix socket -> [loomd] -> stdio -> [mcp-server]
With HTTP (remote):
IDE -> stdio -> [loom proxy --remote] -> HTTPS POST -> [loomd /mcp]
^
Web client --------------------------------- HTTPS POST -> [loomd /mcp]
The Unix socket path remains untouched. Streamable HTTP is an additive parallel listener on a separate port.
Quick Start
Local Development (No Auth)
Start the daemon with HTTP on localhost:
loomd --http-addr localhost:8088 --registry /path/to/registry.yaml
Test with curl:
curl -X POST http://localhost:8088/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'
Expected output includes a JSON-RPC success response and an Mcp-Session-Id header:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "<server-supported-version>",
"capabilities": {},
"serverInfo": {"name": "loom", "version": "<daemon-version>"},
"instructions": "Loom daemon - unified MCP hub management"
}
}
Use that Mcp-Session-Id value on subsequent requests.
Team/Shared Daemon (With Auth)
- Generate a bearer token:
loom auth token-generate
# Token: loom_a1b2c3...
# Stored as: LOOM_HTTP_TOKEN (in macOS Keychain)
- Configure the daemon (
~/.config/loom/config.yaml):
http:
auth:
type: token
token_secret_key: LOOM_HTTP_TOKEN
tls_cert_file: /path/to/cert.pem
tls_key_file: /path/to/key.pem
- Start the daemon:
loomd --http-addr :8088 --registry /path/to/registry.yaml
- Connect from a developer machine:
loom proxy --remote https://shared-server:8088/mcp --remote-token loom_a1b2c3...
Or set the token via environment variable:
export LOOM_REMOTE_TOKEN=loom_a1b2c3...
loom proxy --remote https://shared-server:8088/mcp
Configuration Reference
Daemon Flags
| Flag | Description | Default |
|---|---|---|
--http-addr | Address for Streamable HTTP listener | (disabled) |
File Config (~/.config/loom/config.yaml)
http:
# Session management
session_timeout_minutes: 30 # Idle session expiry
max_sessions: 1000 # Max concurrent sessions
# Origin restriction (DNS rebinding protection)
allowed_origins:
- "https://app.example.com"
# TLS (required for non-localhost)
tls_cert_file: /path/to/cert.pem
tls_key_file: /path/to/key.pem
# Authentication
auth:
type: token # token | oidc | mtls | oauth2
token_secret_key: LOOM_HTTP_TOKEN
# For type: oidc
oidc_issuer: https://auth.example.com
oidc_client_id: loom-daemon
# For type: mtls
tls_client_ca: /path/to/ca.pem
allowed_common_names:
- "developer.example.com"
Authentication Types
| Type | Description | Use Case |
|---|---|---|
token | Static bearer token | Small teams, dev environments |
oidc | JWT via OIDC provider | Enterprise SSO integration |
mtls | Mutual TLS with client certs | Zero-trust infrastructure |
oauth2 | OAuth 2.1 with PKCE | Standards-based access control |
When binding to localhost, auth is optional. When binding to a non-localhost address, auth is required and the daemon refuses to start without it.
OAuth 2.1 (PKCE)
The daemon includes a built-in OAuth 2.1 authorization server. Enable it in the daemon config:
http:
auth:
type: oauth2
oauth:
enabled: true
token_ttl_minutes: 60
allow_dynamic_registration: true
This exposes standard endpoints:
| Path | Description |
|---|---|
/.well-known/oauth-authorization-server | Server metadata (RFC 8414) |
/.well-known/oauth-protected-resource | Protected resource metadata (RFC 9728) |
/oauth2/register | Dynamic client registration (RFC 7591) |
/oauth2/authorize | Authorization endpoint |
/oauth2/token | Token exchange (PKCE S256 required) |
/oauth2/revoke | Token revocation (RFC 7009) |
See docs/ENTERPRISE_SECURITY.md for the full PKCE flow with curl examples.
Security
- Localhost binding: Auth is optional (
http.auth.typemay be omitted). - Non-localhost binding: Auth required. The daemon refuses to start without
http.auth.typeconfigured. - Non-localhost without TLS: The daemon logs a warning. Strongly recommend TLS for any non-localhost deployment.
- Session management: Sessions expire after configurable idle timeout. A background reaper cleans up expired sessions every minute.
Protocol Details
The Streamable HTTP transport follows the MCP Streamable HTTP specification:
- POST /mcp: Send JSON-RPC messages. Returns
application/jsonfor requests,202 Acceptedfor notifications. - GET /mcp: Returns
405 Method Not Allowed(server-initiated messages not yet implemented). - DELETE /mcp: Terminates a session. Requires
Mcp-Session-Idheader. - Mcp-Session-Id: Set by the server on initialize response. Required for all subsequent requests.
Request Headers
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | Must be application/json |
Accept | Recommended | Should include application/json |
Mcp-Session-Id | After init | Session ID from initialize response |
Authorization | If auth enabled | Bearer <token> |
CLI Commands
Token Management
loom auth token-generate # Generate and store a new token
loom auth token-generate --key MY_KEY # Use custom secret key
loom auth token-show # Display current token
loom auth token-revoke # Delete token
Remote Proxy
loom proxy --remote https://host:8088/mcp --remote-token TOKEN
loom proxy --remote https://host:8088/mcp # Uses LOOM_REMOTE_TOKEN env
Endpoints
| Path | Auth | Description |
|---|---|---|
/mcp | Required (if configured) | MCP Streamable HTTP endpoint |
/health | No | Health check for load balancer probes |
/.well-known/oauth-authorization-server | No | OAuth server metadata (when http.oauth.enabled=true) |
/.well-known/oauth-protected-resource | No | OAuth protected resource metadata (when http.oauth.enabled=true) |
/oauth2/register | No | Dynamic client registration (when http.oauth.enabled=true) |
/oauth2/authorize | No | OAuth authorization endpoint (when http.oauth.enabled=true) |
/oauth2/token | No | OAuth token endpoint (when http.oauth.enabled=true) |
/oauth2/revoke | No | OAuth token revocation endpoint (when http.oauth.enabled=true) |