A remote Model Context Protocol (MCP) server deployed on Cloudflare Workers. It provides persistent, long-term memory for AI agents and assistants, backed by Cloudflare D1 (relational SQL) and Cloudflare Vectorize (vector database for semantic search).
- Standard MCP Over HTTP: Utilizes
@modelcontextprotocol/serverwithcreateMcpHandler, supporting both modern (2026-07-28) protocol revision and legacy (2025/2024-11-05) clients natively compatible with Cloudflare Workers'fetchhandler. Served at/and/mcp. - Secure Authentication: Constant-time Bearer token verification via the
Authorization: Bearer <API_AUTH_TOKEN>header (fails closed if the secret is missing). - Edge Performance: Powered by Cloudflare Workers for ultra-low latency worldwide.
- Hybrid Recall: Semantic search (Workers AI
@cf/baai/bge-base-en-v1.5+ Vectorize) fused with keyword search (D1 FTS5) via Reciprocal Rank Fusion. Keyword search also covers the few seconds before new vectors become queryable. - Dedup on Write: Facts ≥ 0.95 similar to an existing memory are merged (tags added) instead of duplicated.
- Token-Efficient: Terse tool descriptions, short integer IDs (
#42), and compact one-line results. - Hardened HTTP: CORS + optional origin allow-list,
/healthprobe, JSON-RPC shaped errors,405for unsupported methods. - Hybrid Storage Architecture:
- Cloudflare D1: Stores raw memory text, tags, timestamps, and the FTS5 keyword index.
- Cloudflare Vectorize: Enables vector similarity search over stored insights.
All results use one compact line per memory:
#42 .87 2026-10-19 [pref,ui] User prefers dark mode in editors
│ │ │ │ └ text (newlines shown as ↵)
│ │ │ └ tags
│ │ └ last updated (UTC date)
│ └ similarity (recall only; "kw" = keyword-only match)
└ memory id
| Tool | Inputs | Notes |
|---|---|---|
remember_fact |
fact (≤4000 chars), tags? (≤10, ≤32 chars each) |
Returns Saved #id or Exists #id (score) when merged into a near-duplicate. |
recall_memories |
query (≤500 chars), tags? (match any), limit? (1–20, default 5) |
Hybrid semantic + keyword search. Read-only. |
list_memories |
tags?, limit? (1–50, default 20), cursor? |
Newest first. Pass the trailing next: N value as cursor for the next page. Read-only. |
update_memory |
id, fact?, tags? (replaces all) |
Re-embeds when text changes. |
forget_memory |
ids (1–50) |
Permanent. Reports deleted and not-found ids. |
Tags are normalized to lowercase with spaces converted to hyphens. Tools carry MCP annotations (readOnlyHint, destructiveHint, …) so clients can auto-approve reads and confirm deletes.
memory://tags: every tag with its memory count.memory://tags/{tag}: memories with that tag (listable, with tag autocompletion).
recall_context(topic): runs the search server-side and injects relevant memories directly into the conversation, saving a tool round-trip.
remote-mcp-memory/
├── src/
│ ├── index.ts # Worker entry point: routing, CORS, auth, transport
│ ├── mcp.ts # MCP tools, resources & prompts
│ ├── store.ts # D1 + Vectorize + Workers AI data layer, hybrid search
│ ├── http.ts # Auth, CORS & JSON error helpers
│ └── env.ts # Bindings interface & server identity
├── migrations/
│ └── 0001_init.sql # D1 schema (memories, tags, FTS5 index + triggers)
├── wrangler.toml # Cloudflare Worker configuration & resource bindings
├── worker-configuration.d.ts # Generated TypeScript types for Cloudflare bindings
├── tsconfig.json # TypeScript compiler configuration
├── package.json # Dependencies and development scripts
├── .node-version # Node.js runtime version pinned to v24
├── .dev.vars.example # Template for local environment secrets
├── .gitignore # Git ignore rules
└── README.md # Project documentation
- Node.js (v22+ required, v24+ recommended)
- A Cloudflare account
- Wrangler CLI authenticated (
npx wrangler login)
npm installCreate the D1 database:
npx wrangler d1 create mcp-memory-dbCopy the resulting database_id into wrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "mcp-memory-db"
database_id = "<your-d1-database-id>"Apply the schema migrations:
npm run db:migrate # remote (production) database
npm run db:migrate:local # local database used by `wrangler dev`Create a Vectorize index. The dimensions must be 768 to match the @cf/baai/bge-base-en-v1.5 embedding model:
npx wrangler vectorize create mcp-memory-index --dimensions=768 --metric=cosineNote: An index's dimensions can't be changed. If you created it with a different size, delete and recreate it:
npx wrangler vectorize delete mcp-memory-index.
Confirm the binding matches wrangler.toml:
[[vectorize]]
binding = "VECTORIZE"
index_name = "mcp-memory-index"No setup is needed beyond the [ai] binding already in wrangler.toml. Vectorize and Workers AI have no local simulator, so wrangler dev uses the real remote resources (remote = true) while D1 stays local.
You can generate a cryptographically secure 256-bit random token using either OpenSSL or Node.js:
# Using OpenSSL (recommended on Linux/macOS)
openssl rand -hex 32
# Or using Node.js (cross-platform)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Copy the provided example secrets template to .dev.vars (this file is ignored by Git) and customize your token:
cp .dev.vars.example .dev.varsYour .dev.vars file should contain:
API_AUTH_TOKEN="your-super-secure-shared-secret-token"Store the secret securely in Cloudflare:
npx wrangler secret put API_AUTH_TOKENWhen prompted, enter your secret token value. Cloudflare encrypts the secret and injects it into your Worker's runtime environment (env.API_AUTH_TOKEN).
Security Note: Never commit actual production API keys or tokens into
wrangler.toml. All secrets must be managed via Cloudflare Secrets in production or.dev.varslocally.
Non-browser MCP clients send no Origin header and are unaffected. To limit browser-based clients, set a comma-separated allow-list in wrangler.toml:
[vars]
ALLOWED_ORIGINS = "https://claude.ai,https://chatgpt.com"Search thresholds live at the top of src/store.ts: MIN_SIMILARITY (0.55, drops weak semantic hits) and DUPLICATE_SIMILARITY (0.95, merge threshold).
Start the local development server:
npm run devWrangler will launch a local server (typically at http://localhost:8787). Check it with curl http://localhost:8787/health.
Validate TypeScript types:
npm run typecheckGenerate TypeScript definitions for bindings configured in wrangler.toml into worker-configuration.d.ts:
npm run cf-typegenDeploy the worker to your Cloudflare account:
npm run deployTo connect an AI assistant or MCP client to your remote memory server, configure the client with your worker URL and the authorization header:
{
"mcpServers": {
"remote-memory": {
"url": "https://remote-mcp-memory.<your-subdomain>.workers.dev/mcp",
"headers": {
"Authorization": "Bearer your-super-secure-shared-secret-token"
}
}
}
}MIT