Cloudflare Memory MCP
by ady133t
README.md
# Cloudflare Memory MCP
A per-project memory MCP server for opencode, hosted entirely on Cloudflare's **free tier**:
- **Notes** — plain keyed memories with tags and types
- **Vector** — semantic recall via [Vectorize](https://developers.cloudflare.com/vectorize/) (embeddings from Workers AI `@cf/baai/bge-m3`, 1024-dim, multilingual)
- **Graph** — entities + relations in [D1](https://developers.cloudflare.com/d1/) (SQLite) with recursive traversal
- **Web dashboard** — open `/` in a browser to see every project with memories. Each project card opens a detail view with an interactive force-directed graph of its entities and relations (wheel = zoom, drag = pan, double-click or buttons = reset, hover = highlight neighbors) and a **Delete project** button that removes the project, its notes, graph, FTS rows, caches, and vector embeddings
```
opencode ── MCP (streamable HTTP + Bearer auth) ──> Worker
├─ Vectorize memory-vectors (namespace per project)
├─ D1 projects / notes / nodes / edges / FTS5
├─ Workers AI bge-m3 embeddings
└─ KV recent-memories cache
```
## Screenshots
### Project List
The dashboard lists every project known to the account, including memory, entity,
relation, and last-active counts.
<p align="center">
<img src="docs/screenshots/project-list.png" alt="Cloudflare Memory MCP project list" width="100%" />
</p>
### Knowledge Graph
Open a project to inspect its memories and graph. The graph supports curved,
directional edges, hover highlighting, wheel zoom, drag panning, and reset controls.
<p align="center">
<img src="docs/screenshots/knowledge-graph.png" alt="Cloudflare Memory MCP knowledge graph" width="100%" />
</p>
## How folder isolation works
The server runs its MCP sessions inside a **Durable Object** (free tier, SQLite
backend), which pins every message for a session to one isolate. Project identity
is resolved in this order:
1. **`project_root` tool argument** (recommended) — you pass your working folder
to any tool, e.g. `remember({ project_root: "D:/Projects/foo", content: ... })`.
The server hashes a normalized form of the path into a stable project id and
**remembers it for the rest of the session**, so later calls can omit it. The
`identify_project` tool exists for explicitly locking it in.
2. **`project_id` tool argument** — an explicit id (also cached for the session).
3. **`x-project-root` / `x-project-id` headers** — for clients that configure them.
4. **MCP roots** — automatic folder detection when the client supports it.
Because the identity is just a hash of the normalized path, the same folder opened
from a different absolute path still resolves to the same project. Every read and
write is scoped to that id — Vectorize queries use a namespace-per-project and
every D1 query filters on `project_id`, so two folders can never see each other's
memory.
### Using it from opencode
opencode knows its working directory (your `bash` cwd). The agent should pass it
once per session. This repo ships a ready-to-copy template at **`AGENTS.md`** —
copy it into any project you want memory in, set `PROJECT_ROOT` to that project's
directory, and the agent will handle identity automatically:
```
When using the memory MCP tools, pass project_root (your bash cwd) on the first
memory call of the session.
```
## Setup
Prereqs: Node 18+, a Cloudflare account, and `npx wrangler login` done once.
```sh
npm install
npm run setup # creates D1 db, KV namespace, Vectorize index; patches wrangler.toml
npm run db:migrate:remote
npx wrangler secret put MEMORY_AUTH_TOKEN # optional but strongly recommended
npm run deploy
```
Your endpoints:
- MCP: `https://cloudflare-memory-mcp.<your-subdomain>.workers.dev/mcp`
- Dashboard: `https://cloudflare-memory-mcp.<your-subdomain>.workers.dev/`
Local development:
```sh
npm run dev # wrangler dev --local (AI + Vectorize are remote-only)
npm run db:migrate:local
```
## Wiring into opencode
One entry — the same config works in every project. No per-project values needed:
```jsonc
// ~/.config/opencode/opencode.jsonc
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"memory": {
"type": "remote",
"url": "https://cloudflare-memory-mcp.<your-subdomain>.workers.dev/mcp",
"oauth": false,
"headers": {
"Authorization": "Bearer {env:MEMORY_AUTH_TOKEN}"
}
}
}
}
```
Project identity is then driven by the agent itself: it passes its working folder
as `project_root` on the first memory call of each session (see the AGENTS.md tip
above), and the server scopes everything to that folder from then on.
The dashboard (`/`) prompts for the same `MEMORY_AUTH_TOKEN` once and stores it in
localStorage. If you leave `MEMORY_AUTH_TOKEN` unset, everything is public — only
do that for personal/throwaway experiments.
## MCP tools
| Tool | Purpose |
|---|---|
| `identify_project` | Lock the project identity for the session from a `project_root` / `project_id` (returns the resolved id) |
| `remember` | Save a note (embeds it for semantic recall); optionally upserts `entities` + `relations` into the graph |
| `query_memory` | Semantic/vector search — recall by meaning. Note: Vectorize mutations are async, so a note is queryable semantically a few seconds after `remember`; use `search_memory` for instant recall |
| `search_memory` | Exact keyword search (D1 FTS5) |
| `get_recent_memories` | Latest notes for the project |
| `get_project_context` | Bundle stats + recent memories + top entities + recent relations (call explicitly) |
| `add_entity` / `add_relation` | Build the knowledge graph deliberately |
| `query_graph` | Neighbors of an entity (depth 1–3) or most-connected entities |
| `forget` | Delete a note + its embedding |
All tools accept an optional `project_root` (folder path or `file://` URI) and
`project_id`; once either is supplied in a session it is remembered.
## Free-tier budget
| Resource | Free allowance | Notes |
|---|---|---|
| Durable Object | 100k requests/day, ~28h/day active compute | Hosts MCP sessions; ~1 request per MCP message |
| Vectorize | **5M stored dims** account-wide | ~4,800 embedded notes @1024-dim; prune with `forget`. Queries ~29k/mo |
| Workers AI | 10k neurons/day | bge-m3 embedding of a 1k-token note ≈ 1 neuron → thousands/day |
| D1 | 5 GB / 500 MB per DB | notes + graph + FTS are effectively unlimited for personal use |
| KV / Worker | 1 GB / 100k req/day | caches + serving; a few requests per opencode session |
Design levers if you ever approach the vector ceiling: embed only durable memories
(`remember` with `type: "decision" | "fact"`) and let `search_memory` (FTS5, uncapped)
hold the long tail; or recreate the index with `bge-small` (384-dim, English-only)
for ~3× capacity.
## Project layout
```
src/index.ts fetch router: dashboard, /api/*, /mcp -> Durable Object
src/session-do.ts MCP session host (Durable Object): sessions, SSE, roots, identity
src/tools.ts the 10 tool definitions (zod schemas + implementations)
src/store.ts D1 / Vectorize / KV data layer
src/identity.ts project-id derivation from folder roots
src/embed.ts Workers AI bge-m3 embeddings
src/api.ts dashboard API (projects, detail, graph, delete)
src/web.ts dashboard page (token-gated)
migrations/ D1 schema
scripts/setup.mjs creates Cloudflare resources and patches wrangler.toml
AGENTS.md template to copy into projects that use this memory server
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues