6.4 KiB
title
| 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
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
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_opsauthorised on the remote host for usertegwick
One-time MCP registration on the remote
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
# 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:
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
- Generate or reuse an SSH key pair (recommend
~/.ssh/id_ops) - Add the public key to the remote host's
~/.ssh/authorized_keys - Check port availability on the remote:
ssh user@host "ss -tlnp | grep 18001" - Add two tunnel entries to
~/.config/bridge/tunnels.yaml(API + MCP) bridge up <tunnel-name>for each- 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.