@cerefox/memory 0.7.1 → 0.8.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.
Files changed (48) hide show
  1. package/README.md +62 -25
  2. package/dist/bin/cerefox.js +1163 -344
  3. package/dist/frontend/assets/{index-HNlMcvli.js → index-CAp2_lFX.js} +2 -2
  4. package/dist/frontend/assets/index-CAp2_lFX.js.map +1 -0
  5. package/dist/frontend/index.html +1 -1
  6. package/dist/server-assets/_shared/ef-meta/index.ts +97 -0
  7. package/dist/server-assets/_shared/embeddings/index.ts +175 -0
  8. package/dist/server-assets/_shared/mcp-tools/_chunker.ts +187 -0
  9. package/dist/server-assets/_shared/mcp-tools/_projects.ts +121 -0
  10. package/dist/server-assets/_shared/mcp-tools/_utils.ts +73 -0
  11. package/dist/server-assets/_shared/mcp-tools/audit-log.ts +95 -0
  12. package/dist/server-assets/_shared/mcp-tools/get-document.ts +73 -0
  13. package/dist/server-assets/_shared/mcp-tools/get-help-content.ts +26 -0
  14. package/dist/server-assets/_shared/mcp-tools/get-help.ts +90 -0
  15. package/dist/server-assets/_shared/mcp-tools/index.ts +67 -0
  16. package/dist/server-assets/_shared/mcp-tools/ingest.ts +315 -0
  17. package/dist/server-assets/_shared/mcp-tools/list-metadata-keys.ts +55 -0
  18. package/dist/server-assets/_shared/mcp-tools/list-projects.ts +59 -0
  19. package/dist/server-assets/_shared/mcp-tools/list-versions.ts +72 -0
  20. package/dist/server-assets/_shared/mcp-tools/metadata-search.ts +154 -0
  21. package/dist/server-assets/_shared/mcp-tools/search.ts +193 -0
  22. package/dist/server-assets/_shared/mcp-tools/set-document-projects.ts +163 -0
  23. package/dist/server-assets/_shared/mcp-tools/types.ts +92 -0
  24. package/dist/server-assets/db/migrations/0003_add_document_versions.sql +91 -0
  25. package/dist/server-assets/db/migrations/0004_add_audit_log_review_status_archived.sql +71 -0
  26. package/dist/server-assets/db/migrations/0005_metadata_search.sql +628 -0
  27. package/dist/server-assets/db/migrations/0006_usage_log.sql +255 -0
  28. package/dist/server-assets/db/migrations/0007_usage_log_requestor.sql +178 -0
  29. package/dist/server-assets/db/migrations/0008_soft_delete.sql +130 -0
  30. package/dist/server-assets/db/migrations/0009_audit_log_restore_operation.sql +20 -0
  31. package/dist/server-assets/db/migrations/0010_requestor_enforcement_config.sql +12 -0
  32. package/dist/server-assets/db/migrations/0011_title_boosting.sql +48 -0
  33. package/dist/server-assets/db/rpcs.sql +1723 -0
  34. package/dist/server-assets/db/schema.sql +380 -0
  35. package/dist/server-assets/supabase/functions/cerefox-get-audit-log/index.ts +117 -0
  36. package/dist/server-assets/supabase/functions/cerefox-get-document/index.ts +138 -0
  37. package/dist/server-assets/supabase/functions/cerefox-ingest/index.ts +819 -0
  38. package/dist/server-assets/supabase/functions/cerefox-list-projects/index.ts +96 -0
  39. package/dist/server-assets/supabase/functions/cerefox-list-versions/index.ts +113 -0
  40. package/dist/server-assets/supabase/functions/cerefox-mcp/index.ts +294 -0
  41. package/dist/server-assets/supabase/functions/cerefox-mcp/shared.ts +42 -0
  42. package/dist/server-assets/supabase/functions/cerefox-metadata/index.ts +99 -0
  43. package/dist/server-assets/supabase/functions/cerefox-metadata-search/index.ts +146 -0
  44. package/dist/server-assets/supabase/functions/cerefox-search/index.ts +382 -0
  45. package/docs/guides/connect-agents.md +58 -3
  46. package/docs/guides/migration-v0.5.md +50 -0
  47. package/package.json +3 -2
  48. package/dist/frontend/assets/index-HNlMcvli.js.map +0 -1
