--- title: Connecting to the State Hub — Reference --- # Connecting to the State Hub — Reference How Claude Code agents on local and remote machines connect to the State Hub API and its MCP server. --- ## Architecture overview The State Hub runs on the **work laptop** only. Remote machines (COULOMBCORE, Railiance nodes) never run their own copy — they connect to the single source of truth via an encrypted SSH reverse tunnel managed by **ops-bridge**. ``` Work laptop Remote machine (e.g. COULOMBCORE) ───────────────────── ───────────────────────────────── PostgreSQL :5432 Claude Code session ↑ │ FastAPI :8000 ←── ops-bridge ────→ :18000 (API health / tools) MCP SSE :8001 ←── ops-bridge ────→ :18001 (MCP for Claude Code) ``` Two services are exposed through the tunnel: | Service | Local port | Remote port | Purpose | |---------|-----------|-------------|---------| | State Hub API | `8000` | `18000` | Health checks, direct curl queries | | MCP SSE server | `8001` | `18001` | Claude Code MCP integration | --- ## Local setup (work laptop) ### Start the services ```bash cd ~/state-hub make api # FastAPI on :8000 make mcp-http # MCP SSE server on :8001 (separate terminal) ``` `make mcp-http` sets `MCP_TRANSPORT=sse MCP_PORT=8001` and starts the same MCP server that Claude Code uses locally in stdio mode. The SSE mode exposes the identical tool surface over HTTP. ### Start the tunnels ```bash bridge up # brings up both tunnels defined in ~/.config/bridge/tunnels.yaml bridge status ``` Both tunnels must show `connected` (or `degraded` — see below) before remote agents can connect. ### Local Claude Code registration (stdio, default) Claude Code on the work laptop uses the stdio MCP server. Registration is in `~/.claude.json` via `.mcp.json` at the repo root. No changes needed for local use. --- ## Remote setup (COULOMBCORE or any remote host) ### Prerequisites - ops-bridge tunnels running on the work laptop (`bridge status`) - SSH key `~/.ssh/id_ops` authorised on the remote host for user `tegwick` ### One-time MCP registration on the remote ```bash claude mcp add-json -s user state-hub \ '{"type":"sse","url":"http://127.0.0.1:18001/sse"}' ``` Restart Claude Code after running this. That's the only setup required — no Python, no repo clone, no local services. ### Verify connectivity ```bash # API reachable through tunnel? curl -s http://127.0.0.1:18000/state/health # → {"status":"ok","db":"connected"} # MCP SSE endpoint reachable? curl -s --max-time 2 http://127.0.0.1:18001/sse | head -2 # → event: endpoint # → data: /messages/?session_id=... ``` --- ## ops-bridge tunnel config Tunnels are defined in `~/.config/bridge/tunnels.yaml` on the work laptop: ```yaml tunnels: state-hub-coulombcore: # API tunnel host: 92.205.130.254 remote_port: 18000 local_port: 8000 ssh_user: tegwick ssh_key: ~/.ssh/id_ops actor: agent.claude-coulombcore health_check: url: http://127.0.0.1:18000/state/health interval_seconds: 30 timeout_seconds: 5 reconnect: max_attempts: 0 # retry forever backoff_initial: 5 backoff_max: 60 state-hub-mcp-coulombcore: # MCP SSE tunnel host: 92.205.130.254 remote_port: 18001 local_port: 8001 ssh_user: tegwick ssh_key: ~/.ssh/id_ops actor: agent.claude-coulombcore health_check: url: http://127.0.0.1:18001/sse interval_seconds: 30 timeout_seconds: 5 reconnect: max_attempts: 0 backoff_initial: 5 backoff_max: 60 state-hub-railiance01: # API tunnel host: 92.205.62.239 remote_port: 18000 local_port: 8000 ssh_user: tegwick ssh_key: ~/.ssh/id_ops actor: agent.claude-railiance01 health_check: url: http://127.0.0.1:8000/state/health interval_seconds: 30 timeout_seconds: 5 reconnect: max_attempts: 0 backoff_initial: 5 backoff_max: 60 state-hub-mcp-railiance01: # MCP SSE tunnel host: 92.205.62.239 remote_port: 18001 local_port: 8001 ssh_user: tegwick ssh_key: ~/.ssh/id_ops actor: agent.claude-railiance01 health_check: url: http://127.0.0.1:18001/sse interval_seconds: 30 timeout_seconds: 5 reconnect: max_attempts: 0 backoff_initial: 5 backoff_max: 60 ``` ops-bridge source: `~/ops-bridge` · SSH key: `~/.ssh/id_ops` --- ## Bridge states | State | Meaning | |-------|---------| | `connected` | SSH process alive, health check passing | | `degraded` | SSH process alive, health check failing (SSE endpoint streams — not always a real error) | | `reconnecting` | SSH dropped, backoff loop active | | `stopped` | Not started or manually stopped | The MCP SSE tunnel often shows `degraded` because the `/sse` health check receives a streaming response rather than a clean 200 — the tunnel is still functional. Confirm with `curl http://127.0.0.1:18001/sse` from the remote. --- ## MCP transport modes The MCP server (`state-hub/mcp_server/server.py`) supports two transports selected by environment variable: | Variable | Default | Effect | |----------|---------|--------| | `MCP_TRANSPORT` | `stdio` | stdio transport (local Claude Code) | | `MCP_TRANSPORT=sse` | — | SSE/HTTP transport for remote clients | | `MCP_PORT` | `8001` | Port for SSE mode | | `API_BASE` | `http://127.0.0.1:8000` | State Hub API URL the MCP server calls | On a remote machine the MCP server process runs locally inside Claude Code — it does not run on the remote host. The transport layer (SSE over the tunnel) handles the connection. --- ## Adding a new remote host 1. Generate or reuse an SSH key pair (recommend `~/.ssh/id_ops`) 2. Add the public key to the remote host's `~/.ssh/authorized_keys` 3. Check port availability on the remote: `ssh user@host "ss -tlnp | grep 18001"` 4. Add two tunnel entries to `~/.config/bridge/tunnels.yaml` (API + MCP) 5. `bridge up ` for each 6. On the remote: `claude mcp add-json -s user state-hub '{"type":"sse","url":"http://127.0.0.1:18001/sse"}'` --- *The State Hub runs on the work laptop only. Remote machines are read-only consumers connected via tunnel — they never own a copy of the database.*