---
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 <tunnel-name>` 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.*
