@cerefox/memory 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1428 @@
1
+ # Connecting AI Agents to Cerefox
2
+
3
+ Cerefox exposes your knowledge base through two access paths. Choose the one that fits your
4
+ client; you can also run both in parallel.
5
+
6
+ > **OpenAI API key — known glitch (all paths):** The simplest setup is an **unrestricted**
7
+ > OpenAI API key — it just works. If you prefer a restricted key and hit a
8
+ > `Missing scopes: model.request` or 401 error despite the key looking correct in the
9
+ > dashboard, this is a [known OpenAI UI bug](https://community.openai.com/t/missing-scopes-model-request-on-restricted-api-key/1371602):
10
+ > narrowing sub-scopes after setting the top-level **Model Capabilities → Write** permission
11
+ > corrupts the internal permission state silently. The fix is either to switch to an
12
+ > unrestricted key, or to open the key in the
13
+ > [OpenAI dashboard](https://platform.openai.com/api-keys), save it without any changes, and
14
+ > retry — this resets the internal state immediately.
15
+ >
16
+ > This applies to all paths (A-Local, A-Remote, Path B) — any path that calls the OpenAI
17
+ > embedding API can be affected. If you're on Fireworks AI instead, see
18
+ > `docs/guides/configuration.md` → "Changing the embedding model".
19
+
20
+ ---
21
+
22
+ ## Access paths at a glance
23
+
24
+ Three top-level paths plus a few special cases:
25
+
26
+ - **Path A** — MCP server (local subprocess or remote Edge Function). Best for purpose-built agent clients like Claude Desktop, Cursor, and Claude Code's MCP integration.
27
+ - **Path B** — direct Edge Function HTTP. Best for ChatGPT Custom GPTs and any HTTP caller (curl, scripts).
28
+ - **Path C** — local shell CLI invoked by a coding agent's Bash tool. Best for Claude Code, Codex CLI, opencode, OpenClaw, Hermes, and similar local-agent CLIs **when the user prefers not to configure MCP** but still wants the agent to read and write Cerefox.
29
+
30
+ | Client | Path | Search | Requirements / caveats |
31
+ |--------|------|--------|-----------------------|
32
+ | Claude Desktop (remote) | Path A-Remote — `cerefox-mcp` Edge Function | Hybrid | Node.js for `npx supergateway` or `npx mcp-remote`; no Python needed |
33
+ | Claude Code (remote) | Path A-Remote — `cerefox-mcp` Edge Function | Hybrid | URL + anon key only; no local install |
34
+ | Cursor (remote) | Path A-Remote — `cerefox-mcp` Edge Function | Hybrid | URL + anon key only; no local install |
35
+ | OpenAI Codex CLI (remote) | Path A-Remote — `cerefox-mcp` Edge Function | Hybrid | URL + anon key env var; TOML config |
36
+ | ChatGPT (chatgpt.com or desktop) | Path B — Custom GPT → Edge Functions | Hybrid | ChatGPT Plus required |
37
+ | Claude Desktop (local) | Path A-Local — `@cerefox/memory` via `npx` (recommended) or `cerefox mcp` (Python fallback) | Hybrid | Local alternative; Node.js (npm path) or Python + uv + local clone (legacy path); zero Edge Function invocations |
38
+ | Claude Code (local) | Path A-Local — `@cerefox/memory` via `npx` or `cerefox mcp` | Hybrid | Local alternative; zero Edge Function invocations |
39
+ | Cursor (local) | Path A-Local — `@cerefox/memory` via `npx` or `cerefox mcp` | Hybrid | Local alternative; zero Edge Function invocations |
40
+ | Cloud Claude (claude.ai web) | Remote Supabase MCP | FTS only | No install; search quality limited |
41
+ | Gemini CLI (remote) | Path A-Remote — `cerefox-mcp` Edge Function | Hybrid | URL + anon key only; no local install |
42
+ | Local coding agents (Claude Code, Codex CLI, opencode, OpenClaw, Hermes, …) | Path C — Shell CLI (Bash tool) | Hybrid | Local clone + `uv`; agent runs `uv run cerefox …` as a shell command. Useful when MCP setup is friction. |
43
+ | curl / scripts | Path B — Edge Functions directly | Hybrid | Direct HTTP; no client needed |
44
+ | Custom Python agents | Python SDK directly | Hybrid | Local Python required |
45
+
46
+ > **"Hybrid"** = FTS + semantic, document-level (complete reconstructed notes, not isolated chunks).
47
+ > **"FTS only"** = keyword search only; no semantic/vector search.
48
+
49
+ > **Cloud hybrid for all clients (future)**: deploying the MCP server to Cloud Run would give
50
+ > cloud clients (claude.ai, chatgpt.com) full hybrid search. Tracked in `docs/TODO.md`.
51
+
52
+ > **Perplexity** supports stdio-only MCP on macOS Desktop (via Helper App). Remote MCP is
53
+ > "coming soon." Perplexity's CTO has signalled a strategic shift away from MCP (March 2026),
54
+ > so API-based integration may be the long-term path. Not a priority.
55
+ >
56
+ > **Gemini web** (gemini.google.com) does not support custom MCP servers. No integration path.
57
+
58
+ > **Quick start with templates:** Copy-pasteable `.mcp.json` templates for each client are
59
+ > available in [`examples/mcp-configs/`](../examples/mcp-configs/). Pick the one for your
60
+ > client, replace the placeholders, and you're connected.
61
+
62
+ ---
63
+
64
+ ## Prerequisites
65
+
66
+ **For all paths:**
67
+ - Supabase project set up and schema deployed (see `setup-supabase.md`)
68
+ - Some content ingested (`cerefox ingest my-notes.md`)
69
+
70
+ **For Path A-Local only:**
71
+ - **Recommended (v0.4.0+):** [Node.js ≥20](https://nodejs.org) (for `npx --package=@cerefox/memory cerefox-mcp`)
72
+ + `.env` file in the working directory the client launches the server from (see "env block"
73
+ in the per-client configs below if your client can't see the file)
74
+ - **Alternative:** [`uv`](https://docs.astral.sh/uv/getting-started/installation/) installed on your machine + Cerefox repository cloned locally (e.g. `/Users/yourname/src/cerefox`)
75
+ + `.env` in the checkout
76
+ - Either way, `.env` must define `CEREFOX_SUPABASE_URL`, `CEREFOX_SUPABASE_KEY`, and your
77
+ embedding API key (`OPENAI_API_KEY`)
78
+
79
+ > **Important — which anon key to use (2026):** Path A-Remote and Path B both require an
80
+ > "anon key" as a Bearer token. As of 2026, you **must** use the **legacy anon JWT**
81
+ > (`eyJ…`) — the new `sb_publishable_…` key is rejected by the Supabase Edge Function
82
+ > gateway with `UNAUTHORIZED_INVALID_JWT_FORMAT`. Find the legacy key in **Project
83
+ > Settings → API Keys → Legacy → anon**. This is a Supabase platform constraint;
84
+ > see [`setup-supabase.md` → Supabase API keys (2026)](setup-supabase.md#supabase-api-keys-2026)
85
+ > for the full story.
86
+
87
+ **For Path A-Remote (remote MCP Edge Function) — recommended:**
88
+ - `cerefox-mcp` Edge Function deployed (`npx supabase functions deploy cerefox-mcp`)
89
+ - Your **legacy anon JWT** (see callout above): Supabase Dashboard → Project Settings → API Keys → Legacy → anon
90
+ - For Claude Desktop: [Node.js](https://nodejs.org) installed (for `npx supergateway` or `npx mcp-remote`)
91
+ - For Claude Code: [Node.js](https://nodejs.org) for `npx mcp-remote` (recommended), or no extra deps for native HTTP
92
+
93
+ **For Path B (Edge Functions / GPT Actions) only:**
94
+ - Supabase Edge Functions deployed: `cerefox-search`, `cerefox-ingest`, `cerefox-metadata`,
95
+ `cerefox-get-document`, `cerefox-list-versions`, `cerefox-get-audit-log`,
96
+ `cerefox-metadata-search`, `cerefox-list-projects` --
97
+ see `setup-supabase.md` for the deploy procedure (`npx supabase functions deploy`)
98
+ - Your **legacy anon JWT** (see callout above): Supabase Dashboard → Project Settings → API Keys → Legacy → anon
99
+ - Your **project ref**: visible in the Supabase Dashboard URL
100
+ (`app.supabase.com/project/<project-ref>`)
101
+
102
+ **For cloud Claude.ai only:**
103
+ - A **Personal Access Token** (PAT): create at `https://supabase.com/dashboard/account/tokens`
104
+
105
+ ---
106
+
107
+ ## Path A-Local — Local MCP server
108
+
109
+ ### What it is
110
+
111
+ The local Cerefox MCP server runs on your machine and exposes the same 10 tools as the remote
112
+ Edge Function, communicating with clients over stdio.
113
+
114
+ As of **v0.4.0** the local server ships as an npm package — **[`@cerefox/memory`](https://www.npmjs.com/package/@cerefox/memory)** — built with the official `@modelcontextprotocol/sdk`.
115
+ The bin entry is `cerefox-mcp`. The recommended client config is `npx -y --package=@cerefox/memory cerefox-mcp`.
116
+
117
+ The legacy `uv run cerefox mcp` invocation **still works** and is preserved as a soft
118
+ wrapper: it tries `npx --no-install @cerefox/memory cerefox-mcp` first and falls back to the
119
+ Python MCP server if npm is unavailable or `@cerefox/memory` isn't installed. New users
120
+ should prefer the npm-native config; existing users don't have to change anything.
121
+
122
+ - Embeddings are computed locally using your `.env` key (no extra credentials)
123
+ - Works offline except for the OpenAI embedding API call per query
124
+ - One setup, all compatible local clients (Claude Desktop, Cursor, Claude Code, Codex CLI, …)
125
+
126
+ See [`docs/guides/migration-v0.4.md`](migration-v0.4.md) for before/after config snippets
127
+ per client.
128
+
129
+ > **Why not `mcp-server-fetch`?** The generic fetch MCP only supports GET requests and cannot
130
+ > make authenticated POST calls to the Edge Functions. The built-in local server is
131
+ > the correct solution.
132
+
133
+ ### Path A MCP tools
134
+
135
+ Once configured, every Path A client has these tools:
136
+
137
+ | Tool | Description |
138
+ |------|-------------|
139
+ | `cerefox_search` | Hybrid (FTS + semantic) document-level search. Filter by `project_name` or `metadata_filter`. |
140
+ | `cerefox_ingest` | Save a note or document to the knowledge base. Pass `document_id` to update by ID (deterministic); or `update_if_exists: true` to update by title match. Accepts optional `author` and `project_name`. |
141
+ | `cerefox_list_metadata_keys` | List all metadata keys in use across documents |
142
+ | `cerefox_get_document` | Retrieve the full content of a document (current or archived version) |
143
+ | `cerefox_list_versions` | List all archived versions of a document |
144
+ | `cerefox_get_audit_log` | Query audit log entries with filters (document, author, operation, time range) |
145
+ | `cerefox_list_projects` | List all projects with names and IDs. Use for discovering available projects. |
146
+ | `cerefox_metadata_search` | Find documents by metadata key-value criteria without a text search term. Supports project, date, and content filters. |
147
+ | `cerefox_set_document_projects` | Set a document's project memberships to exactly the given list (destructive replace; metadata-only, no content change). Use `cerefox_ingest` with singular `project_name` for non-destructive "add". |
148
+ | `cerefox_get_help` | Retrieve Cerefox conventions (the same content as `AGENT_QUICK_REFERENCE.md`) over MCP. Optional `topic` parameter does a case-insensitive H2 substring match. Call this whenever you are uncertain. |
149
+
150
+ > All 10 tools are available on both Path A (local and remote MCP) and Path B (GPT Actions
151
+ > via dedicated Edge Functions, except `cerefox_get_help` which is MCP-only). MCP tools use
152
+ > `project_name` (human-readable); primitive Edge Functions (Path B) use `project_id` (UUID).
153
+
154
+ ### Path A system prompt
155
+
156
+ Set this as Custom Instructions / System Prompt in your client:
157
+
158
+ ```
159
+ You have access to a personal knowledge base via Cerefox MCP tools.
160
+ When answering questions, always call cerefox_search first with a relevant query.
161
+ Cite doc_title for every claim drawn from the knowledge base.
162
+ Use cerefox_ingest to save anything the user asks you to remember.
163
+ Always set your requestor/author parameter to identify yourself.
164
+ For the full tool reference, search Cerefox for "How AI Agents Use Cerefox".
165
+ ```
166
+
167
+ > **Agent reference docs**: `AGENT_GUIDE.md` (comprehensive) and `AGENT_QUICK_REFERENCE.md` (quick
168
+ > reference) in the repo root contain the full tool reference for AI agents. These are also
169
+ > ingested into the Cerefox KB via `sync_docs.py`, so agents can find them by searching.
170
+
171
+ ### Path A verification prompts
172
+
173
+ After setup, ask your client:
174
+
175
+ > "What tools do you have available?"
176
+ > Expected: 10 tools listed (`cerefox_search`, `cerefox_ingest`, `cerefox_get_document`,
177
+ > `cerefox_list_versions`, `cerefox_list_projects`, `cerefox_list_metadata_keys`,
178
+ > `cerefox_metadata_search`, `cerefox_set_document_projects`, `cerefox_get_audit_log`,
179
+ > `cerefox_get_help`).
180
+
181
+ > "Use cerefox_search with query='second brain' and match_count=3. What did you find?"
182
+
183
+ > "Save a note titled 'Test Note' with content '# Test\nThis is a test.' using cerefox_ingest."
184
+
185
+ > "Call cerefox_get_help with no topic. What sections are listed?"
186
+
187
+ ---
188
+
189
+ ### Claude Desktop
190
+
191
+ **Config file location:**
192
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
193
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
194
+
195
+ **Recommended — npm (`@cerefox/memory`, v0.4.0+):**
196
+
197
+ ```json
198
+ {
199
+ "mcpServers": {
200
+ "cerefox": {
201
+ "command": "npx",
202
+ "args": ["-y", "--package=@cerefox/memory", "cerefox-mcp"],
203
+ "env": {
204
+ "CEREFOX_SUPABASE_URL": "https://<your-project-ref>.supabase.co",
205
+ "CEREFOX_SUPABASE_KEY": "<your-service-role-or-sb_secret-key>",
206
+ "OPENAI_API_KEY": "sk-..."
207
+ }
208
+ }
209
+ }
210
+ }
211
+ ```
212
+
213
+ The `env` block is only needed if you don't already have a `.env` file in a directory the
214
+ server can find — the server resolves `.env` from the current working directory.
215
+
216
+ **Alternative — local checkout (Python or pre-v0.4 setups):**
217
+
218
+ ```json
219
+ {
220
+ "mcpServers": {
221
+ "cerefox": {
222
+ "command": "uv",
223
+ "args": ["--directory", "/path/to/cerefox", "run", "cerefox", "mcp"]
224
+ }
225
+ }
226
+ }
227
+ ```
228
+
229
+ Replace `/path/to/cerefox` with the absolute path to your Cerefox checkout
230
+ (e.g. `/Users/yourname/src/cerefox` on macOS, `C:\Users\yourname\src\cerefox` on Windows).
231
+ This invocation soft-wraps `npx --package=@cerefox/memory cerefox-mcp` when available; otherwise the
232
+ legacy Python MCP server takes over.
233
+
234
+ **Important:**
235
+ - Merge the `mcpServers` block into any existing `claude_desktop_config.json` — do not wrap it
236
+ in an extra `{}` or replace the whole file.
237
+ - Restart Claude Desktop fully (Cmd+Q on macOS, not just close the window) after saving.
238
+
239
+ ---
240
+
241
+ ### ChatGPT Desktop
242
+
243
+ > **ChatGPT Desktop does not support local stdio MCP servers.**
244
+ > OpenAI's MCP implementation for ChatGPT only supports remote servers via SSE or
245
+ > streaming HTTP — not local subprocess (stdio) servers like `cerefox mcp`.
246
+ > The "dev mode" MCP connector visible in the app also requires a public URL.
247
+ >
248
+ > **Use Path B (Custom GPT + Edge Functions) for all ChatGPT access** — both the web
249
+ > app and the desktop app. The Custom GPT approach is fully validated and works well.
250
+
251
+ ---
252
+
253
+ ### Cursor
254
+
255
+ 1. Open **Cursor Settings** (`Cmd+,`) → **Tools & Integrations** → **MCP** → **Add new global MCP server**
256
+ 2. Paste either of the following into the MCP config JSON:
257
+
258
+ **Recommended — npm:**
259
+
260
+ ```json
261
+ {
262
+ "mcpServers": {
263
+ "cerefox": {
264
+ "command": "npx",
265
+ "args": ["-y", "--package=@cerefox/memory", "cerefox-mcp"],
266
+ "env": {
267
+ "CEREFOX_SUPABASE_URL": "https://<your-project-ref>.supabase.co",
268
+ "CEREFOX_SUPABASE_KEY": "<your-service-role-or-sb_secret-key>",
269
+ "OPENAI_API_KEY": "sk-..."
270
+ }
271
+ }
272
+ }
273
+ }
274
+ ```
275
+
276
+ **Alternative — local checkout:**
277
+
278
+ ```json
279
+ {
280
+ "mcpServers": {
281
+ "cerefox": {
282
+ "command": "uv",
283
+ "args": ["--directory", "/path/to/cerefox", "run", "cerefox", "mcp"]
284
+ }
285
+ }
286
+ }
287
+ ```
288
+
289
+ 3. Save and restart Cursor.
290
+
291
+ Alternatively, add a `.cursor/mcp.json` file in your project root with the same content for
292
+ project-scoped access (committed to git, shared with your team).
293
+
294
+ ---
295
+
296
+ ### Claude Code
297
+
298
+ Claude Code (the CLI tool and the **Code** tab inside Claude Desktop) uses its own MCP config —
299
+ separate from `claude_desktop_config.json`. Changes made in one do not affect the other.
300
+
301
+ **Option 1: CLI command — npm (recommended)**
302
+
303
+ ```bash
304
+ claude mcp add --scope user cerefox \
305
+ npx -- -y --package=@cerefox/memory cerefox-mcp
306
+ ```
307
+
308
+ - `--scope user` makes the server available in every project (stored in `~/.claude/mcp.json`).
309
+ - Use `--scope project` instead to limit it to the current directory (stored in `.mcp.json`).
310
+
311
+ If you don't already have `.env` resolvable from your shell's CWD, add the credentials inline
312
+ by editing the resulting JSON config to add an `env` block (see the Claude Desktop example
313
+ above).
314
+
315
+ Verify:
316
+ ```bash
317
+ claude mcp list
318
+ ```
319
+
320
+ **Option 2: CLI command — local checkout (uv)**
321
+
322
+ ```bash
323
+ claude mcp add --scope user cerefox \
324
+ uv -- --directory /path/to/cerefox run cerefox mcp
325
+ ```
326
+
327
+ This soft-wraps `npx --package=@cerefox/memory cerefox-mcp` when available; otherwise falls back to
328
+ the legacy Python MCP server.
329
+
330
+ **Option 3: `.mcp.json` in project root (project-scoped, committable)**
331
+
332
+ Create `.mcp.json` in the root of the repo you work in:
333
+
334
+ ```json
335
+ {
336
+ "mcpServers": {
337
+ "cerefox": {
338
+ "command": "npx",
339
+ "args": ["-y", "--package=@cerefox/memory", "cerefox-mcp"]
340
+ }
341
+ }
342
+ }
343
+ ```
344
+
345
+ **Code tab inside Claude Desktop:**
346
+ The **Code** tab in Claude Desktop uses the same config as the Claude Code CLI, not
347
+ `claude_desktop_config.json`. Run the `claude mcp add` command above — the Code tab will
348
+ pick it up automatically.
349
+
350
+ ---
351
+
352
+ ## Path A-Remote — Remote MCP Edge Function (`cerefox-mcp`)
353
+
354
+ ### What it is
355
+
356
+ `cerefox-mcp` is a Supabase Edge Function that speaks the MCP Streamable HTTP protocol
357
+ (spec 2025-03-26). It calls Postgres RPCs directly via per-tool handlers -- no delegation
358
+ to primitive Edge Functions. This means each MCP tool call costs a single Edge Function
359
+ invocation.
360
+
361
+ A single HTTPS URL gives any remote-capable MCP client all 10 tools with full hybrid
362
+ search -- no Python, no `uv`, no local repository clone needed.
363
+
364
+ **URL format:**
365
+ ```
366
+ https://<your-project-ref>.supabase.co/functions/v1/cerefox-mcp
367
+ ```
368
+
369
+ **When to choose Path A-Remote vs Path A-Local:**
370
+
371
+ | Scenario | Prefer |
372
+ |----------|--------|
373
+ | Default / new setup | Path A-Remote -- no Python, no local clone, one URL works everywhere |
374
+ | Multiple machines / cloud dev environments | Path A-Remote |
375
+ | Minimise Supabase Edge Function usage (free tier limits) | Path A-Local -- zero Edge Function invocations |
376
+ | Offline use or development on the cerefox codebase | Path A-Local -- no network dependency |
377
+ | Lowest latency (same machine, no HTTPS round-trip) | Path A-Local -- slightly faster |
378
+
379
+ **Deploy the Edge Function** (once, after cloning the repo):
380
+ ```bash
381
+ npx supabase functions deploy cerefox-mcp
382
+ ```
383
+
384
+ ---
385
+
386
+ ### Path A-Remote: Claude Code
387
+
388
+ > **Recommended: use `mcp-remote` stdio bridge.** While the SSE idle polling issue has been
389
+ > fixed server-side (v0.1.12 -- the server returns 405 for GET per the MCP spec), `mcp-remote`
390
+ > is still recommended because it cleanly bypasses Supabase's GoTrace OAuth discovery conflict
391
+ > via `--header`. See [issue #17](https://github.com/fstamatelopoulos/cerefox/issues/17) for
392
+ > the full investigation.
393
+
394
+ **Option 1 — `mcp-remote` (recommended):**
395
+
396
+ Add to your project's `.mcp.json` (or copy
397
+ [`examples/mcp-configs/claude-code-remote.json`](../examples/mcp-configs/claude-code-remote.json)):
398
+
399
+ ```json
400
+ {
401
+ "mcpServers": {
402
+ "cerefox": {
403
+ "command": "npx",
404
+ "args": [
405
+ "mcp-remote",
406
+ "https://<your-project-ref>.supabase.co/functions/v1/cerefox-mcp",
407
+ "--header",
408
+ "Authorization: Bearer <your-anon-key>"
409
+ ]
410
+ }
411
+ }
412
+ }
413
+ ```
414
+
415
+ **Option 2 — native HTTP:**
416
+
417
+ Claude Code also supports Streamable HTTP natively. This works and no longer has idle polling
418
+ overhead (fixed in v0.1.12). However, `mcp-remote` is still preferred for the OAuth bypass.
419
+
420
+ ```bash
421
+ claude mcp add --transport http cerefox \
422
+ https://<your-project-ref>.supabase.co/functions/v1/cerefox-mcp \
423
+ --header "Authorization: Bearer <your-anon-key>"
424
+ ```
425
+
426
+ Verify:
427
+ ```bash
428
+ claude mcp list
429
+ ```
430
+
431
+ For a user-scoped server (available in all projects), add `--scope user`:
432
+ ```bash
433
+ claude mcp add --transport http --scope user cerefox \
434
+ https://<your-project-ref>.supabase.co/functions/v1/cerefox-mcp \
435
+ --header "Authorization: Bearer <your-anon-key>"
436
+ ```
437
+
438
+ ---
439
+
440
+ ### Path A-Remote: Cursor
441
+
442
+ Cursor supports remote MCP servers natively via `url` + `headers` in `mcp.json`.
443
+
444
+ 1. Open **Cursor Settings** (`Cmd+,`) → **Tools & Integrations** → **MCP** → **Add new global MCP server**
445
+ 2. Paste this config (replace the placeholders):
446
+
447
+ ```json
448
+ {
449
+ "mcpServers": {
450
+ "cerefox": {
451
+ "url": "https://<your-project-ref>.supabase.co/functions/v1/cerefox-mcp",
452
+ "headers": {
453
+ "Authorization": "Bearer <your-anon-key>"
454
+ }
455
+ }
456
+ }
457
+ }
458
+ ```
459
+
460
+ 3. Save and restart Cursor.
461
+
462
+ Alternatively, add `.cursor/mcp.json` in your project root with the same content for
463
+ project-scoped access.
464
+
465
+ ---
466
+
467
+ ### Path A-Remote: Claude Desktop
468
+
469
+ Claude Desktop does not support remote MCP servers natively -- it requires a local subprocess
470
+ (`command` field). Use [`supergateway`](https://www.npmjs.com/package/supergateway) or
471
+ [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) as a stdio-to-HTTP bridge.
472
+
473
+ > **`supergateway` vs `mcp-remote` for Claude Desktop:** `mcp-remote --header` works for
474
+ > Claude Code (tested). For Claude Desktop, `supergateway` is the tested and confirmed option.
475
+ > `mcp-remote` may also work for Claude Desktop now that the 405 SSE fix is in place, but
476
+ > this has not been verified. If you try it, use the same config as Claude Code.
477
+
478
+ **Requirements:** [Node.js](https://nodejs.org) installed (for `npx`).
479
+
480
+ **Config file location:**
481
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
482
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
483
+
484
+ Add (or merge into) the file:
485
+
486
+ ```json
487
+ {
488
+ "mcpServers": {
489
+ "cerefox": {
490
+ "command": "npx",
491
+ "args": [
492
+ "-y", "supergateway",
493
+ "--streamableHttp", "https://<your-project-ref>.supabase.co/functions/v1/cerefox-mcp",
494
+ "--oauth2Bearer", "<your-anon-key>"
495
+ ]
496
+ }
497
+ }
498
+ }
499
+ ```
500
+
501
+ Replace `<your-project-ref>` and `<your-anon-key>` with your actual values.
502
+
503
+ **Important:**
504
+ - Restart Claude Desktop fully (Cmd+Q on macOS) after saving the config.
505
+ - `-y` tells npx to auto-install `supergateway` without prompting.
506
+ - No Python, no local repo clone, no `.env` file needed — just the URL and anon key.
507
+
508
+ ---
509
+
510
+ ### Path A-Remote: OpenAI Codex CLI
511
+
512
+ [Codex](https://github.com/openai/codex) supports remote MCP servers natively via Streamable
513
+ HTTP. Configuration uses TOML (not JSON like most other MCP clients).
514
+
515
+ **Step 1 — Set the anon key as an environment variable:**
516
+
517
+ Codex references Bearer tokens by environment variable name, not by value. Add to your
518
+ `~/.zshrc` (or `~/.bashrc`):
519
+
520
+ ```bash
521
+ export CEREFOX_ANON_KEY="<your-anon-key>"
522
+ ```
523
+
524
+ Then reload: `source ~/.zshrc`
525
+
526
+ **Step 2 — Add the server to `~/.codex/config.toml`:**
527
+
528
+ ```toml
529
+ [mcp_servers.cerefox]
530
+ url = "https://<your-project-ref>.supabase.co/functions/v1/cerefox-mcp"
531
+ bearer_token_env_var = "CEREFOX_ANON_KEY"
532
+ ```
533
+
534
+ Replace `<your-project-ref>` with your Supabase project ref.
535
+
536
+ **Step 3 — Verify:**
537
+
538
+ Launch Codex and use the `/mcp` slash command to confirm the `cerefox` server is connected
539
+ and all 10 tools are listed.
540
+
541
+ **Notes:**
542
+ - `bearer_token_env_var` is the **name** of the env var (e.g. `"CEREFOX_ANON_KEY"`), not the
543
+ token itself. Codex reads the value at runtime.
544
+ - No Python, no local repo clone needed — just the URL and anon key.
545
+ - No idle SSE polling cost — the 405 GET fix in `cerefox-mcp` prevents it.
546
+
547
+ ---
548
+
549
+ ### Path A-Remote: Gemini CLI
550
+
551
+ [Gemini CLI](https://github.com/google-gemini/gemini-cli) supports Streamable HTTP with static
552
+ Bearer token headers natively — no bridge needed. Architecturally identical to Claude Code and
553
+ Cursor.
554
+
555
+ **Config file location:**
556
+ - Global: `~/.gemini/settings.json`
557
+ - Project: `.gemini/settings.json` in the project root
558
+
559
+ Add (or merge into) the file:
560
+
561
+ ```json
562
+ {
563
+ "mcpServers": {
564
+ "cerefox": {
565
+ "httpUrl": "https://<your-project-ref>.supabase.co/functions/v1/cerefox-mcp",
566
+ "headers": {
567
+ "Authorization": "Bearer <your-anon-key>"
568
+ }
569
+ }
570
+ }
571
+ }
572
+ ```
573
+
574
+ Replace `<your-project-ref>` and `<your-anon-key>` with your actual values.
575
+
576
+ **Verify:**
577
+
578
+ Launch `gemini` and use `/mcp` to confirm tools are listed, or ask:
579
+ > "What tools do you have available?"
580
+
581
+ **Notes:**
582
+ - Use `httpUrl` (not `url`) for Streamable HTTP transport.
583
+ - Static headers bypass OAuth discovery entirely — no GoTrue conflict.
584
+ - No Python, no local repo clone needed.
585
+ - Status: **untested** — expected to work based on architecture match with Claude Code/Cursor.
586
+
587
+ ---
588
+
589
+ ## Path B — Supabase Edge Functions (HTTP)
590
+
591
+ ### What they are
592
+
593
+ TypeScript functions deployed to Supabase, callable over HTTPS from anywhere — no local install,
594
+ no MCP client needed. Embeddings are computed server-side using the `OPENAI_API_KEY` secret
595
+ stored in Supabase.
596
+
597
+ - Works from cloud agents (ChatGPT GPT Actions, scripts, CI pipelines)
598
+ - No user machine required; Supabase handles all infrastructure
599
+ - Constraint: embedding model is hardcoded in TypeScript — requires redeployment when changed
600
+ (see `docs/guides/configuration.md` → "Changing the embedding model")
601
+
602
+ ### Path B authentication
603
+
604
+ All Edge Function calls require:
605
+
606
+ ```
607
+ Authorization: Bearer <your-anon-key>
608
+ Content-Type: application/json
609
+ ```
610
+
611
+ Find your anon key: **Supabase Dashboard → Project Settings → API Keys → Legacy → anon** (use the legacy JWT, not the new `sb_publishable_…` — see the API keys callout in the Prerequisites section).
612
+
613
+ ### Path B system prompt
614
+
615
+ For ChatGPT Custom GPT:
616
+ ```
617
+ You have access to a personal knowledge base via the searchKnowledgeBase action.
618
+ When the user asks a question, always search the knowledge base first using a
619
+ relevant query. Present results by document title, citing the source for every claim.
620
+ Use ingestNote to save any new information the user asks you to remember.
621
+ ```
622
+
623
+ ### Path B verification
624
+
625
+ ```bash
626
+ curl -s -X POST \
627
+ "https://<your-project-ref>.supabase.co/functions/v1/cerefox-search" \
628
+ -H "Authorization: Bearer <your-anon-key>" \
629
+ -H "Content-Type: application/json" \
630
+ -d '{"query": "second brain", "match_count": 3}'
631
+ ```
632
+
633
+ Expected: JSON response with `results` array containing documents.
634
+
635
+ ---
636
+
637
+ ### ChatGPT Custom GPT (cloud — chatgpt.com)
638
+
639
+ A Custom GPT with Actions pointing at the Edge Functions gives ChatGPT full hybrid search from
640
+ any browser — no local install, no MCP client, works free with ChatGPT Plus.
641
+
642
+ **Step 1 — Create the Custom GPT**
643
+
644
+ 1. Go to **chatgpt.com → Explore GPTs → Create**
645
+ 2. Name it (e.g. "Cerefox Assistant")
646
+ 3. Paste the system prompt from "Path B system prompt" above into the **Instructions** field
647
+ 4. Click **Create new action**
648
+
649
+ **Step 2 — Paste the OpenAPI schema**
650
+
651
+ In the action editor, paste this schema (replace `<your-project-ref>`):
652
+
653
+ ```yaml
654
+ openapi: 3.1.0
655
+ info:
656
+ title: Cerefox Knowledge Base
657
+ version: 1.7.0
658
+ servers:
659
+ - url: https://<your-project-ref>.supabase.co/functions/v1
660
+ paths:
661
+ /cerefox-search:
662
+ post:
663
+ operationId: searchKnowledgeBase
664
+ summary: Search the knowledge base (hybrid FTS + semantic, document-level)
665
+ requestBody:
666
+ required: true
667
+ content:
668
+ application/json:
669
+ schema:
670
+ type: object
671
+ required: [query]
672
+ properties:
673
+ query:
674
+ type: string
675
+ match_count:
676
+ type: integer
677
+ default: 5
678
+ project_name:
679
+ type: string
680
+ mode:
681
+ type: string
682
+ default: docs
683
+ metadata_filter:
684
+ type: object
685
+ additionalProperties:
686
+ type: string
687
+ description: >
688
+ Optional JSONB containment filter. Only documents whose metadata
689
+ contains ALL specified key-value pairs are returned.
690
+ Example: {"type": "decision", "status": "active"}.
691
+ Call listMetadataKeys to discover available keys and their values.
692
+ Omit or set to null to search all documents.
693
+ requestor:
694
+ type: string
695
+ description: >
696
+ Name of the agent making this request (e.g., "ChatGPT").
697
+ Recorded in the usage log for attribution. Optional.
698
+ responses:
699
+ '200':
700
+ description: >
701
+ { results, query, mode, match_count, project_name, metadata_filter, truncated, response_bytes }.
702
+ Each item in results (docs mode) contains: document_id, doc_title, full_content,
703
+ chunk_count, total_chars, best_score, is_partial.
704
+ is_partial is true when the document exceeded the small-to-big threshold — in that
705
+ case full_content contains matched chunks plus their neighbours rather than the
706
+ complete document, and total_chars still reflects the full document size.
707
+ /cerefox-ingest:
708
+ post:
709
+ operationId: ingestNote
710
+ summary: >
711
+ Save a note to the knowledge base. When update_if_exists is true and the
712
+ document already exists, the previous version is archived automatically —
713
+ you can retrieve it later with getDocument.
714
+ requestBody:
715
+ required: true
716
+ content:
717
+ application/json:
718
+ schema:
719
+ type: object
720
+ required: [title, content]
721
+ properties:
722
+ title:
723
+ type: string
724
+ content:
725
+ type: string
726
+ document_id:
727
+ type: string
728
+ description: >
729
+ UUID of an existing document to update. When provided, updates
730
+ that document directly regardless of update_if_exists. Returns
731
+ an error if the document does not exist. Workflow: search for
732
+ the document, note the document_id, pass it here.
733
+ project_name:
734
+ type: string
735
+ source:
736
+ type: string
737
+ default: agent
738
+ metadata:
739
+ type: object
740
+ update_if_exists:
741
+ type: boolean
742
+ default: false
743
+ description: >
744
+ When true, update an existing document with the same title
745
+ instead of creating a new one. The previous content is archived
746
+ as a version. If content is unchanged, the document is skipped
747
+ (no re-indexing). Ignored when document_id is provided.
748
+ author:
749
+ type: string
750
+ description: >
751
+ Name of the agent or tool performing the ingestion (e.g.,
752
+ "ChatGPT", "Claude Code"). Recorded in the audit log for
753
+ attribution. Defaults to "agent" if not provided.
754
+ author_type:
755
+ type: string
756
+ enum: [user, agent]
757
+ default: agent
758
+ description: >
759
+ Whether this write is from a human user or an AI agent.
760
+ Controls review_status auto-transition: agent writes set
761
+ the document to pending_review, user writes set it to approved.
762
+ responses:
763
+ '200':
764
+ description: Ingest result
765
+ /cerefox-metadata:
766
+ post:
767
+ operationId: listMetadataKeys
768
+ summary: List all metadata keys in use across documents with counts and example values
769
+ requestBody:
770
+ required: true
771
+ content:
772
+ application/json:
773
+ schema:
774
+ type: object
775
+ properties:
776
+ requestor:
777
+ type: string
778
+ description: Name of the agent making this request. Optional.
779
+ responses:
780
+ '200':
781
+ description: Array of metadata keys with doc_count and example_values
782
+ /cerefox-get-document:
783
+ post:
784
+ operationId: getDocument
785
+ summary: >
786
+ Retrieve the full reconstructed content of a document (current version or a specific
787
+ archived version). Use listVersions first to discover available version UUIDs.
788
+ requestBody:
789
+ required: true
790
+ content:
791
+ application/json:
792
+ schema:
793
+ type: object
794
+ required: [document_id]
795
+ properties:
796
+ document_id:
797
+ type: string
798
+ description: UUID of the document to retrieve
799
+ version_id:
800
+ type: string
801
+ description: >
802
+ UUID of a specific archived version to retrieve. Omit (or pass null)
803
+ for the current version. Version UUIDs are returned by listVersions.
804
+ requestor:
805
+ type: string
806
+ description: Name of the agent making this request. Optional.
807
+ responses:
808
+ '200':
809
+ description: >
810
+ Document content and metadata:
811
+ { document_id, doc_title, full_content, chunk_count, total_chars,
812
+ is_archived, version_id }
813
+ '404':
814
+ description: Document not found
815
+ /cerefox-list-versions:
816
+ post:
817
+ operationId: listVersions
818
+ summary: >
819
+ List all archived versions of a document, newest first. Returns version UUIDs
820
+ to pass to getDocument for historical content retrieval.
821
+ requestBody:
822
+ required: true
823
+ content:
824
+ application/json:
825
+ schema:
826
+ type: object
827
+ required: [document_id]
828
+ properties:
829
+ document_id:
830
+ type: string
831
+ description: UUID of the document whose version history to list
832
+ requestor:
833
+ type: string
834
+ description: Name of the agent making this request. Optional.
835
+ responses:
836
+ '200':
837
+ description: >
838
+ Array of version objects (empty array if no versions exist):
839
+ [{ version_id, version_number, source, chunk_count, total_chars, archived, created_at }]
840
+ /cerefox-get-audit-log:
841
+ post:
842
+ operationId: getAuditLog
843
+ summary: >
844
+ Query audit log entries with optional filters. Returns entries with document
845
+ titles, author attribution, operation types, size changes, and descriptions.
846
+ requestBody:
847
+ required: true
848
+ content:
849
+ application/json:
850
+ schema:
851
+ type: object
852
+ properties:
853
+ document_id:
854
+ type: string
855
+ description: Filter by document UUID (optional)
856
+ author:
857
+ type: string
858
+ description: Filter by author name (optional)
859
+ operation:
860
+ type: string
861
+ description: >
862
+ Filter by operation type: create, update-content, update-metadata,
863
+ delete, status-change, archive, unarchive (optional)
864
+ since:
865
+ type: string
866
+ description: ISO timestamp lower bound for temporal queries (optional)
867
+ limit:
868
+ type: integer
869
+ default: 50
870
+ description: Max entries to return (max 200)
871
+ requestor:
872
+ type: string
873
+ description: Name of the agent making this request. Optional.
874
+ responses:
875
+ '200':
876
+ description: >
877
+ Array of audit log entries:
878
+ [{ id, document_id, doc_title, version_id, operation, author, author_type,
879
+ size_before, size_after, description, created_at }]
880
+ /cerefox-list-projects:
881
+ post:
882
+ operationId: listProjects
883
+ summary: List all projects with their names, IDs, and descriptions
884
+ requestBody:
885
+ required: true
886
+ content:
887
+ application/json:
888
+ schema:
889
+ type: object
890
+ properties:
891
+ requestor:
892
+ type: string
893
+ description: Name of the agent making this request. Optional.
894
+ responses:
895
+ '200':
896
+ description: >
897
+ Array of projects: [{ id, name, description }]
898
+ /cerefox-metadata-search:
899
+ post:
900
+ operationId: metadataSearch
901
+ summary: >
902
+ Find documents by metadata key-value criteria without a text search term.
903
+ Use to discover documents tagged with specific attributes or browse by taxonomy.
904
+ requestBody:
905
+ required: true
906
+ content:
907
+ application/json:
908
+ schema:
909
+ type: object
910
+ required: [metadata_filter]
911
+ properties:
912
+ metadata_filter:
913
+ type: object
914
+ additionalProperties:
915
+ type: string
916
+ description: >
917
+ Key-value pairs; ALL must match (AND semantics).
918
+ Example: {"type": "decision", "status": "active"}.
919
+ project_id:
920
+ type: string
921
+ description: Filter by project UUID (optional)
922
+ updated_since:
923
+ type: string
924
+ description: ISO-8601 timestamp; only docs updated on/after (optional)
925
+ created_since:
926
+ type: string
927
+ description: ISO-8601 timestamp; only docs created on/after (optional)
928
+ limit:
929
+ type: integer
930
+ default: 10
931
+ include_content:
932
+ type: boolean
933
+ default: false
934
+ description: Include full document text in results
935
+ requestor:
936
+ type: string
937
+ description: Name of the agent making this request. Optional.
938
+ responses:
939
+ '200':
940
+ description: >
941
+ Array of matching documents:
942
+ [{ document_id, title, doc_metadata, review_status, source, created_at,
943
+ updated_at, total_chars, chunk_count, project_ids, project_names,
944
+ version_count, content }]
945
+ ```
946
+
947
+ **Step 3 — Configure authentication**
948
+
949
+ In the action's **Authentication** settings:
950
+ - Type: **API Key**
951
+ - Auth type: **Bearer**
952
+ - API key: your Supabase **anon key**
953
+
954
+ > **Important:** ChatGPT may reset the API key when you update the action schema.
955
+ > If you get a 403 error after changing the schema, re-enter the anon key in the
956
+ > authentication settings — the functions themselves are fine.
957
+
958
+ **Step 4 — Save and test**
959
+
960
+ Save the GPT. In a new chat, ask:
961
+ > "Search my knowledge base for 'second brain'."
962
+
963
+ > **Cost**: GPT Actions are free with ChatGPT Plus. Each search call uses a small amount of
964
+ > OpenAI API credits for embedding the query. See `docs/guides/operational-cost.md`.
965
+
966
+ ---
967
+
968
+ ### curl / scripts
969
+
970
+ Direct HTTP access — useful for shell scripts, CI pipelines, or one-off queries.
971
+
972
+ **Search:**
973
+ ```bash
974
+ curl -s -X POST \
975
+ "https://<your-project-ref>.supabase.co/functions/v1/cerefox-search" \
976
+ -H "Authorization: Bearer <your-anon-key>" \
977
+ -H "Content-Type: application/json" \
978
+ -d '{"query": "knowledge management", "match_count": 5}'
979
+ ```
980
+
981
+ **Ingest:**
982
+ ```bash
983
+ curl -s -X POST \
984
+ "https://<your-project-ref>.supabase.co/functions/v1/cerefox-ingest" \
985
+ -H "Authorization: Bearer <your-anon-key>" \
986
+ -H "Content-Type: application/json" \
987
+ -d '{
988
+ "title": "Meeting Notes 2026-03-11",
989
+ "content": "# Meeting Notes\n\n## Q1 Roadmap\n\nWe agreed to prioritize...",
990
+ "project_name": "Work",
991
+ "source": "agent"
992
+ }'
993
+ ```
994
+
995
+ If the same content was already ingested (SHA-256 hash match), returns `"skipped": true`.
996
+
997
+ **Edge Function parameters — `cerefox-search`:**
998
+
999
+ | Parameter | Type | Default | Description |
1000
+ |-----------|------|---------|-------------|
1001
+ | `query` | string | required | Natural-language search query |
1002
+ | `project_name` | string | optional | Filter by project name (case-insensitive) |
1003
+ | `match_count` | number | 5 | Maximum **documents** to return |
1004
+ | `mode` | string | `"docs"` | `"docs"` = full document results (recommended) |
1005
+ | `alpha` | number | 0.7 | Semantic weight (0 = FTS only, 1 = semantic only) |
1006
+ | `min_score` | number | 0.5 | Minimum cosine similarity threshold |
1007
+ | `max_bytes` | number | 200000 | Response size budget in bytes. Results are dropped whole (never truncated mid-document) once the budget is reached. The response includes `truncated: true` and `response_bytes` when the limit was hit. See "Response size limit" below. |
1008
+
1009
+ **Response envelope fields:**
1010
+
1011
+ | Field | Type | Description |
1012
+ |-------|------|-------------|
1013
+ | `results` | array | Matched documents or chunks (see per-row fields below) |
1014
+ | `query` | string | The original query |
1015
+ | `mode` | string | Search mode used |
1016
+ | `match_count` | number | `match_count` value used |
1017
+ | `project_name` | string\|null | Project filter applied (if any) |
1018
+ | `truncated` | boolean | `true` when results were dropped to stay within `max_bytes` |
1019
+ | `response_bytes` | number | Actual bytes in the returned `results` array |
1020
+
1021
+ **Per-result row fields (`docs` mode — the recommended default):**
1022
+
1023
+ | Field | Type | Description |
1024
+ |-------|------|-------------|
1025
+ | `document_id` | string | UUID of the matched document |
1026
+ | `doc_title` | string | Document title |
1027
+ | `doc_source` | string | Origin: `"file"`, `"paste"`, `"agent"` |
1028
+ | `doc_metadata` | object | Arbitrary JSON metadata |
1029
+ | `best_score` | number | Highest chunk relevance score (0–1) |
1030
+ | `best_chunk_heading_path` | string[] | Heading breadcrumb of the best-scoring chunk |
1031
+ | `full_content` | string | Reconstructed document content (may be partial — see `is_partial`) |
1032
+ | `chunk_count` | integer | Number of chunks in `full_content` |
1033
+ | `total_chars` | integer | Full document size in characters (always the whole doc, even when `is_partial` is true) |
1034
+ | `is_partial` | boolean | `true` when `full_content` contains only matched chunks + neighbours instead of the complete document. Triggered when the document exceeds the small-to-big threshold (default 40 000 chars). Use `getDocument` to retrieve the full text. |
1035
+ | `doc_updated_at` | string | ISO 8601 timestamp of the last document update |
1036
+ | `version_count` | integer | Number of archived versions (0 if never updated) |
1037
+ | `doc_project_ids` | string[] | Project UUIDs the document belongs to |
1038
+
1039
+ **Response size limit (`max_bytes`):**
1040
+
1041
+ 200 KB is a safety ceiling that prevents runaway responses under unusual settings (e.g. very high `match_count`). Under normal usage the small-to-big retrieval path already keeps individual large-document results compact (matched chunks + neighbours only), so this limit is rarely reached.
1042
+
1043
+ You can override it per-request if needed:
1044
+ ```json
1045
+ { "query": "deployment checklist", "max_bytes": 400000 }
1046
+ ```
1047
+
1048
+ See `docs/guides/configuration.md` → "Response size limit" for full details.
1049
+
1050
+ ---
1051
+
1052
+ ### Cloud Claude (claude.ai web)
1053
+
1054
+ Claude.ai web can connect to the Supabase-hosted remote MCP (no local install):
1055
+
1056
+ 1. In Claude.ai: **Settings → Integrations → Add integration**
1057
+ 2. Enter the MCP URL:
1058
+ ```
1059
+ https://mcp.supabase.com/sse?project_ref=<your-project-ref>
1060
+ ```
1061
+ 3. Authenticate with your Personal Access Token when prompted.
1062
+
1063
+ > **Limitation**: The cloud Supabase MCP only supports **FTS keyword search** — no hybrid or
1064
+ > semantic search. For full hybrid search from the web, deploy the MCP server to Cloud Run
1065
+ > (see `docs/TODO.md` → "Remote HTTP MCP server").
1066
+
1067
+ ---
1068
+
1069
+ ## Path C — Shell CLI for local coding agents
1070
+
1071
+ ### What it is
1072
+
1073
+ Modern local coding agents — Claude Code, OpenAI Codex CLI, opencode, OpenClaw, Hermes, and many others — all expose a **Bash tool** (or similar shell-execution tool) to their underlying model. If the agent's user grants the agent access to a checked-out Cerefox repo, the agent can read and write the knowledge base by running `uv run cerefox …` exactly the same way a human would.
1074
+
1075
+ This is **not a separate Cerefox installation path** — it's the same Layer 2 access (Python REST + service-role key) that you already use as a human via the CLI. What's new is the *usage model*: the user authorizes a local agent to use that CLI on their behalf, instead of (or alongside) configuring MCP.
1076
+
1077
+ When to choose Path C over Path A:
1078
+
1079
+ - **No MCP setup friction** — the agent already has a Bash tool; no `.mcp.json`, no `claude mcp add`, no Claude Desktop config edits.
1080
+ - **One Cerefox checkout serves any number of local agents** — Claude Code, Codex CLI, opencode, etc. running in the same project all use the same `uv run cerefox …` commands.
1081
+ - **Best for power users who already use the CLI themselves** — the agent and the user share one mental model and one set of conventions.
1082
+
1083
+ When Path A is still better:
1084
+
1085
+ - Cleaner agent UX — named tool calls (`cerefox_search(...)`) read better in agent transcripts than `Bash("uv run cerefox search 'foo'")`.
1086
+ - Some agents may rate-limit or budget Bash calls separately from MCP calls.
1087
+ - Cloud-only agents (claude.ai, chatgpt.com) cannot use Path C at all — they have no Bash tool.
1088
+
1089
+ ### Prerequisites
1090
+
1091
+ Same as **Path A-Local**:
1092
+
1093
+ - [`uv`](https://docs.astral.sh/uv/getting-started/installation/) installed on your machine
1094
+ - Cerefox repository cloned locally (e.g. `/Users/yourname/src/cerefox`)
1095
+ - `.env` configured with `CEREFOX_SUPABASE_URL`, `CEREFOX_SUPABASE_KEY` (service-role / new secret key), and your embedding API key (`OPENAI_API_KEY`)
1096
+
1097
+ Quick sanity check before pointing an agent at it:
1098
+
1099
+ ```bash
1100
+ cd /path/to/cerefox
1101
+ uv run cerefox search "any query"
1102
+ uv run cerefox list-projects
1103
+ ```
1104
+
1105
+ If both work for you, they'll work for the agent.
1106
+
1107
+ ### How to enable it for an agent
1108
+
1109
+ The pattern is the same across Claude Code, Codex CLI, opencode, OpenClaw, Hermes, and similar tools:
1110
+
1111
+ 1. **Tell the agent the Cerefox checkout path** (e.g. via system prompt, project memory, or your agent's equivalent of `CLAUDE.md`).
1112
+ 2. **Point the agent at the agent docs** in that checkout: `AGENT_GUIDE.md` and `AGENT_QUICK_REFERENCE.md`. These already describe what to read, what to write, and the audit/metadata conventions. They cover MCP usage; the CLI mapping is in `AGENT_GUIDE.md` ("Using Cerefox via the CLI").
1113
+ 3. **Optionally**: add a one-line reminder in the agent's system prompt so the model defaults to using Cerefox proactively.
1114
+
1115
+ Example system-prompt snippet (adapt for your agent — Claude Code's `CLAUDE.md`, Codex's `AGENTS.md`, opencode's project config, etc.):
1116
+
1117
+ ```
1118
+ You have access to a personal Cerefox knowledge base via a local CLI.
1119
+
1120
+ - Path: /path/to/cerefox (cd here before running commands)
1121
+ - Run any command with: uv run cerefox <subcommand>
1122
+ - Read AGENT_GUIDE.md and AGENT_QUICK_REFERENCE.md in that directory for
1123
+ conventions, metadata rules, and the MCP-tool → CLI-command mapping.
1124
+ Full per-flag reference: docs/guides/cli.md.
1125
+
1126
+ Identify yourself on every call:
1127
+ - Writes (ingest, ingest-dir): pass --author "<your-name>" --author-type agent
1128
+ - Reads (search, get-doc, list-versions, list-projects, metadata-search,
1129
+ get-audit-log): pass --requestor "<your-name>"
1130
+
1131
+ When answering questions, search Cerefox first. When the user asks you to
1132
+ remember something, ingest it. Cite document titles for every claim drawn
1133
+ from the knowledge base.
1134
+ ```
1135
+
1136
+ ### MCP tool ↔ CLI command mapping
1137
+
1138
+ The agent docs are written around MCP tool names. **CLI flag names match MCP parameter names exactly** (kebab-cased) — short forms like `--project`, `--filter`, `--count`, `--update`, `--version` are accepted as aliases. Full per-flag reference: [`docs/guides/cli.md`](cli.md).
1139
+
1140
+ | MCP tool | CLI command |
1141
+ |---|---|
1142
+ | `cerefox_search` | `uv run cerefox search "<query>" --match-count N --project-name <n> --metadata-filter '<json>' --requestor <name>` (CLI-only: `--mode`, `--alpha`, `--min-score`) |
1143
+ | `cerefox_ingest` (file) | `uv run cerefox ingest <path> --title <t> --project-name <n> --metadata '<json>' --update-if-exists\|--document-id <uuid> --source <s> --author <a> --author-type user\|agent` |
1144
+ | `cerefox_ingest` (paste) | `printf '...' \| uv run cerefox ingest --paste --title "<title>"` (same flags) |
1145
+ | `cerefox_get_document` | `uv run cerefox get-doc <document-id> --version-id <vid> --requestor <name>` |
1146
+ | `cerefox_list_versions` | `uv run cerefox list-versions <document-id> --requestor <name>` |
1147
+ | `cerefox_list_projects` | `uv run cerefox list-projects --requestor <name>` |
1148
+ | `cerefox_list_metadata_keys` | `uv run cerefox list-metadata-keys` |
1149
+ | `cerefox_metadata_search` | `uv run cerefox metadata-search --metadata-filter '<json>' --project-name <n> --requestor <name>` |
1150
+ | `cerefox_get_audit_log` | `uv run cerefox get-audit-log --document-id <id> --author <a> --operation <op> --since <iso> --until <iso> --limit N --json --requestor <name>` |
1151
+
1152
+ ### Path C verification prompts
1153
+
1154
+ After pointing your agent at the repo, ask it:
1155
+
1156
+ > "Run a Cerefox search for 'second brain'. What did you find?"
1157
+ > Expected: agent runs `uv run cerefox search "second brain"` via its Bash tool and reports results.
1158
+
1159
+ > "Save a note titled 'Test Note' to Cerefox with the content '# Test\nThis is a Path C test.'"
1160
+ > Expected: agent runs `cerefox ingest --paste --title "Test Note"` (or equivalent) and reports the new document ID.
1161
+
1162
+ > "List my Cerefox projects."
1163
+ > Expected: agent runs `uv run cerefox list-projects`.
1164
+
1165
+ ### Caveats
1166
+
1167
+ - **Privilege level**: the CLI uses the **service-role key** (`CEREFOX_SUPABASE_KEY`), which bypasses Row Level Security. An agent with Bash access has the same full read/write power you do. Only enable Path C for agents you trust to act on your behalf — the same trust level you'd grant Cursor/Claude Code for editing your source code.
1168
+ - **Audit attribution**: Path C records `access_path = "cli"` in usage logs, distinct from `"local-mcp"` / `"remote-mcp"`. **Agents must set `--author <name> --author-type agent` on writes and `--requestor <name>` on reads** (or rely on `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` env vars). Without these flags, writes attribute to `"unknown"` / `"user"`, which under-reports agent activity. See the 2026-05-18 Decision Log Q2 entry for the design rationale (`author_type` is caller-declared on ambiguous channels — CLI and Edge Functions — but `access_path` is always derived from the code layer).
1169
+ - **Soft-delete is reachable; purge and restore are not** — by design. `cerefox delete-doc` is exposed on the CLI and sends documents to trash with an audit entry. **Permanent purge** (irreversible) and **restore from trash** (un-doing a destructive action) are intentionally web-UI-only and require human-in-the-loop confirmation. If an agent on Path C decides to delete content, it should surface that to the user explicitly so they can review and either restore or commit. See [`access-paths.md` → Destructive operations and the trust model](access-paths.md#destructive-operations-and-the-trust-model) for the full rationale and contributor guidance.
1170
+ - **Cross-doc links in content you ingest** become clickable when the user views them in the Cerefox web UI. Author them as `[Text](uuid)` (most stable), `[Text](docs/path.md)` (repo files), or `[Text](<Title With Spaces>)` (angle-bracket form — bare spaces break markdown). See [`AGENT_GUIDE.md` → "Writing linkable content"](../../AGENT_GUIDE.md#writing-linkable-content) for the full set of rules.
1171
+ - **One repo per machine**: the agent needs your checkout — there's no "Path C without a local clone". If you skip the local install entirely, Path A-Remote or Path B is the only option.
1172
+ - **No sandboxing beyond the agent's existing Bash sandbox**: the CLI is just shell. If your agent's tool framework restricts which commands run, allowlist `uv run cerefox …` explicitly.
1173
+
1174
+ ### Path C is configuration-free, but here's the per-agent footprint
1175
+
1176
+ | Agent | Where to mention the Cerefox path |
1177
+ |---|---|
1178
+ | Claude Code | `CLAUDE.md` in the project, or `~/.claude/CLAUDE.md` globally. No MCP entry needed. |
1179
+ | OpenAI Codex CLI | `AGENTS.md` or the project's instructions file. |
1180
+ | opencode | Project config / agent system prompt. |
1181
+ | OpenClaw, Hermes, custom local agents | Whatever the tool's system-prompt / memory mechanism is. |
1182
+
1183
+ There is nothing Cerefox-specific to install for the agent itself — just the repo + your `.env`.
1184
+
1185
+ ---
1186
+
1187
+ ## Custom agents (Python SDK)
1188
+
1189
+ Use the Cerefox Python client directly for scripted or embedded agents:
1190
+
1191
+ ```python
1192
+ from cerefox.config import Settings
1193
+ from cerefox.db.client import CerefoxClient
1194
+ from cerefox.embeddings.cloud import CloudEmbedder
1195
+ from cerefox.retrieval.search import SearchClient
1196
+
1197
+ settings = Settings() # reads from .env
1198
+ client = CerefoxClient(settings)
1199
+ embedder = CloudEmbedder(
1200
+ api_key=settings.get_embedder_api_key(),
1201
+ base_url=settings.get_embedder_base_url(),
1202
+ model=settings.get_embedder_model(),
1203
+ dimensions=settings.get_embedder_dimensions(),
1204
+ )
1205
+ sc = SearchClient(client, embedder, settings)
1206
+
1207
+ resp = sc.search_docs("what did I write about Rust?", match_count=5)
1208
+ for hit in resp.results:
1209
+ print(f"[{hit.best_score:.2f}] {hit.doc_title}")
1210
+ print(hit.full_content[:400])
1211
+ ```
1212
+
1213
+ ---
1214
+
1215
+ ## Keeping both paths in sync
1216
+
1217
+ Both paths use the same Postgres RPCs and the same stored embeddings, but embed queries
1218
+ independently. If you change the embedding model, **update both paths** before searching:
1219
+
1220
+ 1. Update `.env` + run `cerefox reindex` (re-embeds stored chunks via Python)
1221
+ 2. Update the TypeScript constants in `supabase/functions/*/index.ts` + redeploy Edge Functions
1222
+
1223
+ See `docs/guides/configuration.md` → "Changing the embedding model" for the full procedure.
1224
+
1225
+ ---
1226
+
1227
+ ## MCP tool reference
1228
+
1229
+ ### `cerefox_search`
1230
+
1231
+ Search the knowledge base. Returns complete documents ranked by hybrid (FTS + semantic) relevance.
1232
+
1233
+ | Parameter | Type | Default | Description |
1234
+ |-----------|------|---------|-------------|
1235
+ | `query` | string | required | Natural-language search query |
1236
+ | `match_count` | integer | 5 | Maximum **documents** to return |
1237
+ | `project_name` | string | optional | Filter to a specific project |
1238
+
1239
+ Each result includes `doc_title`, `best_score`, `full_content`, `chunk_count`, `total_chars`, and `is_partial`. When `is_partial` is true, the document exceeded the small-to-big threshold: `full_content` contains the best-matching chunks and their neighbours rather than the whole document. The heading for such results includes a `— partial (N of M chars)` annotation. Use `cerefox_get_document` to retrieve the full text when needed.
1240
+
1241
+ ### `cerefox_ingest`
1242
+
1243
+ Save a note or document to the knowledge base.
1244
+
1245
+ | Parameter | Type | Default | Description |
1246
+ |-----------|------|---------|-------------|
1247
+ | `title` | string | required | Document title |
1248
+ | `content` | string | required | Markdown content |
1249
+ | `document_id` | string | optional | UUID of an existing document to update. When provided, updates that document directly regardless of `update_if_exists`. Returns an error if the document does not exist. Workflow: `cerefox_search` → note `[id: ...]` → pass here. |
1250
+ | `project_name` | string | optional | Assign to a project (created if absent) |
1251
+ | `source` | string | `"agent"` | Origin label |
1252
+ | `metadata` | object | `{}` | Arbitrary JSON metadata |
1253
+ | `update_if_exists` | boolean | `false` | When true, update an existing document with the same title instead of creating a new one. The previous version is archived automatically. Content is re-indexed only if it changed. Ignored when `document_id` is provided. |
1254
+
1255
+ ### `cerefox_list_metadata_keys`
1256
+
1257
+ No parameters. Returns all distinct metadata keys currently in use across documents, with document counts and up to 5 example values per key.
1258
+
1259
+ ### `cerefox_get_document`
1260
+
1261
+ Retrieve the full reconstructed content of a document. Pass `version_id` to retrieve an archived version; omit it for the current version. Version UUIDs are returned by `cerefox_list_versions`.
1262
+
1263
+ | Parameter | Type | Default | Description |
1264
+ |-----------|------|---------|-------------|
1265
+ | `document_id` | string | required | UUID of the document to retrieve |
1266
+ | `version_id` | string | optional | UUID of a specific archived version; omit for current |
1267
+
1268
+ ### `cerefox_list_versions`
1269
+
1270
+ List all archived versions of a document, newest first. Returns `version_id` (use with `cerefox_get_document`), `version_number`, `source`, `chunk_count`, `total_chars`, and `created_at`.
1271
+
1272
+ | Parameter | Type | Default | Description |
1273
+ |-----------|------|---------|-------------|
1274
+ | `document_id` | string | required | UUID of the document whose version history to list |
1275
+
1276
+ ---
1277
+
1278
+ ## RPC reference
1279
+
1280
+ All RPCs are defined in `src/cerefox/db/rpcs.sql`.
1281
+
1282
+ ### Search RPCs
1283
+
1284
+ Every chunk-level RPC returns these fields:
1285
+
1286
+ | Field | Type | Description |
1287
+ |-------|------|-------------|
1288
+ | `chunk_id` | UUID | ID of the matching chunk |
1289
+ | `document_id` | UUID | ID of the parent document |
1290
+ | `chunk_index` | INT | Position within the document |
1291
+ | `title` | TEXT | Chunk heading (H1/H2/H3) |
1292
+ | `content` | TEXT | Full chunk text |
1293
+ | `heading_path` | TEXT[] | Breadcrumb: e.g. `["Doc Title", "Section", "Sub"]` |
1294
+ | `heading_level` | INT | 0–3 |
1295
+ | `score` | FLOAT | Relevance score (higher = more relevant) |
1296
+ | `doc_title` | TEXT | Parent document title |
1297
+ | `doc_source` | TEXT | Origin: `"file"`, `"paste"`, `"agent"` |
1298
+ | `doc_project_ids` | UUID[] | Project UUIDs assigned to the document |
1299
+ | `doc_metadata` | JSONB | Document metadata |
1300
+
1301
+ #### `cerefox_fts_search`
1302
+
1303
+ Full-text keyword search. Does not require an embedding model.
1304
+
1305
+ | Parameter | Type | Default | Description |
1306
+ |-----------|------|---------|-------------|
1307
+ | `p_query_text` | TEXT | required | Keyword query |
1308
+ | `p_match_count` | INT | 10 | Results to return |
1309
+ | `p_project_id` | UUID | null | Filter by project |
1310
+
1311
+ #### `cerefox_semantic_search`
1312
+
1313
+ Vector similarity search. Requires a pre-computed query embedding.
1314
+
1315
+ | Parameter | Type | Default | Description |
1316
+ |-----------|------|---------|-------------|
1317
+ | `p_query_embedding` | VECTOR(768) | required | Query embedding |
1318
+ | `p_match_count` | INT | 10 | Results to return |
1319
+ | `p_use_upgrade` | BOOL | false | Use upgrade embedding column |
1320
+ | `p_project_id` | UUID | null | Filter by project |
1321
+ | `p_min_score` | FLOAT | 0.0 | Minimum cosine similarity |
1322
+
1323
+ #### `cerefox_hybrid_search`
1324
+
1325
+ Combines FTS and semantic search via linear alpha blending. Two overloads (with/without `p_project_id`).
1326
+
1327
+ | Parameter | Type | Default | Description |
1328
+ |-----------|------|---------|-------------|
1329
+ | `p_query_text` | TEXT | required | Query string for FTS |
1330
+ | `p_query_embedding` | VECTOR(768) | required | Query embedding |
1331
+ | `p_match_count` | INT | 10 | Results to return |
1332
+ | `p_alpha` | FLOAT | 0.7 | Semantic weight (0=FTS only, 1=semantic only) |
1333
+ | `p_use_upgrade` | BOOL | false | Use upgrade embedding column |
1334
+ | `p_project_id` | UUID | null | Filter by project |
1335
+ | `p_min_score` | FLOAT | 0.0 | Minimum cosine similarity |
1336
+
1337
+ #### `cerefox_search_docs`
1338
+
1339
+ Document-level search. Runs hybrid search internally, deduplicates by document, then returns up to
1340
+ `p_match_count` **distinct documents** with their full reconstructed content. **This is the
1341
+ recommended RPC for agent use** — agents receive complete notes, not isolated chunks.
1342
+
1343
+ | Parameter | Type | Default | Description |
1344
+ |-----------|------|---------|-------------|
1345
+ | `p_query_text` | TEXT | required | Query string for FTS |
1346
+ | `p_query_embedding` | VECTOR(768) | required | Query embedding |
1347
+ | `p_match_count` | INT | 5 | Max documents to return |
1348
+ | `p_alpha` | FLOAT | 0.7 | Semantic weight |
1349
+ | `p_project_id` | UUID | null | Filter by project |
1350
+ | `p_min_score` | FLOAT | 0.0 | Minimum cosine similarity |
1351
+ | `p_small_to_big_threshold` | INT | 40000 | Documents larger than this return matched chunks + neighbours instead of the full document. Set to `0` to always return full content. Change the DEFAULT in `rpcs.sql` to apply server-wide. |
1352
+ | `p_context_window` | INT | 1 | Neighbour chunks on each side of each matched chunk. `1` → up to 3 contiguous chunks per hit. `0` → matched chunks only. |
1353
+
1354
+ Returns: `document_id`, `doc_title`, `doc_source`, `doc_metadata`, `doc_project_ids`,
1355
+ `best_score`, `best_chunk_heading_path`, `full_content`, `chunk_count`, `total_chars`,
1356
+ `doc_updated_at`, `version_count`, `is_partial`.
1357
+
1358
+ `is_partial` is `TRUE` when the document exceeded `p_small_to_big_threshold` — in that
1359
+ case `full_content` contains matched chunks + up to `p_context_window` neighbours on each
1360
+ side, deduplicated and sorted by `chunk_index`. `total_chars` always reflects the full
1361
+ document size regardless of whether the result is partial.
1362
+
1363
+ ---
1364
+
1365
+ ### Document RPCs
1366
+
1367
+ #### `cerefox_reconstruct_doc`
1368
+
1369
+ Fetch a full document by ID, concatenating all chunks in order.
1370
+
1371
+ | Parameter | Type | Description |
1372
+ |-----------|------|-------------|
1373
+ | `p_document_id` | UUID | Document to reconstruct |
1374
+
1375
+ Returns: `document_id`, `doc_title`, `doc_source`, `doc_metadata`, `full_content`,
1376
+ `chunk_count`, `total_chars`
1377
+
1378
+ #### `cerefox_context_expand`
1379
+
1380
+ Small-to-big retrieval: given a set of chunk IDs, returns those chunks **plus their immediate
1381
+ neighbours** (±`p_window_size` chunks within the same document).
1382
+
1383
+ | Parameter | Type | Default | Description |
1384
+ |-----------|------|---------|-------------|
1385
+ | `p_chunk_ids` | UUID[] | required | Array of chunk UUIDs from search results |
1386
+ | `p_window_size` | INT | 1 | Chunks to expand in each direction |
1387
+
1388
+ Returns: `chunk_id`, `document_id`, `chunk_index`, `title`, `content`, `heading_path`,
1389
+ `heading_level`, `doc_title`, `is_seed` (TRUE for the original seed chunks)
1390
+
1391
+ #### `cerefox_save_note`
1392
+
1393
+ Create a document record directly. The note is stored but **not embedded** — use `cerefox-ingest`
1394
+ Edge Function instead for notes that need to be immediately searchable.
1395
+
1396
+ | Parameter | Type | Default | Description |
1397
+ |-----------|------|---------|-------------|
1398
+ | `p_title` | TEXT | required | Note title |
1399
+ | `p_content` | TEXT | required | Markdown content |
1400
+ | `p_source` | TEXT | `'agent'` | Origin label |
1401
+ | `p_project_id` | UUID | null | Project to assign |
1402
+ | `p_metadata` | JSONB | `{}` | Metadata (agent name, tags, etc.) |
1403
+
1404
+ Returns: `id`, `title`, `created_at`
1405
+
1406
+ ---
1407
+
1408
+ ### Metadata RPCs
1409
+
1410
+ #### `cerefox_list_metadata_keys`
1411
+
1412
+ No parameters. Returns all distinct metadata keys currently in use across documents.
1413
+
1414
+ | Column | Type | Description |
1415
+ |--------|------|-------------|
1416
+ | `key` | TEXT | Metadata key name |
1417
+ | `doc_count` | BIGINT | Number of documents using this key |
1418
+ | `example_values` | TEXT[] | Up to 5 sample values |
1419
+
1420
+ This RPC derives keys from actual `doc_metadata` JSONB — no separate registry table.
1421
+
1422
+ ---
1423
+
1424
+ ## Response size
1425
+
1426
+ Cerefox's default `max_response_bytes = 200000` is a safety ceiling; small-to-big retrieval
1427
+ keeps individual results compact so this limit is rarely reached in practice. If your MCP
1428
+ client has a lower context limit, reduce it via `CEREFOX_MAX_RESPONSE_BYTES` in your `.env`.