HTTP bridge
REA speaks MCP over stdio only. The bridge in src/ is a Node service that accepts MCP over Streamable HTTP and relays every JSON-RPC message, unchanged, to a rea mcp child process over stdio. It does not interpret tools. Whatever REA registers is what the client sees.
Endpoints
| Method and path | Auth | What it does |
|---|---|---|
POST /mcp | Bearer token | Sends JSON-RPC messages. Without an mcp-session-id header the body must contain initialize, which opens a new session. |
GET /mcp | Bearer token | Opens the server-to-client event stream of an existing session. |
DELETE /mcp | Bearer token | Ends a session and stops its REA process. |
GET /healthz | none | Returns {"status":"ok","sessions":N}, where N counts live REA processes. |
REA_BRIDGE_PATH moves /mcp. Any other path answers 404 with {"error":"not found"}.
Every MCP request goes through the same checks, in order:
- The
Hostheader must matchREA_BRIDGE_ALLOWED_HOSTS(port ignored), unless that is*. Authorization: Bearer <token>must matchREA_BRIDGE_TOKEN. The bridge compares SHA-256 digests with a constant-time comparison, so timing does not reveal the token’s length or content.- A
POSTbody must be valid JSON no larger thanREA_BRIDGE_MAX_BODY_BYTES.
Sessions
Each MCP session gets its own rea mcp process, because REA keeps analysis state per process. Two sessions never share one. Implemented in src/session.ts.
- An
initializerequest starts a new REA process, and the response carries a random UUID in themcp-session-idheader. Later requests send that header. If theinitializedoes not complete, the bridge stops that REA process again. - Every message from the client resets the session’s idle timer.
- Progress notifications from REA go back on the HTTP stream of the request that asked for them.
- A session ends on
DELETE, afterREA_BRIDGE_IDLE_TIMEOUT_SECONDSwithout a message, when the client’s transport closes, when REA exits, or when the container stops. Its REA process stops with it. - At most
REA_BRIDGE_MAX_SESSIONSREA processes run at once, counting ones still initializing. One moreinitializegets503.
REA’s stderr lines go into the bridge log at info with "source":"rea".
Status codes
Refusals from the bridge itself. MCP-level errors from REA arrive inside normal JSON-RPC responses.
| Status | Body | Cause |
|---|---|---|
401 | {"error":"unauthorized"} with WWW-Authenticate: Bearer realm="rea" | Missing or wrong token. |
403 | JSON-RPC error | Host header not in REA_BRIDGE_ALLOWED_HOSTS. |
400 | JSON-RPC -32700 Parse error: body is not valid JSON | The body is not JSON. |
400 | JSON-RPC -32600 Bad Request: no session ID; send initialize first | No mcp-session-id header and the request is not an initialize POST. |
404 | JSON-RPC -32001 Session not found | The session ID is unknown, or the session already ended. Start a new one. |
404 | {"error":"not found"} | A path other than the MCP path or GET /healthz. |
413 | JSON-RPC -32600 Request body too large | The body exceeds REA_BRIDGE_MAX_BODY_BYTES. |
503 | JSON-RPC -32000 Too many active sessions; close one or retry later | REA_BRIDGE_MAX_SESSIONS REA processes are running. |
500 | JSON-RPC -32000 Could not start REA or Internal error | REA failed to start, or the bridge hit an unexpected error. The log has the cause. |
Limits
| Limit | Value | Where |
|---|---|---|
| Request body | REA_BRIDGE_MAX_BODY_BYTES, 4 MiB by default | Configuration |
| Concurrent sessions | REA_BRIDGE_MAX_SESSIONS, 4 by default | Configuration |
| Idle session | REA_BRIDGE_IDLE_TIMEOUT_SECONDS, 30 minutes by default | Configuration |
| Request headers | must arrive within 30 seconds | fixed |
| Open connections | 128 | fixed |
| Request duration | none | fixed |
There is no request timeout on purpose. Tool calls stream their results for as long as REA works on them, and native analysis can take minutes.
Logs
The bridge writes JSON lines to stdout with "service":"rea-bridge", an uppercase level and an ISO time. Each request carries a request_id, each new session a connection_id, and session events a session_id. The bridge never logs request bodies, and the pino redaction list in src/logger.ts replaces authorization headers, cookies, tokens, secrets, passwords and API keys with [REDACTED].
The opt-in tool installer logs the same way to stderr with "service":"rea-install".
On SIGTERM or SIGINT the bridge closes every session, stops their REA processes and exits.