@@ -0,0 +1,95 @@
1
+ /**
2
+ * `cerefox_get_audit_log` — retrieve audit log entries with filters
3
+ * (document, author, operation, time range, limit).
4
+ */
5
+
6
+ import type { MCPSupabaseClient } from "./types.ts";
7
+
8
+ import { logUsage } from "./_utils.ts";
9
+ import type { ToolContext, ToolDefinition } from "./types.ts";
10
+
11
+ async function handler(
12
+ supabase: MCPSupabaseClient,
13
+ args: Record<string, unknown>,
14
+ ctx: ToolContext,
15
+ ): Promise<string> {
16
+ const params: Record<string, unknown> = {};
17
+ if (args.document_id) params.p_document_id = args.document_id;
18
+ if (args.author) params.p_author = args.author;
19
+ if (args.operation) params.p_operation = args.operation;
20
+ if (args.since) params.p_since = args.since;
21
+ if (args.until) params.p_until = args.until;
22
+ if (args.limit) params.p_limit = Math.min(Number(args.limit) || 50, 200);
23
+
24
+ const { data, error } = await supabase.rpc("cerefox_list_audit_entries", params);
25
+
26
+ if (error) throw new Error(`RPC error: ${error.message}`);
27
+
28
+ const entries = (data ?? []) as Array<{
29
+ id: string;
30
+ document_id: string | null;
31
+ doc_title: string | null;
32
+ operation: string;
33
+ author: string;
34
+ author_type: string;
35
+ size_before: number | null;
36
+ size_after: number | null;
37
+ description: string;
38
+ created_at: string;
39
+ }>;
40
+
41
+ logUsage(supabase, {
42
+ operation: "get_audit_log",
43
+ accessPath: ctx.accessPath,
44
+ requestor: args.requestor as string | undefined,
45
+ result_count: entries.length,
46
+ });
47
+
48
+ if (!entries.length) return "No audit log entries found.";
49
+
50
+ const lines = entries.map((e) => {
51
+ const docLabel =
52
+ e.doc_title ?? (e.document_id ? e.document_id.slice(0, 8) + "..." : "(deleted)");
53
+ const sizeInfo =
54
+ e.size_before != null && e.size_after != null
55
+ ? ` | ${e.size_before} -> ${e.size_after} chars`
56
+ : e.size_after != null
57
+ ? ` | ${e.size_after} chars`
58
+ : "";
59
+ return `${e.created_at.slice(0, 19)} | ${e.operation} | ${e.author} (${e.author_type}) | ${docLabel}${sizeInfo} | ${e.description}`;
60
+ });
61
+ return `Audit log (${entries.length} entries, newest first):\n\n${lines.join("\n")}`;
62
+ }
63
+
64
+ export const auditLogTool: ToolDefinition = {
65
+ name: "cerefox_get_audit_log",
66
+ description:
67
+ "Retrieve audit log entries showing who changed what and when. Supports filtering by document, author, operation type, and time range. Returns entries with document titles, author attribution, size changes, and descriptions.",
68
+ inputSchema: {
69
+ type: "object",
70
+ required: [],
71
+ properties: {
72
+ document_id: { type: "string", description: "Filter by document UUID (optional)" },
73
+ author: { type: "string", description: "Filter by author name (optional)" },
74
+ operation: {
75
+ type: "string",
76
+ description:
77
+ "Filter by operation type: create, update-content, update-metadata, delete, status-change, archive, unarchive (optional)",
78
+ },
79
+ since: {
80
+ type: "string",
81
+ description: "ISO timestamp lower bound for temporal queries (optional)",
82
+ },
83
+ limit: {
84
+ type: "integer",
85
+ description: "Maximum number of entries to return (default: 50, max: 200)",
86
+ },
87
+ requestor: {
88
+ type: "string",
89
+ description:
90
+ 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent" if not provided. May be enforced via server config.',
91
+ },
92
+ },
93
+ },
94
+ handler,
95
+ };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * `cerefox_get_document` — retrieve full document content by ID. Pass
3
+ * `version_id` (from `cerefox_list_versions`) to retrieve an archived
4
+ * version; omit for the current version.
5
+ */
6
+
7
+ import type { MCPSupabaseClient } from "./types.ts";
8
+
9
+ import { logUsage } from "./_utils.ts";
10
+ import { McpInvalidParams, type ToolContext, type ToolDefinition } from "./types.ts";
11
+
12
+ async function handler(
13
+ supabase: MCPSupabaseClient,
14
+ args: Record<string, unknown>,
15
+ ctx: ToolContext,
16
+ ): Promise<string> {
17
+ const document_id = args.document_id as string | undefined;
18
+ const version_id = (args.version_id as string | null | undefined) ?? null;
19
+
20
+ if (!document_id) throw new McpInvalidParams("document_id is required");
21
+
22
+ const { data, error } = await supabase.rpc("cerefox_get_document", {
23
+ p_document_id: document_id,
24
+ p_version_id: version_id,
25
+ });
26
+
27
+ if (error) throw new Error(`RPC error: ${error.message}`);
28
+
29
+ const row = data?.[0] as
30
+ | {
31
+ doc_title?: string;
32
+ full_content?: string;
33
+ chunk_count?: number;
34
+ total_chars?: number;
35
+ }
36
+ | undefined;
37
+
38
+ if (!row) return "Document not found.";
39
+
40
+ logUsage(supabase, {
41
+ operation: "get_document",
42
+ accessPath: ctx.accessPath,
43
+ requestor: args.requestor as string | undefined,
44
+ document_id,
45
+ result_count: 1,
46
+ });
47
+
48
+ const label = version_id !== null ? " (archived version)" : " (current)";
49
+ return `# ${row.doc_title ?? "Untitled"}${label}\n\n${row.full_content ?? ""}`;
50
+ }
51
+
52
+ export const getDocumentTool: ToolDefinition = {
53
+ name: "cerefox_get_document",
54
+ description:
55
+ "Retrieve the full reconstructed content of a document. Pass version_id to retrieve an archived version; omit it (or pass null) for the current version. Version UUIDs are returned by cerefox_list_versions.",
56
+ inputSchema: {
57
+ type: "object",
58
+ required: ["document_id"],
59
+ properties: {
60
+ document_id: { type: "string", description: "UUID of the document to retrieve" },
61
+ version_id: {
62
+ type: "string",
63
+ description: "UUID of a specific archived version to retrieve (optional)",
64
+ },
65
+ requestor: {
66
+ type: "string",
67
+ description:
68
+ 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent" if not provided. May be enforced via server config.',
69
+ },
70
+ },
71
+ },
72
+ handler,
73
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * GENERATED FILE — do not edit by hand.
3
+ *
4
+ * Generated by `bun scripts/bundle_help.ts` from AGENT_QUICK_REFERENCE.md.
5
+ * Re-run after editing the source file. CI's `scripts/check_help_bundle.ts`
6
+ * fails if this file diverges from the source.
7
+ *
8
+ * Bundled into both `@cerefox/memory` and the `cerefox-mcp` Edge Function
9
+ * so the `cerefox_get_help` MCP tool can return content without filesystem
10
+ * access. Layer 3 of the MCP discoverability response — see
11
+ * docs/specs/polish-and-distribution-design.md §10d.
12
+ */
13
+
14
+ export const HELP_FULL = "# Cerefox Knowledge Base -- Agent Quick Reference\n\nCerefox is a persistent, shared knowledge base. You have **10 MCP tools** (9 of them have CLI equivalents — `cerefox_get_help` is MCP-only). For the full guide, search Cerefox for \"How AI Agents Use Cerefox\" or call `cerefox_get_help` to retrieve this content over MCP.\n\n## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata`, `author` |\n| `cerefox_get_document` | Get full document by ID | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find docs by metadata (no text query) | `metadata_filter` (required), `include_content`, `updated_since` |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc's project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |\n\n## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., \"Claude Code\", \"archiver\"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user's `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` (\"decision-log\", \"research\", \"design-doc\") and `status` (\"active\", \"draft\").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don't write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don't construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc's full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean \"add\" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.\n\n## Update Workflow (ID-based -- preferred)\n\n```\nsearch(\"topic\") -> find doc [id: abc123] -> get_document(abc123) -> modify ->\ningest(title=\"Same Title\", content=\"...\", document_id=\"abc123\", author=\"my-agent\")\n```\n\n## Update Workflow (title-based -- fallback)\n\n```\nsearch(\"topic\") -> find doc -> modify ->\ningest(title=\"Same Title\", content=\"...\", update_if_exists=true, author=\"my-agent\")\n```\n\n## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={\"type\": \"decision-log\"}, updated_since=\"2026-03-28T00:00:00Z\")\n```\n\n## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. From v0.5+ the canonical invocation is plain **`cerefox <subcommand>`** (installed via `npm install -g @cerefox/memory`). The legacy `uv run cerefox <subcommand>` (Python CLI in a Cerefox checkout) still works through v0.7 but emits a deprecation banner.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); short forms (`--filter`, `--project`, `--count`, `--update`, `--version`) work as aliases.\n\n| MCP tool | CLI (v0.5+ canonical) |\n|---|---|\n| `cerefox_search` | `cerefox search \"<q>\" --requestor \"<your-name>\"` |\n| `cerefox_ingest` (paste) | `printf '...' \\| cerefox ingest --paste --title \"<t>\" --author \"<your-name>\" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf '...' \\| cerefox ingest --paste --title \"<t>\" --document-id \"<uuid>\" --author \"<your-name>\" --author-type agent` |\n| `cerefox_get_document` | `cerefox get-doc <id> --version-id <vid> --requestor \"<your-name>\"` |\n| `cerefox_list_versions` | `cerefox list-versions <id> --requestor \"<your-name>\"` |\n| `cerefox_list_projects` | `cerefox list-projects --requestor \"<your-name>\"` |\n| `cerefox_list_metadata_keys` | `cerefox list-metadata-keys` |\n| `cerefox_metadata_search` | `cerefox metadata-search --metadata-filter '<json>' --requestor \"<your-name>\"` |\n| `cerefox_set_document_projects` | _MCP-only; a CLI command will be added in a future release. Until then, run via MCP if available._ |\n| `cerefox_get_audit_log` | `cerefox get-audit-log --requestor \"<your-name>\"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox docs agent-quick-reference --print` (or `cerefox docs --list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`ingest`, `ingest-dir`): `--author \"<your-name>\" --author-type agent`\n- Reads: `--requestor \"<your-name>\"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.\n";
15
+
16
+ /** Sections keyed by their H2 heading text (lower-cased for matching). */
17
+ export const HELP_SECTIONS: Record<string, string> = {
18
+ "Tools": "## Tools\n\n| Tool | Purpose | Key params |\n|------|---------|------------|\n| `cerefox_search` | Find documents (hybrid FTS + semantic) | `query` (required), `project_name`, `metadata_filter`, `requestor` |\n| `cerefox_ingest` | Save or update a document | `title`, `content` (required), `document_id` (update by ID), `update_if_exists`, `project_name` (single, non-destructive add on update), `project_names` (list, destructive replace on update), `metadata`, `author` |\n| `cerefox_get_document` | Get full document by ID | `document_id` (required) |\n| `cerefox_list_versions` | Version history of a document | `document_id` (required) |\n| `cerefox_metadata_search` | Find docs by metadata (no text query) | `metadata_filter` (required), `include_content`, `updated_since` |\n| `cerefox_list_metadata_keys` | Discover available metadata keys | (none required) |\n| `cerefox_list_projects` | List all projects | (none required) |\n| `cerefox_set_document_projects` | Set doc's project memberships to exactly the given list (destructive replace; metadata-only, no content change) | `document_id`, `project_names` (required) |\n| `cerefox_get_audit_log` | Query write operation history | `document_id`, `author`, `operation`, `since` |\n| `cerefox_get_help` | Retrieve Cerefox conventions (this reference) over MCP. **Call this whenever uncertain.** | `topic` (optional, case-insensitive H2 substring match) |",
19
+ "Essential Rules": "## Essential Rules\n\n1. **Search before ingesting** -- check if the document exists first.\n2. **Prefer ID-based updates** -- pass `document_id` from search results for deterministic updates. Falls back to title-matching with `update_if_exists: true`.\n3. **Set `author`/`requestor`** to your name on every call (e.g., \"Claude Code\", \"archiver\"). On MCP, pass as parameters. On CLI, pass `--author`/`--author-type`/`--requestor` flags, or rely on `CEREFOX_AUTHOR_NAME`/`CEREFOX_AUTHOR_TYPE`/`CEREFOX_REQUESTOR_NAME` env vars set in the user's `.env`.\n4. **Use `document_id` from search results** `[id: uuid]` for get_document and list_versions.\n5. **Add metadata** -- at minimum `type` (\"decision-log\", \"research\", \"design-doc\") and `status` (\"active\", \"draft\").\n6. **Write structured Markdown** with H1/H2/H3 headings for good chunking and search.\n7. **Deletes are soft (recoverable); purge is web-UI-only.** If you decide to delete, surface it to the user (`I soft-deleted X — recoverable from the Cerefox web UI trash`). You cannot un-do your own delete from agent code by design.\n8. **Cross-doc links inside content**: **always use `[Text](document-uuid)`.** UUIDs are the only fully reliable link form — stable across title changes, never ambiguous, no encoding gotchas. Every `cerefox_search` result shows `[id: <uuid>]` after the title; grab it and use it. Title-based linking (`[Text](<Title With Spaces>)`) is fragile (breaks on colons, parens, ampersands, brackets — silently navigates to wrong page) — **don't write title-based links**; do an extra search to get the UUID instead. Repo-path forms (`[Text](docs/path.md)`) exist for repo-ingested files; don't construct manually. See `AGENT_GUIDE.md → Writing linkable content` for the full rule.\n9. **Project memberships — non-destructive by default**: on `cerefox_ingest` updates, **`project_name` (singular) is a non-destructive add** (ensures membership, preserves others). Use **`project_names` (list)** when you want to set the doc's full project set in one call (destructive replace). For metadata-only project changes without writing content, use **`cerefox_set_document_projects(document_id, project_names)`** — that tool is the destructive-replace contract made explicit. Never call `cerefox_set_document_projects` with a single name when you mean \"add\" — that would REMOVE the doc from all other projects. When in doubt, use `cerefox_ingest` with singular `project_name`.",
20
+ "Update Workflow (ID-based -- preferred)": "## Update Workflow (ID-based -- preferred)\n\n```\nsearch(\"topic\") -> find doc [id: abc123] -> get_document(abc123) -> modify ->\ningest(title=\"Same Title\", content=\"...\", document_id=\"abc123\", author=\"my-agent\")\n```",
21
+ "Update Workflow (title-based -- fallback)": "## Update Workflow (title-based -- fallback)\n\n```\nsearch(\"topic\") -> find doc -> modify ->\ningest(title=\"Same Title\", content=\"...\", update_if_exists=true, author=\"my-agent\")\n```",
22
+ "Catch-Up Workflow": "## Catch-Up Workflow\n\n```\nmetadata_search(metadata_filter={\"type\": \"decision-log\"}, updated_since=\"2026-03-28T00:00:00Z\")\n```",
23
+ "CLI fallback (when MCP is unavailable)": "## CLI fallback (when MCP is unavailable)\n\nIf `cerefox_search` is not in your tool list, your user has likely installed the Cerefox CLI. From v0.5+ the canonical invocation is plain **`cerefox <subcommand>`** (installed via `npm install -g @cerefox/memory`). The legacy `uv run cerefox <subcommand>` (Python CLI in a Cerefox checkout) still works through v0.7 but emits a deprecation banner.\n\nSame operations, same conventions. Full reference: [`docs/guides/cli.md`](docs/guides/cli.md). CLI flag names match MCP parameter names exactly (e.g. `metadata_filter` ↔ `--metadata-filter`); short forms (`--filter`, `--project`, `--count`, `--update`, `--version`) work as aliases.\n\n| MCP tool | CLI (v0.5+ canonical) |\n|---|---|\n| `cerefox_search` | `cerefox search \"<q>\" --requestor \"<your-name>\"` |\n| `cerefox_ingest` (paste) | `printf '...' \\| cerefox ingest --paste --title \"<t>\" --author \"<your-name>\" --author-type agent` |\n| `cerefox_ingest` (update by ID) | `printf '...' \\| cerefox ingest --paste --title \"<t>\" --document-id \"<uuid>\" --author \"<your-name>\" --author-type agent` |\n| `cerefox_get_document` | `cerefox get-doc <id> --version-id <vid> --requestor \"<your-name>\"` |\n| `cerefox_list_versions` | `cerefox list-versions <id> --requestor \"<your-name>\"` |\n| `cerefox_list_projects` | `cerefox list-projects --requestor \"<your-name>\"` |\n| `cerefox_list_metadata_keys` | `cerefox list-metadata-keys` |\n| `cerefox_metadata_search` | `cerefox metadata-search --metadata-filter '<json>' --requestor \"<your-name>\"` |\n| `cerefox_set_document_projects` | _MCP-only; a CLI command will be added in a future release. Until then, run via MCP if available._ |\n| `cerefox_get_audit_log` | `cerefox get-audit-log --requestor \"<your-name>\"` (add `--json` for scripted access) |\n| `cerefox_get_help` | `cerefox docs agent-quick-reference --print` (or `cerefox docs --list` for the full bundled-docs index) |\n\n**Set identity on every call**, exactly as you would on MCP:\n- Writes (`ingest`, `ingest-dir`): `--author \"<your-name>\" --author-type agent`\n- Reads: `--requestor \"<your-name>\"`\n\nOr have your user set `CEREFOX_AUTHOR_NAME` / `CEREFOX_AUTHOR_TYPE` / `CEREFOX_REQUESTOR_NAME` in their `.env` to apply defaults once.",
24
+ };
25
+
26
+ export const HELP_SECTION_HEADINGS: string[] = ["Tools", "Essential Rules", "Update Workflow (ID-based -- preferred)", "Update Workflow (title-based -- fallback)", "Catch-Up Workflow", "CLI fallback (when MCP is unavailable)"];
@@ -0,0 +1,90 @@
1
+ /**
2
+ * `cerefox_get_help` — return curated agent guidance.
3
+ *
4
+ * Layer 3 of the MCP discoverability response (design doc §10d). Bundles
5
+ * `AGENT_QUICK_REFERENCE.md` whole so remote / hosted-MCP / Edge-Function-only
6
+ * agents — who can't read the repo filesystem — have an in-protocol way to
7
+ * discover Cerefox conventions.
8
+ *
9
+ * The bundle is generated by `scripts/bundle_help.ts` from
10
+ * `AGENT_QUICK_REFERENCE.md` and CI-checked by `scripts/check_help_bundle.ts`.
11
+ *
12
+ * Topic filter: case-insensitive substring match against H2 headings (e.g.
13
+ * `topic="links"` matches the section whose heading contains "links",
14
+ * `topic="update"` matches both "Update Workflow" sections). No topic →
15
+ * the full file plus an index of section headings.
16
+ */
17
+
18
+ import type { MCPSupabaseClient } from "./types.ts";
19
+
20
+ import { logUsage } from "./_utils.ts";
21
+ import {
22
+ HELP_FULL,
23
+ HELP_SECTION_HEADINGS,
24
+ HELP_SECTIONS,
25
+ } from "./get-help-content.ts";
26
+ import type { ToolContext, ToolDefinition } from "./types.ts";
27
+
28
+ async function handler(
29
+ supabase: MCPSupabaseClient,
30
+ args: Record<string, unknown>,
31
+ ctx: ToolContext,
32
+ ): Promise<string> {
33
+ const topic = (args.topic as string | undefined)?.trim();
34
+
35
+ logUsage(supabase, {
36
+ operation: "get_help",
37
+ accessPath: ctx.accessPath,
38
+ requestor: args.requestor as string | undefined,
39
+ query_text: topic ?? null,
40
+ result_count: 1,
41
+ });
42
+
43
+ if (!topic) {
44
+ const idx = HELP_SECTION_HEADINGS.map((h) => ` - ${h}`).join("\n");
45
+ return (
46
+ HELP_FULL +
47
+ "\n\n---\n\n" +
48
+ "## Available topics\n\n" +
49
+ "Call `cerefox_get_help(topic: \"<heading>\")` to retrieve a single section.\n\n" +
50
+ idx +
51
+ "\n\n(Topic match is case-insensitive substring on the headings above.)"
52
+ );
53
+ }
54
+
55
+ const t = topic.toLowerCase();
56
+ const matched = HELP_SECTION_HEADINGS.filter((h) => h.toLowerCase().includes(t));
57
+
58
+ if (matched.length === 0) {
59
+ return (
60
+ `No help topic matched "${topic}".\n\n` +
61
+ `Available topics:\n` +
62
+ HELP_SECTION_HEADINGS.map((h) => ` - ${h}`).join("\n") +
63
+ "\n\nCall `cerefox_get_help()` with no topic for the full document."
64
+ );
65
+ }
66
+
67
+ return matched.map((h) => HELP_SECTIONS[h]).join("\n\n---\n\n");
68
+ }
69
+
70
+ export const getHelpTool: ToolDefinition = {
71
+ name: "cerefox_get_help",
72
+ description:
73
+ "Retrieve Cerefox's own agent-usage guidance (the AGENT_QUICK_REFERENCE.md content). Call with no arguments to get the full reference + a section index. Call with `topic` to get a single section (case-insensitive substring match against H2 headings). Use this whenever you're uncertain about Cerefox conventions — link forms, project-membership semantics, update workflows, etc.",
74
+ inputSchema: {
75
+ type: "object",
76
+ properties: {
77
+ topic: {
78
+ type: "string",
79
+ description:
80
+ 'Optional. Case-insensitive substring against H2 headings (e.g. "links", "update", "metadata"). Omit for the full reference.',
81
+ },
82
+ requestor: {
83
+ type: "string",
84
+ description:
85
+ 'Name of the agent or user making this request. Recorded in the usage log. Defaults to "mcp-agent".',
86
+ },
87
+ },
88
+ },
89
+ handler,
90
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * `_shared/mcp-tools/` — runtime-neutral MCP tool handlers.
3
+ *
4
+ * Single source of truth for the Cerefox MCP tool surface. Imported by:
5
+ * - `supabase/functions/cerefox-mcp/index.ts` (Deno; HTTP transport).
6
+ * - `packages/memory/src/server.ts` (Bun/Node; stdio transport).
7
+ *
8
+ * Adding a new tool: write `<name>.ts` exporting a `ToolDefinition`, then
9
+ * add it to `ALL_TOOLS` below. Both consumers pick it up automatically.
10
+ *
11
+ * Tool surface (v0.4.0): 10 tools.
12
+ */
13
+
14
+ import { auditLogTool } from "./audit-log.ts";
15
+ import { getDocumentTool } from "./get-document.ts";
16
+ import { getHelpTool } from "./get-help.ts";
17
+ import { ingestTool } from "./ingest.ts";
18
+ import { listMetadataKeysTool } from "./list-metadata-keys.ts";
19
+ import { listProjectsTool } from "./list-projects.ts";
20
+ import { listVersionsTool } from "./list-versions.ts";
21
+ import { metadataSearchTool } from "./metadata-search.ts";
22
+ import { searchTool } from "./search.ts";
23
+ import { setDocumentProjectsTool } from "./set-document-projects.ts";
24
+ import type { ToolDefinition } from "./types.ts";
25
+
26
+ /** All Cerefox MCP tools, in canonical order (matches AGENT_QUICK_REFERENCE.md). */
27
+ export const ALL_TOOLS: ToolDefinition[] = [
28
+ searchTool,
29
+ ingestTool,
30
+ getDocumentTool,
31
+ listVersionsTool,
32
+ metadataSearchTool,
33
+ listMetadataKeysTool,
34
+ listProjectsTool,
35
+ setDocumentProjectsTool,
36
+ auditLogTool,
37
+ getHelpTool,
38
+ ];
39
+
40
+ /** Build a name → definition map for fast dispatch. */
41
+ export const TOOLS_BY_NAME: Record<string, ToolDefinition> = Object.fromEntries(
42
+ ALL_TOOLS.map((t) => [t.name, t]),
43
+ );
44
+
45
+ export { McpInvalidParams } from "./types.ts";
46
+ export type {
47
+ AccessPath,
48
+ JsonSchema,
49
+ ToolContext,
50
+ ToolDefinition,
51
+ } from "./types.ts";
52
+
53
+ // Re-export individual tools so non-MCP consumers (e.g. the v0.5 CLI's
54
+ // `ingest` / `delete-doc` commands) can call a specific handler directly
55
+ // without going through ALL_TOOLS dispatch.
56
+ export {
57
+ auditLogTool,
58
+ getDocumentTool,
59
+ getHelpTool,
60
+ ingestTool,
61
+ listMetadataKeysTool,
62
+ listProjectsTool,
63
+ listVersionsTool,
64
+ metadataSearchTool,
65
+ searchTool,
66
+ setDocumentProjectsTool,
67
+ };