r2mcp 0.2.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 (138) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/LICENSE +21 -0
  3. package/README.md +532 -0
  4. package/dist/breadcrumbs.d.ts +123 -0
  5. package/dist/breadcrumbs.js +135 -0
  6. package/dist/cli/classify-edges.d.ts +2 -0
  7. package/dist/cli/classify-edges.js +130 -0
  8. package/dist/cli/compile-wiki.d.ts +2 -0
  9. package/dist/cli/compile-wiki.js +173 -0
  10. package/dist/cli/dump-edges-json.d.ts +2 -0
  11. package/dist/cli/dump-edges-json.js +21 -0
  12. package/dist/cli/extract-entities.d.ts +17 -0
  13. package/dist/cli/extract-entities.js +166 -0
  14. package/dist/cli/lint-memory.d.ts +16 -0
  15. package/dist/cli/lint-memory.js +94 -0
  16. package/dist/cli/migrate.d.ts +17 -0
  17. package/dist/cli/migrate.js +146 -0
  18. package/dist/cli/setup-helpers.d.ts +7 -0
  19. package/dist/cli/setup-helpers.js +72 -0
  20. package/dist/cli/setup.d.ts +15 -0
  21. package/dist/cli/setup.js +95 -0
  22. package/dist/compiler/clustering.d.ts +29 -0
  23. package/dist/compiler/clustering.js +66 -0
  24. package/dist/compiler/frontmatter.d.ts +35 -0
  25. package/dist/compiler/frontmatter.js +168 -0
  26. package/dist/compiler/manifest.d.ts +32 -0
  27. package/dist/compiler/manifest.js +82 -0
  28. package/dist/compiler/prompts.d.ts +17 -0
  29. package/dist/compiler/prompts.js +82 -0
  30. package/dist/compiler/run.d.ts +52 -0
  31. package/dist/compiler/run.js +186 -0
  32. package/dist/compiler/tier.d.ts +10 -0
  33. package/dist/compiler/tier.js +85 -0
  34. package/dist/compiler/topic.d.ts +16 -0
  35. package/dist/compiler/topic.js +105 -0
  36. package/dist/compiler/types.d.ts +101 -0
  37. package/dist/compiler/types.js +4 -0
  38. package/dist/db.d.ts +10 -0
  39. package/dist/db.js +46 -0
  40. package/dist/edges/candidate-pairs.d.ts +24 -0
  41. package/dist/edges/candidate-pairs.js +35 -0
  42. package/dist/edges/classifier.d.ts +45 -0
  43. package/dist/edges/classifier.js +172 -0
  44. package/dist/edges/signals.d.ts +13 -0
  45. package/dist/edges/signals.js +45 -0
  46. package/dist/edges/stage1-haiku.d.ts +21 -0
  47. package/dist/edges/stage1-haiku.js +33 -0
  48. package/dist/edges/stage2-opus.d.ts +41 -0
  49. package/dist/edges/stage2-opus.js +101 -0
  50. package/dist/edges/state.d.ts +44 -0
  51. package/dist/edges/state.js +79 -0
  52. package/dist/edges/types.d.ts +20 -0
  53. package/dist/edges/types.js +1 -0
  54. package/dist/embeddings.d.ts +13 -0
  55. package/dist/embeddings.js +54 -0
  56. package/dist/entities/db.d.ts +49 -0
  57. package/dist/entities/db.js +109 -0
  58. package/dist/entities/extractor.d.ts +14 -0
  59. package/dist/entities/extractor.js +154 -0
  60. package/dist/entities/normalize.d.ts +5 -0
  61. package/dist/entities/normalize.js +7 -0
  62. package/dist/entities/prompt.d.ts +19 -0
  63. package/dist/entities/prompt.js +100 -0
  64. package/dist/entities/state.d.ts +44 -0
  65. package/dist/entities/state.js +99 -0
  66. package/dist/entities/types.d.ts +62 -0
  67. package/dist/entities/types.js +6 -0
  68. package/dist/env.d.ts +13 -0
  69. package/dist/env.js +32 -0
  70. package/dist/fingerprint.d.ts +2 -0
  71. package/dist/fingerprint.js +12 -0
  72. package/dist/graph-rebuild.d.ts +6 -0
  73. package/dist/graph-rebuild.js +20 -0
  74. package/dist/index.d.ts +4 -0
  75. package/dist/index.js +403 -0
  76. package/dist/instrumentation.d.ts +10 -0
  77. package/dist/instrumentation.js +37 -0
  78. package/dist/lint/checks/contradictions.d.ts +30 -0
  79. package/dist/lint/checks/contradictions.js +52 -0
  80. package/dist/lint/checks/drift.d.ts +5 -0
  81. package/dist/lint/checks/drift.js +34 -0
  82. package/dist/lint/checks/orphans.d.ts +5 -0
  83. package/dist/lint/checks/orphans.js +25 -0
  84. package/dist/lint/checks/stale.d.ts +6 -0
  85. package/dist/lint/checks/stale.js +29 -0
  86. package/dist/lint/checks/superseded-unflagged.d.ts +5 -0
  87. package/dist/lint/checks/superseded-unflagged.js +47 -0
  88. package/dist/lint/run.d.ts +11 -0
  89. package/dist/lint/run.js +95 -0
  90. package/dist/lint/types.d.ts +60 -0
  91. package/dist/lint/types.js +13 -0
  92. package/dist/mcp-response.d.ts +7 -0
  93. package/dist/mcp-response.js +13 -0
  94. package/dist/providers/anthropic.d.ts +13 -0
  95. package/dist/providers/anthropic.js +56 -0
  96. package/dist/providers/claude-code.d.ts +35 -0
  97. package/dist/providers/claude-code.js +175 -0
  98. package/dist/providers/errors.d.ts +12 -0
  99. package/dist/providers/errors.js +19 -0
  100. package/dist/providers/index.d.ts +30 -0
  101. package/dist/providers/index.js +71 -0
  102. package/dist/providers/openrouter.d.ts +19 -0
  103. package/dist/providers/openrouter.js +76 -0
  104. package/dist/providers/semaphore.d.ts +19 -0
  105. package/dist/providers/semaphore.js +51 -0
  106. package/dist/providers/types.d.ts +27 -0
  107. package/dist/providers/types.js +7 -0
  108. package/dist/schema.sql +116 -0
  109. package/dist/server-instructions.d.ts +9 -0
  110. package/dist/server-instructions.js +20 -0
  111. package/dist/telemetry.d.ts +39 -0
  112. package/dist/telemetry.js +130 -0
  113. package/dist/tools/classify.d.ts +44 -0
  114. package/dist/tools/classify.js +121 -0
  115. package/dist/tools/compile.d.ts +31 -0
  116. package/dist/tools/compile.js +132 -0
  117. package/dist/tools/dump-edges-sidecar.d.ts +37 -0
  118. package/dist/tools/dump-edges-sidecar.js +80 -0
  119. package/dist/tools/extract-entities.d.ts +53 -0
  120. package/dist/tools/extract-entities.js +169 -0
  121. package/dist/tools/lint.d.ts +10 -0
  122. package/dist/tools/lint.js +13 -0
  123. package/dist/tools/meditate.d.ts +25 -0
  124. package/dist/tools/meditate.js +128 -0
  125. package/dist/tools/recall.d.ts +66 -0
  126. package/dist/tools/recall.js +409 -0
  127. package/dist/tools/reject.d.ts +10 -0
  128. package/dist/tools/reject.js +24 -0
  129. package/dist/tools/remember.d.ts +26 -0
  130. package/dist/tools/remember.js +140 -0
  131. package/dist/tools/search.d.ts +30 -0
  132. package/dist/tools/search.js +69 -0
  133. package/dist/tools/spawn-cli.d.ts +14 -0
  134. package/dist/tools/spawn-cli.js +41 -0
  135. package/dist/tools/stats.d.ts +31 -0
  136. package/dist/tools/stats.js +88 -0
  137. package/package.json +86 -0
  138. package/skills/remember/SKILL.md +357 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,66 @@
