Skip to main content
Glama
bellfireg

hybrid-memory-mcp

by bellfireg
README.md
# hybrid-memory-mcp

Universal FastMCP bridge into a hybrid memory stack. Any MCP-capable agent harness
(Claude Code, Codex, RooCode, OpenClaw, Pi) adds one streamable-HTTP URL + bearer
token and gains four tools against the SAME memory the host agent uses.

- **OpenViking** (primary, read+write) — `mem_save`, `mem_search`, `mem_read`
- **Honcho** (secondary, read-only reasoning) — `mem_recall`

## Endpoint

    URL:    http://<HOST>:8899/mcp        (bind a private LAN/VPN IP — not public)
    Auth:   Authorization: Bearer <HMCP_BEARER_TOKEN from .env>
    Transport: streamable-http

`<HOST>` is set via `HMCP_BIND_HOST` in `.env` (defaults to `127.0.0.1`). Bind it
to a private tailnet/LAN IP so only trusted machines on your network can reach it —
this server is not designed for public internet exposure.

## Tools

| tool         | args                                          | backend                    |
|--------------|-----------------------------------------------|----------------------------|
| `mem_save`   | content (req), category, source_agent         | OV disk-write + async index|
| `mem_search` | query (req), limit=8                           | OV `/search/search`        |
| `mem_read`   | uri (req, must start `viking://user/<user>/`)  | OV `/content/read`         |
| `mem_recall` | query (req)                                    | Honcho `/chat` (read-only) |

`category` enum: `pattern` | `entity` | `event` (default) | `preference`.
Every saved fact is footer-tagged `[via: <source_agent>]` for cross-harness attribution.

## Configuration (`.env`, chmod 0600)

    HMCP_BEARER_TOKEN=<generate: python3 -c "import secrets;print('hmcp_'+secrets.token_urlsafe(32))">
    HMCP_BIND_HOST=127.0.0.1        # set to your private tailnet/LAN IP
    HMCP_BIND_PORT=8899
    OPENVIKING_ENDPOINT=http://127.0.0.1:1933
    HONCHO_BASE_URL=http://localhost:8000
    HONCHO_WORKSPACE=hermes
    HONCHO_PEER=<your-peer-id>

## Client config

Replace `<TOKEN>` with the value of `HMCP_BEARER_TOKEN`, and `<HOST>` with your bind IP.

### Claude Code

    claude mcp add --transport http hybrid-memory http://<HOST>:8899/mcp \
      --header "Authorization: Bearer <TOKEN>"

### Codex (`~/.codex/config.toml`)

    [mcp_servers.hybrid_memory]
    url = "http://<HOST>:8899/mcp"
    http_headers = { Authorization = "Bearer <TOKEN>" }

### Generic streamable-http (RooCode / OpenClaw / Pi / any MCP client)

    {
      "mcpServers": {
        "hybrid-memory": {
          "type": "streamable-http",
          "url": "http://<HOST>:8899/mcp",
          "headers": { "Authorization": "Bearer <TOKEN>" }
        }
      }
    }

## Run

    python3 -m venv venv && ./venv/bin/pip install -r requirements.txt
    ./venv/bin/python server.py     # binds $HMCP_BIND_HOST:$HMCP_BIND_PORT, mounts /mcp

For production, run under a supervisor with restart-on-failure (systemd unit,
`Restart=always`).

## Checks

    bash specs/hybrid-memory-mcp/checks/roundtrip.sh    # C3 save->search->read (server stopped)
    bash specs/hybrid-memory-mcp/checks/validation.sh   # C6 input-validation (server stopped)
    bash specs/hybrid-memory-mcp/checks/auth.sh         # C4 bearer enforced (spins ephemeral server)
    bash specs/hybrid-memory-mcp/checks/mcp_handshake.sh # C8 MCP initialize handshake
    bash specs/hybrid-memory-mcp/checks/restart.sh      # C7 supervised persistence (after unit installed)

## Design notes

- **Async index waiting-list.** OV's `content/write` runs the semantic+vector index
  inline behind one global write lock (~40s/write). `mem_save` does the durable part
  synchronously (write the file to disk — the memory is persisted and recoverable
  immediately) and hands the slow index to a background worker that drains one job at
  a time. Saves return in <10ms; search visibility follows asynchronously.
- **Crash-durable queue.** Each queued index job drops a pending-marker on disk; the
  worker deletes it after a successful index; on startup any survivors are re-queued.
  A `kill -9` with items in flight loses nothing — markers reconcile on restart, and
  the worker also re-enqueues on transient failure so it self-heals once OV recovers.
- **OV 0.3.8 create-mode bug.** `content/write` cannot CREATE new files, so `mem_save`
  writes the file to the local store first, then `content/write mode=replace` to index.
- **Graceful degradation.** `mem_recall` returns `{"status": "honcho unavailable", ...}`
  instead of crashing when Honcho is down or slow.