1
+ # Changelog
2
+
3
+ All notable changes to r2mcp. Versions follow [semver](https://semver.org/);
4
+ entries reference the internal spec numbers that shipped them.
5
+
6
+ ## [0.2.0] — 2026-06-13
7
+
8
+ First release packaged for use beyond the original workspace.
9
+
10
+ ### Added
11
+
12
+ - **npm bins**: `r2mcp` (the MCP server) and `r2mcp-setup` (schema provisioner) —
13
+ `.mcp.json` can now use `npx -y r2mcp` instead of an absolute dist path.
14
+ - **MCP server instructions** sent in the initialize response — a fresh project's
15
+ agent learns the recall-at-start / remember-as-you-go loop with zero setup.
16
+ - **`warnings[]` on `remember`/`recall`** when embeddings are unavailable,
17
+ distinguishing *disabled* (no `R2MCP_OPENROUTER_API_KEY`) from *failed*.
18
+ - **Fail-fast configuration**: the server and `npm run setup` refuse to start
19
+ without `R2MCP_DATABASE_URL` (previously defaulted silently to localhost);
20
+ startup logs a warning when embeddings are off.
21
+ - **`extract_entities` tool + entity-scoped `recall`** (SPEC-046): light entity
22
+ extraction (project/person/tool/decision) with alias merging and cost caps.
23
+ - **`next_tools[]` breadcrumbs** on every tool response (SPEC-047): context-aware
24
+ follow-up suggestions, capped at 3.
25
+ - **MCP-only operation** (SPEC-045): `classify` and `dump_edges_sidecar` promoted
26
+ to MCP tools; subprocess spawning centralized via `resolveCliCommand`;
27
+ `R2MCP_CLAUDE_BIN` escape hatch for sanitized-PATH hosts (now documented, and
28
+ named in ENOENT spawn errors).
29
+ - **MIT LICENSE**, engines field, repository metadata, this changelog.
30
+
31
+ ### Changed
32
+
33
+ - **Single-rootDir build**: CLI drivers moved from `scripts/` to `src/cli/`;
34
+ the dual compile tree (`dist/` + `dist/src/`, 43 duplicate files) is gone and
35
+ `schema.sql` is copied once. `npm run` script names are unchanged.
36
+ - **README onboarding overhaul**: Supabase **Session pooler** is the recommended
37
+ connection (the Direct connection is IPv6-only without a paid add-on; setup
38
+ classifies `ENETUNREACH` accordingly); `.mcp.json` examples use `${VAR}`
39
+ expansion with a secret-hygiene warning; the bundled `/remember` skill install
40
+ corrected to `.claude/skills/`; new Configuration table and Troubleshooting
41
+ section.
42
+
43
+ ### Fixed
44
+
45
+ - **`.env` loader regex could not match any `R2MCP_*` key** (the digit excluded
46
+ by `[A-Z_]+`) — every documented fresh-clone setup silently fell back to
47
+ localhost. All five hand-rolled loaders replaced by a shared `loadEnvFile()`.
48
+ - CLI drivers load `.env` only on their CLI entry path, never at module import
49
+ (a module-level load leaked the consumer's DB URL into test processes).
50
+ - `lint:memory` previously loaded no environment at all.
51
+
52
+ ## [0.1.0] — 2026-05-09
53
+
54
+ Initial extraction from the ClaudeClaw workspace (SPEC-041 through SPEC-044).
55
+
56
+ - 9 MCP tools: `remember`, `recall`, `search`, `meditate`, `reject`, `stats`,
57
+ `compile`, `lint`, `classify` over PostgreSQL + pgvector (Docker or Supabase
58
+ free tier).
59
+ - 3-tier memory (preferences / project-context / conversations) with semantic +
60
+ full-text hybrid retrieval, MMR diversity, progressive tier search,
61
+ token-budget retrieval (Recall v2).
62
+ - Typed memory edges (SPEC-043) surfaced as `signals[]` on recall.
63
+ - Wiki mode (SPEC-044): regenerable compiled views, SQL-only lint
64
+ (contradictions / stale / orphans / drift / superseded_unflagged).
65
+ - Multi-provider LLM layer: claude-code (Max plan, $0/call), Anthropic API,
66
+ OpenRouter — batch jobs only; the MCP server itself never makes LLM calls.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dustin Cheng
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,532 @@
1
+ # r2mcp — Persistent Memory for Claude Code
2
+
3
+ Persistent, semantic, tiered memory layer for Claude Code sessions.
4
+
5
+ **The problem:** Every Claude Code session starts fresh. Context is lost. You repeat yourself.
6
+
7
+ **The fix:** r2mcp gives Claude a structured, searchable memory that survives session boundaries — stored in PostgreSQL with pgvector semantic search.
8
+
9
+ ## What you get
10
+
11
+ - **11 MCP tools:** `remember`, `recall`, `search`, `meditate`, `reject`, `stats`, `compile`, `lint`, `classify`, `dump_edges_sidecar`, `extract_entities`
12
+ - **3-tier memory:** `preferences` (decisions, style) → `project-context` (architecture, state) → `conversations` (relationship, history)
13
+ - **Semantic search:** Progressive tier search with MMR diversity reranking and relevance floor filtering (Recall v2)
14
+ - **Typed memory edges:** `contradicts`, `supersedes`, `supports`, `evolved_into`, `depends_on`, `related_to` — surfaced as signals on `recall()`
15
+ - **Wiki compile:** Regenerable browsable views — `compile()` synthesizes `memory/compiled/` from pgvector
16
+ - **Lint as a first-class op:** SQL-only structural feedback — contradictions, stale, orphans, drift, superseded_unflagged
17
+ - **Multi-provider LLM layer:** Classifier and compile work on a Max plan ($0/call), Anthropic API, or OpenRouter — picked per-invocation
18
+ - **Bundled `/remember` skill:** Client-side judgment pipeline — classify → conflict-check → store
19
+
20
+ ## Setup
21
+
22
+ **Prerequisites:** Node.js 20+. An OpenRouter API key is strongly recommended — it powers semantic-search embeddings. Without one, r2mcp still works but degrades to full-text search (and tells you so via a startup warning and `warnings[]` on tool responses). Docker is optional (Option B only).
23
+
24
+ r2mcp works with any PostgreSQL + pgvector backend. The fastest path is Supabase (free tier, no Docker required).
25
+
26
+ ### Option A: Supabase (no Docker required)
27
+
28
+ #### 1. Create a Supabase project
29
+
30
+ Create a free project at [supabase.com](https://supabase.com). Once created, click **Connect** (top of the dashboard) and copy the **Session pooler** connection string — port `5432`, host like `aws-0-<region>.pooler.supabase.com`, username `postgres.<project-ref>`.
31
+
32
+ > **Why the Session pooler?** The Direct connection (`db.<ref>.supabase.co:5432`) resolves to an IPv6 address, and IPv4 for direct connections is a paid add-on — on an IPv4-only network it fails with `connect ENETUNREACH`. The Session pooler is IPv4-compatible on every tier and fully supports schema setup. (Do **not** use the Transaction pooler on port `6543` — it can't run DDL; setup will refuse it.) If your network has IPv6, the Direct connection works too.
33
+
34
+ #### 2. Clone and configure
35
+
36
+ ```bash
37
+ git clone https://github.com/DMokong/r2mcp.git && cd r2mcp && npm install
38
+ cp .env.example .env
39
+ # Set R2MCP_DATABASE_URL to your Session pooler URL (port 5432, not 6543)
40
+ # Set R2MCP_OPENROUTER_API_KEY to your OpenRouter key (enables semantic search)
41
+ ```
42
+
43
+ #### 3. Provision schema and build
44
+
45
+ ```bash
46
+ npm run setup && npm run build
47
+ ```
48
+
49
+ This creates the `memories` table, pgvector indexes, and full-text search index. **Safe to re-run.**
50
+
51
+ #### 4. Register in Claude Code
52
+
53
+ Add to your project's `.mcp.json`. Use `${VAR}` expansion so credentials stay in your environment instead of the file — **`.mcp.json` is typically committed, so never paste real credentials into it**:
54
+
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "memory": {
59
+ "command": "node",
60
+ "args": ["/path/to/r2mcp/dist/index.js"],
61
+ "env": {
62
+ "R2MCP_DATABASE_URL": "${R2MCP_DATABASE_URL}",
63
+ "R2MCP_OPENROUTER_API_KEY": "${R2MCP_OPENROUTER_API_KEY}"
64
+ }
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ Claude Code expands `${VAR}` (and `${VAR:-default}`) from your environment at launch. Inline literal values are fine only for throwaway local experiments — if you go that route, gitignore `.mcp.json` and treat any committed credential as compromised.
71
+
72
+ Restart Claude Code, then see [After setup](#after-setup-both-options).
73
+
74
+ ---
75
+
76
+ ### Option B: Docker (local dev)
77
+
78
+ For local development or air-gapped environments.
79
+
80
+ #### 1. Clone
81
+
82
+ ```bash
83
+ git clone https://github.com/DMokong/r2mcp.git
84
+ cd r2mcp
85
+ npm install
86
+ ```
87
+
88
+ #### 2. Configure
89
+
90
+ ```bash
91
+ cp .env.example .env
92
+ # Edit .env — set R2MCP_DATABASE_URL and R2MCP_OPENROUTER_API_KEY
93
+ ```
94
+
95
+ #### 3. Start Postgres
96
+
97
+ ```bash
98
+ docker compose up -d
99
+ # Wait ~10s for healthy status
100
+ ```
101
+
102
+ #### 4. Provision schema
103
+
104
+ ```bash
105
+ npm run setup
106
+ ```
107
+
108
+ This creates the `memories` table, pgvector indexes, and full-text search index. **Safe to re-run.**
109
+
110
+ #### 5. Build
111
+
112
+ ```bash
113
+ npm run build
114
+ ```
115
+
116
+ #### 6. Register in Claude Code
117
+
118
+ Add to your project's `.mcp.json`:
119
+
120
+ ```json
121
+ {
122
+ "mcpServers": {
123
+ "memory": {
124
+ "command": "node",
125
+ "args": ["/path/to/r2mcp/dist/index.js"],
126
+ "env": {
127
+ "R2MCP_DATABASE_URL": "postgresql://r2mcp:r2mcp@localhost:5432/r2mcp",
128
+ "R2MCP_OPENROUTER_API_KEY": "${R2MCP_OPENROUTER_API_KEY}"
129
+ }
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ (The local Docker DB URL contains no real secret; the OpenRouter key does — keep it in your environment via `${VAR}` expansion.)
136
+
137
+ Restart Claude Code, then see [After setup](#after-setup-both-options).
138
+
139
+ ## After setup (both options)
140
+
141
+ You now have `mcp__memory__remember`, `mcp__memory__recall`, etc. available. Two optional steps make memory actually get used:
142
+
143
+ ### Install the /remember skill (recommended)
144
+
145
+ The bundled skill gives Claude a judgment pipeline for memory writes — classify → conflict-check → store. Copy it into your **consuming project** (the one whose `.mcp.json` registers r2mcp):
146
+
147
+ ```bash
148
+ mkdir -p .claude/skills && cp -r /path/to/r2mcp/skills/remember .claude/skills/
149
+ ```
150
+
151
+ Claude Code auto-discovers project skills from `.claude/skills/<name>/SKILL.md`. (For all your projects at once, use `~/.claude/skills/` instead.) Then `/remember <note>` persists memories through the full pipeline.
152
+
153
+ ### Teach your agent the session loop (recommended)
154
+
155
+ r2mcp ships MCP server instructions that Claude Code loads automatically, so the agent knows the basics. For stronger habits, add a short protocol to your project's `CLAUDE.md`:
156
+
157
+ ```markdown
158
+ ## Memory
159
+
160
+ This project has persistent memory via the `memory` MCP server.
161
+ - At session start, `recall` context relevant to the task at hand.
162
+ - When a durable decision, preference, or correction surfaces, `remember` it
163
+ (tier: preferences = decisions/style, project-context = architecture/state,
164
+ conversations = session continuity).
165
+ - Run `/remember` before ending a work session to persist anything unsaved.
166
+ ```
167
+
168
+ **First session on an empty database:** `recall` returning zero results is expected — start `remember`-ing as decisions come up and recall pays off within a session or two.
169
+
170
+ ## Configuration
171
+
172
+ r2mcp reads its configuration from the MCP transport's environment — for
173
+ consumers, **the `.mcp.json` `env` block is the primary config surface**
174
+ (use `${VAR}` expansion for secrets):
175
+
176
+ ```json
177
+ {
178
+ "mcpServers": {
179
+ "memory": {
180
+ "command": "node",
181
+ "args": ["./node_modules/r2mcp/dist/index.js"],
182
+ "env": {
183
+ "R2MCP_DATABASE_URL": "${R2MCP_DATABASE_URL}",
184
+ "R2MCP_OPENROUTER_API_KEY": "${R2MCP_OPENROUTER_API_KEY}",
185
+ "R2MCP_CLASSIFIER_PROVIDER": "claude-code",
186
+ "R2MCP_EDGE_MAX_USD": "1.00",
187
+ "R2MCP_COMPILE_MAX_USD": "1.00"
188
+ }
189
+ }
190
+ }
191
+ }
192
+ ```
193
+
194
+ | Variable | Required | What it does |
195
+ |----------|----------|--------------|
196
+ | `R2MCP_DATABASE_URL` | **Yes** | PostgreSQL + pgvector connection string. The server **fails fast at startup** if unset — it never guesses a database. |
197
+ | `R2MCP_OPENROUTER_API_KEY` | Recommended | Enables semantic-search embeddings. When unset, the server logs a startup warning and `remember`/`recall` responses carry a `warnings[]` field — everything still works full-text. |
198
+ | `R2MCP_CLAUDE_BIN` | Sometimes | Absolute path to the `claude` binary for the $0 Max-plan provider. Needed when the spawning process's PATH doesn't include it — common under launchd jobs and some MCP hosts (e.g. `~/.local/bin/claude`). The spawn error names this variable when it's the fix. |
199
+ | `ANTHROPIC_API_KEY` | Optional | Only for `--provider=anthropic` on classifier/compile runs. |
200
+ | `R2MCP_CLASSIFIER_PROVIDER` | Optional | Pin a provider (`claude-code` \| `anthropic` \| `openrouter`) instead of auto-fallback. |
201
+ | `R2MCP_EDGE_MAX_USD` / `R2MCP_COMPILE_MAX_USD` / `R2MCP_ENTITY_MAX_USD` | Optional | Cost caps for the batch jobs (defaults `$1.00`). |
202
+
203
+ The server also loads a `.env` file from its working directory at startup
204
+ (non-clobbering — real environment variables always win). A `.env` at the
205
+ r2mcp source root is the normal path for `npm run` scripts when working from
206
+ a checkout; consumers configuring via `.mcp.json env` don't need one.
207
+
208
+ ## Troubleshooting
209
+
210
+ | Symptom | Cause & fix |
211
+ |---------|-------------|
212
+ | `connect ENETUNREACH 2406:...` during setup | Supabase Direct connection is IPv6-only (IPv4 is a paid add-on) and your network is IPv4-only. Use the **Session pooler** string instead: Dashboard → Connect → Session pooler (port 5432). Setup classifies this error and says the same. |
213
+ | `Transaction-pooler URL detected (port 6543)` | The transaction pooler can't run DDL or prepared statements. Use the Session pooler (port 5432). |
214
+ | `R2MCP_DATABASE_URL is not set` | Deliberate fail-fast — set it in `.mcp.json env` or `.env`. The error lists both surfaces and the Docker default URL. |
215
+ | `embeddings disabled` warning at startup or in `warnings[]` | `R2MCP_OPENROUTER_API_KEY` is unset (or the embed call failed — the message distinguishes the two). Full-text search still works; set the key to enable semantic search. |
216
+ | `could not spawn 'claude' (ENOENT)` on classifier/compile runs | The claude CLI isn't on the spawning process's PATH. Set `R2MCP_CLAUDE_BIN` to its absolute path. |
217
+ | Fresh credentials rejected right after a Supabase password reset | The pooler caches auth-rejection state for 30–60s. Wait a minute and retry before assuming the rotation failed. |
218
+
219
+ ## Memory Tiers
220
+
221
+ | Tier | What goes here | Auto-archived after |
222
+ |------|---------------|---------------------|
223
+ | `preferences` | Decisions, coding style, tool choices | Never |
224
+ | `project-context` | Architecture, system state, what's built | 180 days |
225
+ | `conversations` | Relationship continuity, session history | 90 days |
226
+
227
+ ## Tools Reference
228
+
229
+ | Tool | Description |
230
+ |------|-------------|
231
+ | `remember` | Store/update/archive a memory with tier + metadata |
232
+ | `recall` | Semantic + full-text search with progressive tier search; emits `signals[]` from typed edges |
233
+ | `search` | Filter by type, tier, topics, date range |
234
+ | `meditate` | Archive stale entries, find duplicates; pass `include_lint: true` to fold lint findings in |
235
+ | `reject` | Mark a memory as rejected (excluded from future recall) |
236
+ | `stats` | Health check — counts, staleness, embedding status |
237
+ | `compile` | Regenerate browsable wiki views under `memory/compiled/` (SPEC-044, see below) |
238
+ | `classify` | Classify candidate memory pairs into typed edges (supports, contradicts, supersedes, evolved_into, depends_on, related_to). Subprocess-spawned (SPEC-044 invariant). |
239
+ | `dump_edges_sidecar` | In-process JSON dump of memory_edges + memories to a caller-supplied directory. Used by downstream consumers like Memory Explorer. |
240
+ | `lint` | Surface structural feedback: contradictions, stale, orphans, drift, superseded_unflagged (SPEC-044, see below) |
241
+ | `extract_entities` | Extract structured entities (project / person / tool / decision) from memories. Spawns the entity extractor driver via the shared `resolveCliCommand` helper. Inherits cost cap (`R2MCP_ENTITY_MAX_USD`, default $1.00) and resumability from SPEC-043. Top-N known entities (`R2MCP_ENTITY_CONTEXT_TOP_N`, default 100) seed the LLM context. (SPEC-046, see below) |
242
+ | `recall` (extended) | Accepts an optional `entity` parameter that narrows results to memories linked to a named entity (matched by canonical name or any alias). When `entity` is set, `query` is optional. Response gains `entity_resolved: boolean`, optional `entity_id`, and per-result `entity_links[]`. (SPEC-046) |
243
+
244
+ ## Recall v2 — semantic + budget-aware retrieval
245
+
246
+ `recall()` is the workhorse retrieval tool. v2 (xMemory-inspired, 2026-04) layers four retrieval shapes on top of the underlying hybrid semantic + full-text search:
247
+
248
+ ### 1. Relevance floor — `min_score`
249
+
250
+ Filter out low-quality matches before they're returned. Without this, semantic search dumps a long tail of weakly-related results.
251
+
252
+ ```ts
253
+ recall({ query: "edge classifier cost cap", min_score: 0.3 })
254
+ ```
255
+
256
+ Suggested defaults: `0.3` for semantic queries, `0.1` for keyword-driven ones.
257
+
258
+ ### 2. MMR diversity — `diversity` (lambda 0.0–1.0)
259
+
260
+ Maximal Marginal Relevance reranks results to balance relevance against redundancy. `1.0` is pure relevance (may return three near-duplicates of the top hit); `0.0` is pure diversity (spreads coverage); the default `0.7` favors relevance with mild diversification.
261
+
262
+ ```ts
263
+ recall({ query: "memory architecture", diversity: 0.5, top_k: 8 })
264
+ ```
265
+
266
+ Use lower values when you want broad coverage of a topic, higher when you want the single best answer plus close runners-up.
267
+
268
+ ### 3. Context budget — `max_tokens`
269
+
270
+ Token-budget retrieval: walks MMR-reranked results in score order and stops when adding the next result would exceed the budget. Returns `tokens_used` in the response so you know how much you actually pulled.
271
+
272
+ ```ts
273
+ recall({ query: "what we learned about classifiers", max_tokens: 4000 })
274
+ // → up to N results, summing to ≤4000 tokens, prioritized by relevance × diversity
275
+ ```
276
+
277
+ This is the right call when you're stuffing recall results into a downstream prompt and have a hard context limit. `top_k` is ignored when `max_tokens` is set — the budget decides the cut.
278
+
279
+ ### 4. Progressive tier search — `progressive` + `confidence_threshold`
280
+
281
+ Top-down retrieval through the tier hierarchy (`preferences` → `project-context` → `conversations`). High-confidence matches in `preferences` short-circuit the search before lower tiers are consulted, mimicking the xMemory observation that decisions/preferences usually answer questions before context/history needs to.
282
+
283
+ ```ts
284
+ recall({ query: "do we use bun or npm", progressive: true, confidence_threshold: 0.82 })
285
+ // → returns immediately if a preferences-tier match scores ≥0.82, else widens to project-context, then conversations
286
+ ```
287
+
288
+ Default behavior — turn off with `progressive: false` to force a full sweep across tiers, or pin a single tier with `tier: 'preferences'`.
289
+
290
+ ### Composing them
291
+
292
+ The four parameters compose:
293
+
294
+ ```ts
295
+ recall({
296
+ query: "spec-bench cleanup conventions",
297
+ min_score: 0.3, // drop weak matches
298
+ diversity: 0.6, // some diversification
299
+ max_tokens: 3000, // fit in context
300
+ progressive: true, // early-stop on prefs hits
301
+ confidence_threshold: 0.82,
302
+ })
303
+ ```
304
+
305
+ Plus `signals[]` on the response surfaces typed memory edges (`contradicts`, `superseded_by`) on the returned memories so callers can flag conflicts inline.
306
+
307
+ ## Cross-Project Memory
308
+
309
+ All projects pointing at the same `R2MCP_DATABASE_URL` share a single memory pool. This is intentional — your knowledge travels with you. Namespace isolation is a v2 roadmap item.
310
+
311
+ ## OpenTelemetry (optional)
312
+
313
+ Enable OTel tracing and metrics:
314
+
315
+ ```bash
316
+ OTEL_ENABLED=true
317
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
318
+ ```
319
+
320
+ Metrics use the `r2mcp.memory.*` namespace.
321
+
322
+ ## Prior Art & Acknowledgements
323
+
324
+ r2mcp stands on the shoulders of two projects:
325
+
326
+ **[Open Brain](https://github.com/NateBJones-Projects/OB1) by [Nate B. Jones](https://natesnewsletter.substack.com/)**
327
+ The core architectural insight — "one database, any AI plugs in" — comes from Open Brain. The idea that your knowledge layer should be sovereign and portable (not locked inside a specific tool) is the founding premise of r2mcp. Open Brain proved the PostgreSQL + pgvector substrate works for personal AI memory at minimal cost ($0.10–0.30/month). r2mcp narrows the scope to Claude Code's MCP protocol and adds a more opinionated retrieval layer on top of that foundation.
328
+
329
+ **[xMemory](https://arxiv.org/abs/2602.02007) — "Beyond RAG for Agent Memory: Retrieval by Decoupling and Aggregation"**
330
+ Hu et al. (2026) established the hierarchical tier approach and showed that progressive top-down retrieval with coverage maximization + redundancy minimization cuts token usage ~50% vs. flat RAG while improving accuracy. r2mcp's 3-tier memory (preferences → project-context → conversations) is a hand-crafted simplification of their 4-level hierarchy (messages → episodes → semantics → themes). The MMR diversity reranking in `recall()` directly implements their redundancy minimization insight.
331
+
332
+ ## Migrating from ClaudeClaw
333
+
334
+ If you're moving from the ClaudeClaw-internal `memory-mcp-server`:
335
+
336
+ ```bash
337
+ R2MCP_DATABASE_URL=<your-new-url> npx tsx src/cli/migrate.ts /path/to/your/memory/
338
+ ```
339
+
340
+ The migration script reads `preferences.md`, `project-context.md`, and `conversations.md` from the specified directory and imports them. It's idempotent — safe to re-run.
341
+
342
+ ## Memory edges (SPEC-043)
343
+
344
+ r2mcp supports a typed-relation table (`memory_edges`) that captures structural
345
+ relations between memories — `contradicts`, `supersedes`, `supports`,
346
+ `evolved_into`, `depends_on`, `related_to`. The `recall()` MCP tool surfaces
347
+ `contradicts` / `superseded_by` relations as an optional `signals[]` field on
348
+ the response (additive — existing clients work unchanged).
349
+
350
+ ### Running the classifier
351
+
352
+ The classifier is a manual batch process — it is NOT invoked from the MCP server
353
+ hot path. Provider selection follows the SPEC-044 precedence (see below); on a
354
+ Max plan, no API key is required.
355
+
356
+ ```bash
357
+ # Estimate cost without making API calls or writing edges
358
+ npm run edges:classify -- --dry-run
359
+
360
+ # Auto-fallback: prefers claude-code (Max plan, $0/call)
361
+ npm run edges:classify -- --max-cost=1.00
362
+
363
+ # Force a specific provider
364
+ npm run edges:classify -- --provider=anthropic --max-cost=1.00
365
+ npm run edges:classify -- --provider=openrouter --max-cost=1.00
366
+
367
+ # Incremental run on memories from the last 7 days
368
+ npm run edges:classify -- --since=7d --max-cost=0.25
369
+
370
+ # Resume a prior run that hit its cap (the run_id is printed at exit and stored in
371
+ # data/edges-state.last-run)
372
+ npm run edges:classify -- --resume=<run_id>
373
+ ```
374
+
375
+ State and run summaries are written under `data/edges-state.*` (JSONL append-log,
376
+ last-run sidecar, per-run JSON summary at `data/edges-state.runs/<run_id>.json`).
377
+
378
+ ## LLM provider abstraction (SPEC-044)
379
+
380
+ The classifier and wiki compiler share a small `LLMProvider` abstraction with
381
+ three adapters. Providers run from standalone Node processes only — the MCP
382
+ server itself never makes LLM calls.
383
+
384
+ | Adapter | Auth | Cost per call | Concurrency cap |
385
+ |---------|------|---------------|------------------|
386
+ | `claude-code` | Claude Code OAuth (Max plan) | **$0** (strict equality) | 2 (subprocess overhead) |
387
+ | `anthropic` | `ANTHROPIC_API_KEY` | Per-token (list price) | 10 |
388
+ | `openrouter` | `R2MCP_OPENROUTER_API_KEY` | Per-token (list price) | 10 |
389
+
390
+ ### Selection precedence
391
+
392
+ 1. `--provider=<name>` CLI flag — highest priority
393
+ 2. `R2MCP_CLASSIFIER_PROVIDER` environment variable
394
+ 3. Auto-fallback: `claude-code` if logged in → `anthropic` if API key set →
395
+ `openrouter` if API key set → fatal error naming all three remediation paths
396
+
397
+ The fallback prefers `claude-code` so a Max-plan user pays nothing by default.
398
+
399
+ OpenRouter's primary role remains text→vector embeddings. Its classifier /
400
+ compile use is opt-in per invocation, never auto-routed for embeddings.
401
+
402
+ ## Wiki compile (SPEC-044)
403
+
404
+ `compile()` regenerates browsable markdown views of the memory store from
405
+ pgvector. Output goes to `memory/compiled/` (gitignored, regenerable).
406
+
407
+ ```bash
408
+ # Compile all three tier files (preferences.md, project-context.md, conversations.md)
409
+ npm run compile-wiki -- --all
410
+
411
+ # Compile a single tier
412
+ npm run compile-wiki -- --tier=preferences
413
+
414
+ # Compile a per-topic page
415
+ npm run compile-wiki -- --topic=wiki-mode
416
+
417
+ # Preview without writing
418
+ npm run compile-wiki -- --all --dry-run
419
+
420
+ # Force a provider (otherwise uses auto-fallback)
421
+ npm run compile-wiki -- --all --provider=claude-code
422
+ ```
423
+
424
+ ### Output shape
425
+
426
+ Every compiled file carries YAML frontmatter recording `generated_at`,
427
+ `compile_run_id`, `source_count`, `source_memory_ids`, `provider`,
428
+ `source_git_sha`, and `tier` or `topic`. The body is structured prose with
429
+ inline `<m:id>` citations and a `Sources:` line per cluster.
430
+
431
+ ### Structural stability
432
+
433
+ Compile is treated as a regenerable view: across two runs against the same
434
+ input, the set of `## H2` / `### H3` headers and the set of cited memory IDs
435
+ are bit-identical. Prose-level variance is bounded at 5% (Levenshtein ratio
436
+ ≥ 0.95) — the only LLM nondeterminism allowance. The compiler controls
437
+ headers and citations; only the prose paragraphs come from the LLM.
438
+
439
+ ### Cost cap
440
+
441
+ `R2MCP_COMPILE_MAX_USD` (default `$1.00`) — when exceeded mid-run, compile
442
+ exits cleanly with `hit_cost_cap: true` and partial files. Same shape as the
443
+ classifier cap-hit behavior.
444
+
445
+ ### What compile never does
446
+
447
+ - Modifies `memory/MEMORY.md` — the human-curated hub stays invariant
448
+ - Writes outside `memory/compiled/`
449
+ - Touches the live `memories` or `memory_edges` tables — read-only at the DB layer
450
+ - Uses any direct Anthropic SDK call — every synthesis routes through `LLMProvider`
451
+
452
+ ## Lint (SPEC-044)
453
+
454
+ `lint()` surfaces five structural checks on the memory store. SQL-only — no
455
+ LLM calls, no cost cap.
456
+
457
+ ```bash
458
+ # Run all checks against the live DB and produce a human-readable report
459
+ npm run lint:memory
460
+
461
+ # Run a single check
462
+ npm run lint:memory -- --check=stale
463
+
464
+ # Apply auto-fixes for high-confidence findings
465
+ npm run lint:memory -- --fix
466
+ ```
467
+
468
+ | Check | What it surfaces |
469
+ |-------|-------------------|
470
+ | `contradictions` | Edges where `relation='contradicts'` between two unarchived memories |
471
+ | `stale` | Memories older than 90d with zero incoming edges, tier ≠ preferences |
472
+ | `orphans` | Memories with zero edges in either direction, older than 30d |
473
+ | `drift` | Pairs sharing ≥2 topics with no edge yet — classifier hasn't run on this pair |
474
+ | `superseded_unflagged` | `contradicts` edge where the temporal pattern says it should be `supersedes` |
475
+
476
+ ### `--fix` semantics
477
+
478
+ `lint --fix` only acts on findings with `confidence ≥ 0.9`:
479
+
480
+ - `stale` → memory is archived (`type='archived'`)
481
+ - `superseded_unflagged` → edge type is rewritten from `contradicts` to `supersedes`
482
+
483
+ Lower-confidence findings are returned as suggestions only, never auto-acted.
484
+
485
+ ### `meditate` integration
486
+
487
+ `meditate({include_lint: true})` runs lint first and surfaces findings as a
488
+ `lint_findings` field on the response. The default invocation
489
+ (`meditate({mode: 'full', dry_run: false})`) returns the byte-identical
490
+ pre-spec response shape — backward compatibility for direct callers is
491
+ preserved.
492
+
493
+ ## Entity extraction (SPEC-046)
494
+
495
+ Light entity extraction over the memory store — pulls structured `project` /
496
+ `person` / `tool` / `decision` entities out of memories, persists them to two
497
+ new tables (`entities` for canonical names + aliases, `memory_entities` for the
498
+ M:N link to `memories`), and lets `recall()` filter on entity name or alias.
499
+
500
+ The extractor is a subprocess-driven batch process — the MCP server itself
501
+ never makes LLM calls. Provider selection follows the SPEC-044 precedence; on a
502
+ Max plan, no API key is required.
503
+
504
+ ```bash
505
+ # One-shot batch extraction over the last week, capped at $0.50
506
+ npm run entities:extract -- --since-days=7 --max-cost=0.5
507
+
508
+ # Or via MCP tool from any client
509
+ # mcp.callTool('extract_entities', { since_days: 7, max_cost_usd: 0.5 })
510
+
511
+ # Then ask for Speculator-scoped recall
512
+ # mcp.callTool('recall', { entity: 'Speculator', query: 'compaction' })
513
+ ```
514
+
515
+ ### Env vars
516
+
517
+ | Variable | Default | What it controls |
518
+ |----------|---------|------------------|
519
+ | `R2MCP_ENTITY_MAX_USD` | `1.00` | Cost cap for a single extraction run. On overrun, the run exits cleanly with `hit_cost_cap: true` (same shape as the classifier and compile caps). |
520
+ | `R2MCP_ENTITY_CONTEXT_TOP_N` | `100` | Number of known entities seeded into the LLM context to bias toward canonical names + alias merging. |
521
+
522
+ ### Scoped recall
523
+
524
+ When `recall()` is called with `entity` set:
525
+
526
+ - `query` is optional — entity-only recall returns all memories linked to the entity (matched by canonical name or any alias).
527
+ - The response carries `entity_resolved: boolean` and, when resolved, `entity_id`.
528
+ - Each result carries an `entity_links[]` array describing how that memory connects to the named entity.
529
+
530
+ ## License
531
+
532
+ [MIT](LICENSE)