mnemo-agent 0.2.2__tar.gz → 0.3.1__tar.gz

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 (26) hide show
  1. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/PKG-INFO +60 -19
  2. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/README.md +59 -18
  3. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/pyproject.toml +1 -1
  4. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo/cli.py +101 -6
  5. mnemo_agent-0.3.1/src/mnemo/server.py +383 -0
  6. mnemo_agent-0.3.1/src/mnemo/stdio_server.py +64 -0
  7. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo_agent.egg-info/PKG-INFO +60 -19
  8. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo_agent.egg-info/SOURCES.txt +3 -1
  9. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/tests/test_cli.py +82 -0
  10. mnemo_agent-0.3.1/tests/test_server.py +380 -0
  11. mnemo_agent-0.2.2/src/mnemo/server.py +0 -237
  12. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/LICENSE +0 -0
  13. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/setup.cfg +0 -0
  14. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo/__init__.py +0 -0
  15. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo/adapters/__init__.py +0 -0
  16. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo/adapters/letta_adapter.py +0 -0
  17. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo/adapters/mem0_adapter.py +0 -0
  18. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo/models.py +0 -0
  19. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo/remotes.py +0 -0
  20. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo/search.py +0 -0
  21. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo/storage.py +0 -0
  22. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo_agent.egg-info/dependency_links.txt +0 -0
  23. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo_agent.egg-info/entry_points.txt +0 -0
  24. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo_agent.egg-info/requires.txt +0 -0
  25. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/src/mnemo_agent.egg-info/top_level.txt +0 -0
  26. {mnemo_agent-0.2.2 → mnemo_agent-0.3.1}/tests/test_remote.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mnemo-agent
3
- Version: 0.2.2
3
+ Version: 0.3.1
4
4
  Summary: Local-first agent memory CLI — dump, diff, migrate & query across Mem0, Letta, and more
5
5
  Author: Joshua Ndala
6
6
  License-Expression: MIT
@@ -62,7 +62,7 @@ Inspired by Mnemosyne (Greek goddess of memory), **mnemo** is a portable CLI for
62
62
  - **TF-IDF search** — `mnemo recall "query"` with zero external ML deps, filterable by `--tag`
63
63
  - **Rich tables** — confidence color-coded (🟢 ≥0.8, 🟡 ≥0.5, 🔴 <0.5)
64
64
  - **HTML + graph diffs** — visual diff between dump snapshots
65
- - **MCP server** — FastAPI `/mcp/list_tools` + `/mcp/call_tool` for Ollama/Claude Code agents
65
+ - **MCP server** — JSON-RPC 2.0 + stdio transport; plug directly into Claude Desktop, Cursor, or any MCP client
66
66
  - **Push/pull sync** — S3, Cloudflare R2, or local filesystem remote; timestamp-based merge
67
67
  - **Safe writes** — `--dry-run` on load, pull, and migrate
68
68
 
@@ -110,8 +110,10 @@ mnemo load --file tests/fixtures/job_prep_sample.json --agent job-prep
110
110
  mnemo diff --agent-a job-prep --agent-b job-prep-v2
111
111
  mnemo diff dump1.json dump2.json --html diff_report.html
112
112
 
113
- # Start the MCP server (for Ollama / Claude Code agents)
113
+ # Start the MCP server HTTP mode
114
114
  mnemo serve --agent job-prep --port 8080
115
+ # Or stdio mode (Claude Desktop / Cursor — no port needed)
116
+ mnemo serve --agent job-prep --stdio
115
117
 
116
118
  # Sync to S3 (prompts for credentials on first add)
117
119
  mnemo remote add origin s3://my-bucket/mnemo --agent job-prep
@@ -137,7 +139,7 @@ mnemo pull --agent job-prep # merges remote facts into local
137
139
  | `mnemo retract <fact-id> --agent <name>` | Remove a fact by ID or 8-char prefix |
138
140
  | `mnemo edit <fact-id> --agent <name>` | Edit value/attribute/confidence of an existing fact |
139
141
  | `mnemo migrate --dump f.json --target mem0 --agent name` | Migrate between providers |
140
- | `mnemo serve --agent <name> [--port 8080] [--read-only]` | MCP FastAPI server |
142
+ | `mnemo serve --agent <name> [--port 8080] [--stdio] [--read-only]` | MCP server — HTTP (JSON-RPC 2.0) or stdio for Claude Desktop / Cursor |
141
143
  | `mnemo remote add <name> <url> --agent <name>` | Add a named remote (s3://, r2://, file://) |
142
144
  | `mnemo remote list --agent <name>` | List configured remotes |
143
145
  | `mnemo remote remove <name> --agent <name>` | Remove a remote |
@@ -157,13 +159,15 @@ mnemo-agent/
157
159
  │ ├── storage.py # Local file I/O (JSON, YAML, credentials)
158
160
  │ ├── search.py # TF-IDF search + diff engine
159
161
  │ ├── remotes.py # Push/pull backends: FileBackend, S3Backend
160
- │ ├── server.py # FastAPI MCP server
162
+ │ ├── server.py # FastAPI MCP server (HTTP + JSON-RPC 2.0)
163
+ │ ├── stdio_server.py # stdio MCP transport (Claude Desktop / Cursor)
161
164
  │ └── adapters/
162
165
  │ ├── mem0_adapter.py # Mem0 API → normalized facts
163
166
  │ └── letta_adapter.py # Letta API → normalized facts
164
167
  ├── tests/
165
168
  │ ├── test_cli.py # CLI command tests
166
169
  │ ├── test_remote.py # Remote backends, merge, push/pull tests
170
+ │ ├── test_server.py # MCP server: JSON-RPC 2.0, tools, stdio transport
167
171
  │ └── fixtures/
168
172
  │ └── job_prep_sample.json
169
173
  ├── config.yaml # Sample agent config
@@ -238,7 +242,28 @@ mnemo-agent/
238
242
 
239
243
  ---
240
244
 
241
- ## 🔌 MCP Server (for Ollama / Claude Code)
245
+ ## 🔌 MCP Server
246
+
247
+ mnemo implements the [MCP 2024-11-05 spec](https://spec.modelcontextprotocol.io) and supports two transports.
248
+
249
+ ### stdio — Claude Desktop / Cursor
250
+
251
+ Add to `~/.claude/claude_desktop_config.json`:
252
+
253
+ ```json
254
+ {
255
+ "mcpServers": {
256
+ "mnemo-job-prep": {
257
+ "command": "mnemo",
258
+ "args": ["serve", "--agent", "job-prep", "--stdio"]
259
+ }
260
+ }
261
+ }
262
+ ```
263
+
264
+ That's it — Claude Desktop will spawn mnemo as a subprocess and communicate over stdin/stdout.
265
+
266
+ ### HTTP — REST + JSON-RPC 2.0
242
267
 
243
268
  ```bash
244
269
  mnemo serve --agent job-prep --port 8080
@@ -246,20 +271,26 @@ mnemo serve --agent job-prep --port 8080
246
271
 
247
272
  | Endpoint | Description |
248
273
  |---|---|
249
- | `GET /mcp/list_tools` | List available tools (MCP schema) |
250
- | `POST /mcp/call_tool` | Call a tool by name with arguments |
251
- | `GET /facts` | REST: list all facts |
252
- | `GET /search?q=query` | REST: search memories |
274
+ | `POST /` | JSON-RPC 2.0 — `initialize`, `tools/list`, `tools/call`, `ping` |
275
+ | `GET /mcp/list_tools` | List available tools (legacy, kept for compatibility) |
276
+ | `POST /mcp/call_tool` | Call a tool by name (legacy, kept for compatibility) |
277
+ | `GET /facts` | REST: list all facts (`?entity=`, `?attribute=`, `?tag=`) |
278
+ | `GET /search?q=query` | REST: search memories (`?tag=` filter supported) |
279
+ | `GET /health` | Health check with version info |
253
280
  | `GET /docs` | Swagger UI |
254
281
 
255
282
  ### Available MCP tools
256
283
 
257
- ```json
258
- { "name": "search_memory", "description": "TF-IDF search over agent memory" }
259
- { "name": "list_facts", "description": "Return all facts, optionally filtered" }
260
- { "name": "upsert_fact", "description": "Add a fact to agent memory" }
261
- { "name": "get_agent_info", "description": "Agent metadata and fact count" }
262
- ```
284
+ | Tool | Description |
285
+ |---|---|
286
+ | `search_memory` | TF-IDF keyword search; supports `tag` filter |
287
+ | `list_facts` | List all facts; filterable by `entity`, `attribute`, `tag`; shows IDs |
288
+ | `upsert_fact` | Add a fact; supports `tags` array |
289
+ | `retract_fact` | Remove a fact by ID or 8-char prefix |
290
+ | `edit_fact` | Update a fact's `value`, `attribute`, or `confidence` |
291
+ | `get_agent_info` | Agent name, fact count, last updated timestamp |
292
+
293
+ `retract_fact` and `edit_fact` are disabled when `--read-only` is set.
263
294
 
264
295
  ---
265
296
 
@@ -299,20 +330,23 @@ Remote credentials (S3/R2 access keys) are stored separately in `~/.mnemo/creden
299
330
  ## Tests
300
331
 
301
332
  ```bash
302
- pip install "mnemo-agent[dev]"
333
+ pip install "mnemo-agent[dev,s3]"
303
334
  pytest tests/ -v
304
335
  ```
305
336
 
337
+ 114 tests across `test_cli.py`, `test_remote.py`, and `test_server.py`.
338
+
306
339
  ---
307
340
 
308
341
  ## Roadmap
309
342
 
310
343
  - [x] Push/pull sync to S3, R2, and local filesystem remotes
344
+ - [x] Write-time conflict detection with overwrite / keep-both / abort prompt
345
+ - [x] Full MCP 2024-11-05 protocol — JSON-RPC 2.0 + stdio transport (Claude Desktop / Cursor)
311
346
  - [ ] Vector embeddings for semantic search (v2)
312
347
  - [ ] Parquet export for analytics
313
348
  - [ ] `mnemo audit` — fact provenance trace
314
349
  - [ ] Web UI dashboard
315
- - [ ] Native Ollama MCP client registration
316
350
 
317
351
  ---
318
352
 
@@ -323,13 +357,20 @@ pytest tests/ -v
323
357
  mnemo init --agent job-prep
324
358
  mnemo load --file tests/fixtures/job_prep_sample.json --agent job-prep
325
359
 
326
- # Ask your agent questions via MCP (Ollama / Claude Code reads from :8080)
360
+ # Connect to Claude Desktop (add to claude_desktop_config.json, then restart)
361
+ mnemo serve --agent job-prep --stdio
362
+
363
+ # Or run as an HTTP server for other MCP clients
327
364
  mnemo serve --agent job-prep --port 8080
328
365
 
329
366
  # After a practice interview, add what you learned
330
367
  mnemo add --fact "Lead with Supabase migration story at FAANG interviews" \
331
368
  --agent job-prep --attribute interview_tip --confidence 0.9 --tag tip
332
369
 
370
+ # If you added a conflicting fact by mistake, retract it by ID prefix
371
+ mnemo show --agent job-prep --format plain # see IDs
372
+ mnemo retract a1b2c3d4 --agent job-prep
373
+
333
374
  # Before next session, recall relevant context
334
375
  mnemo recall "React Supabase full-stack" --agent job-prep
335
376
  ```
@@ -16,7 +16,7 @@ Inspired by Mnemosyne (Greek goddess of memory), **mnemo** is a portable CLI for
16
16
  - **TF-IDF search** — `mnemo recall "query"` with zero external ML deps, filterable by `--tag`
17
17
  - **Rich tables** — confidence color-coded (🟢 ≥0.8, 🟡 ≥0.5, 🔴 <0.5)
18
18
  - **HTML + graph diffs** — visual diff between dump snapshots
19
- - **MCP server** — FastAPI `/mcp/list_tools` + `/mcp/call_tool` for Ollama/Claude Code agents
19
+ - **MCP server** — JSON-RPC 2.0 + stdio transport; plug directly into Claude Desktop, Cursor, or any MCP client
20
20
  - **Push/pull sync** — S3, Cloudflare R2, or local filesystem remote; timestamp-based merge
21
21
  - **Safe writes** — `--dry-run` on load, pull, and migrate
22
22
 
@@ -64,8 +64,10 @@ mnemo load --file tests/fixtures/job_prep_sample.json --agent job-prep
64
64
  mnemo diff --agent-a job-prep --agent-b job-prep-v2
65
65
  mnemo diff dump1.json dump2.json --html diff_report.html
66
66
 
67
- # Start the MCP server (for Ollama / Claude Code agents)
67
+ # Start the MCP server HTTP mode
68
68
  mnemo serve --agent job-prep --port 8080
69
+ # Or stdio mode (Claude Desktop / Cursor — no port needed)
70
+ mnemo serve --agent job-prep --stdio
69
71
 
70
72
  # Sync to S3 (prompts for credentials on first add)
71
73
  mnemo remote add origin s3://my-bucket/mnemo --agent job-prep
@@ -91,7 +93,7 @@ mnemo pull --agent job-prep # merges remote facts into local
91
93
  | `mnemo retract <fact-id> --agent <name>` | Remove a fact by ID or 8-char prefix |
92
94
  | `mnemo edit <fact-id> --agent <name>` | Edit value/attribute/confidence of an existing fact |
93
95
  | `mnemo migrate --dump f.json --target mem0 --agent name` | Migrate between providers |
94
- | `mnemo serve --agent <name> [--port 8080] [--read-only]` | MCP FastAPI server |
96
+ | `mnemo serve --agent <name> [--port 8080] [--stdio] [--read-only]` | MCP server — HTTP (JSON-RPC 2.0) or stdio for Claude Desktop / Cursor |
95
97
  | `mnemo remote add <name> <url> --agent <name>` | Add a named remote (s3://, r2://, file://) |
96
98
  | `mnemo remote list --agent <name>` | List configured remotes |
97
99
  | `mnemo remote remove <name> --agent <name>` | Remove a remote |
@@ -111,13 +113,15 @@ mnemo-agent/
111
113
  │ ├── storage.py # Local file I/O (JSON, YAML, credentials)
112
114
  │ ├── search.py # TF-IDF search + diff engine
113
115
  │ ├── remotes.py # Push/pull backends: FileBackend, S3Backend
114
- │ ├── server.py # FastAPI MCP server
116
+ │ ├── server.py # FastAPI MCP server (HTTP + JSON-RPC 2.0)
117
+ │ ├── stdio_server.py # stdio MCP transport (Claude Desktop / Cursor)
115
118
  │ └── adapters/
116
119
  │ ├── mem0_adapter.py # Mem0 API → normalized facts
117
120
  │ └── letta_adapter.py # Letta API → normalized facts
118
121
  ├── tests/
119
122
  │ ├── test_cli.py # CLI command tests
120
123
  │ ├── test_remote.py # Remote backends, merge, push/pull tests
124
+ │ ├── test_server.py # MCP server: JSON-RPC 2.0, tools, stdio transport
121
125
  │ └── fixtures/
122
126
  │ └── job_prep_sample.json
123
127
  ├── config.yaml # Sample agent config
@@ -192,7 +196,28 @@ mnemo-agent/
192
196
 
193
197
  ---
194
198
 
195
- ## 🔌 MCP Server (for Ollama / Claude Code)
199
+ ## 🔌 MCP Server
200
+
201
+ mnemo implements the [MCP 2024-11-05 spec](https://spec.modelcontextprotocol.io) and supports two transports.
202
+
203
+ ### stdio — Claude Desktop / Cursor
204
+
205
+ Add to `~/.claude/claude_desktop_config.json`:
206
+
207
+ ```json
208
+ {
209
+ "mcpServers": {
210
+ "mnemo-job-prep": {
211
+ "command": "mnemo",
212
+ "args": ["serve", "--agent", "job-prep", "--stdio"]
213
+ }
214
+ }
215
+ }
216
+ ```
217
+
218
+ That's it — Claude Desktop will spawn mnemo as a subprocess and communicate over stdin/stdout.
219
+
220
+ ### HTTP — REST + JSON-RPC 2.0
196
221
 
197
222
  ```bash
198
223
  mnemo serve --agent job-prep --port 8080
@@ -200,20 +225,26 @@ mnemo serve --agent job-prep --port 8080
200
225
 
201
226
  | Endpoint | Description |
202
227
  |---|---|
203
- | `GET /mcp/list_tools` | List available tools (MCP schema) |
204
- | `POST /mcp/call_tool` | Call a tool by name with arguments |
205
- | `GET /facts` | REST: list all facts |
206
- | `GET /search?q=query` | REST: search memories |
228
+ | `POST /` | JSON-RPC 2.0 — `initialize`, `tools/list`, `tools/call`, `ping` |
229
+ | `GET /mcp/list_tools` | List available tools (legacy, kept for compatibility) |
230
+ | `POST /mcp/call_tool` | Call a tool by name (legacy, kept for compatibility) |
231
+ | `GET /facts` | REST: list all facts (`?entity=`, `?attribute=`, `?tag=`) |
232
+ | `GET /search?q=query` | REST: search memories (`?tag=` filter supported) |
233
+ | `GET /health` | Health check with version info |
207
234
  | `GET /docs` | Swagger UI |
208
235
 
209
236
  ### Available MCP tools
210
237
 
211
- ```json
212
- { "name": "search_memory", "description": "TF-IDF search over agent memory" }
213
- { "name": "list_facts", "description": "Return all facts, optionally filtered" }
214
- { "name": "upsert_fact", "description": "Add a fact to agent memory" }
215
- { "name": "get_agent_info", "description": "Agent metadata and fact count" }
216
- ```
238
+ | Tool | Description |
239
+ |---|---|
240
+ | `search_memory` | TF-IDF keyword search; supports `tag` filter |
241
+ | `list_facts` | List all facts; filterable by `entity`, `attribute`, `tag`; shows IDs |
242
+ | `upsert_fact` | Add a fact; supports `tags` array |
243
+ | `retract_fact` | Remove a fact by ID or 8-char prefix |
244
+ | `edit_fact` | Update a fact's `value`, `attribute`, or `confidence` |
245
+ | `get_agent_info` | Agent name, fact count, last updated timestamp |
246
+
247
+ `retract_fact` and `edit_fact` are disabled when `--read-only` is set.
217
248
 
218
249
  ---
219
250
 
@@ -253,20 +284,23 @@ Remote credentials (S3/R2 access keys) are stored separately in `~/.mnemo/creden
253
284
  ## Tests
254
285
 
255
286
  ```bash
256
- pip install "mnemo-agent[dev]"
287
+ pip install "mnemo-agent[dev,s3]"
257
288
  pytest tests/ -v
258
289
  ```
259
290
 
291
+ 114 tests across `test_cli.py`, `test_remote.py`, and `test_server.py`.
292
+
260
293
  ---
261
294
 
262
295
  ## Roadmap
263
296
 
264
297
  - [x] Push/pull sync to S3, R2, and local filesystem remotes
298
+ - [x] Write-time conflict detection with overwrite / keep-both / abort prompt
299
+ - [x] Full MCP 2024-11-05 protocol — JSON-RPC 2.0 + stdio transport (Claude Desktop / Cursor)
265
300
  - [ ] Vector embeddings for semantic search (v2)
266
301
  - [ ] Parquet export for analytics
267
302
  - [ ] `mnemo audit` — fact provenance trace
268
303
  - [ ] Web UI dashboard
269
- - [ ] Native Ollama MCP client registration
270
304
 
271
305
  ---
272
306
 
@@ -277,13 +311,20 @@ pytest tests/ -v
277
311
  mnemo init --agent job-prep
278
312
  mnemo load --file tests/fixtures/job_prep_sample.json --agent job-prep
279
313
 
280
- # Ask your agent questions via MCP (Ollama / Claude Code reads from :8080)
314
+ # Connect to Claude Desktop (add to claude_desktop_config.json, then restart)
315
+ mnemo serve --agent job-prep --stdio
316
+
317
+ # Or run as an HTTP server for other MCP clients
281
318
  mnemo serve --agent job-prep --port 8080
282
319
 
283
320
  # After a practice interview, add what you learned
284
321
  mnemo add --fact "Lead with Supabase migration story at FAANG interviews" \
285
322
  --agent job-prep --attribute interview_tip --confidence 0.9 --tag tip
286
323
 
324
+ # If you added a conflicting fact by mistake, retract it by ID prefix
325
+ mnemo show --agent job-prep --format plain # see IDs
326
+ mnemo retract a1b2c3d4 --agent job-prep
327
+
287
328
  # Before next session, recall relevant context
288
329
  mnemo recall "React Supabase full-stack" --agent job-prep
289
330
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mnemo-agent"
7
- version = "0.2.2"
7
+ version = "0.3.1"
8
8
  description = "Local-first agent memory CLI — dump, diff, migrate & query across Mem0, Letta, and more"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -827,6 +827,7 @@ def cmd_edit(
827
827
 
828
828
 
829
829
  @cli.command("add")
830
+ @click.option("--force", "force", is_flag=True, default=False, help="Skip conflict check and keep both facts (useful for scripts)")
830
831
  @click.option("--fact", "-f", "fact_text", required=True, help="Free-text fact to store")
831
832
  @AGENT_OPTION
832
833
  @DIR_OPTION
@@ -844,8 +845,13 @@ def cmd_add(
844
845
  confidence: float,
845
846
  source: str,
846
847
  tags: tuple[str, ...],
848
+ force: bool,
847
849
  ) -> None:
848
- """Add a fact to an agent's memory store."""
850
+ """Add a fact to an agent's memory store.
851
+
852
+ If an existing fact shares the same entity+attribute, you will be prompted
853
+ to overwrite it, keep both, or abort. Use --force to always keep both (for scripts).
854
+ """
849
855
  base = _resolve_base(mnemo_dir)
850
856
  ag = _resolve_agent(agent)
851
857
  require_agent(ag, base)
@@ -856,8 +862,42 @@ def cmd_add(
856
862
  except FileNotFoundError:
857
863
  dump = AgentDump(agent=ag)
858
864
 
865
+ resolved_entity = entity or ag
866
+
867
+ # ── Conflict detection ────────────────────────────────────────────────────
868
+ if not force:
869
+ conflicts = [
870
+ f for f in dump.facts
871
+ if f.entity.lower() == resolved_entity.lower()
872
+ and f.attribute.lower() == attribute.lower()
873
+ ]
874
+ if conflicts:
875
+ console.print(
876
+ f"\n[yellow]⚠ Conflict[/] — {len(conflicts)} existing fact(s) for "
877
+ f"[bold]{resolved_entity}[/].[cyan]{attribute}[/]:\n"
878
+ )
879
+ for c in conflicts:
880
+ color = _conf_color(c.confidence)
881
+ console.print(
882
+ f" [dim]{c.id[:8]}[/] {c.value} "
883
+ f"[{color}](conf={c.confidence:.2f}, {c.timestamp.strftime('%Y-%m-%d')})[/]"
884
+ )
885
+ console.print()
886
+ choice = click.prompt(
887
+ " Choice",
888
+ type=click.Choice(["o", "k", "a"], case_sensitive=False),
889
+ default="k",
890
+ prompt_suffix=" — [o] overwrite [k] keep both [a] abort: ",
891
+ )
892
+ if choice == "a":
893
+ console.print("[dim]Aborted.[/]")
894
+ return
895
+ if choice == "o":
896
+ conflict_ids = {c.id for c in conflicts}
897
+ dump.facts = [f for f in dump.facts if f.id not in conflict_ids]
898
+
859
899
  fact = Fact(
860
- entity=entity or ag,
900
+ entity=resolved_entity,
861
901
  attribute=attribute,
862
902
  value=fact_text,
863
903
  source=source, # type: ignore[arg-type]
@@ -881,12 +921,51 @@ def cmd_add(
881
921
  @click.option("--agent", "-a", required=True)
882
922
  @click.option("--port", "-p", default=8080, show_default=True)
883
923
  @click.option("--read-only", is_flag=True, help="Disable write endpoints")
924
+ @click.option(
925
+ "--stdio",
926
+ "use_stdio",
927
+ is_flag=True,
928
+ help="Run in stdio mode (for Claude Desktop / Cursor MCP integration)",
929
+ )
884
930
  @DIR_OPTION
885
- def cmd_serve(agent: str, port: int, read_only: bool, mnemo_dir: Path | None) -> None:
886
- """Start the MCP-compatible FastAPI server for an agent."""
931
+ def cmd_serve(agent: str, port: int, read_only: bool, use_stdio: bool, mnemo_dir: Path | None) -> None:
932
+ """Start the MCP server for an agent.
933
+
934
+ \b
935
+ HTTP mode (default) — REST + JSON-RPC 2.0 at POST /:
936
+ mnemo serve --agent job-prep --port 8080
937
+
938
+ \b
939
+ stdio mode — for Claude Desktop / Cursor integration:
940
+ mnemo serve --agent job-prep --stdio
941
+
942
+ \b
943
+ Claude Desktop config (~/.claude/claude_desktop_config.json):
944
+ {
945
+ "mcpServers": {
946
+ "mnemo-job-prep": {
947
+ "command": "mnemo",
948
+ "args": ["serve", "--agent", "job-prep", "--stdio"]
949
+ }
950
+ }
951
+ }
952
+ """
887
953
  base = _resolve_base(mnemo_dir)
888
954
  require_agent(agent, base)
889
955
 
956
+ if use_stdio:
957
+ # stdio transport — used by Claude Desktop, Cursor, etc.
958
+ # Print nothing to stdout (it's the JSON-RPC channel); use stderr for status.
959
+ import sys
960
+ print(
961
+ f"mnemo MCP stdio — agent={agent} read_only={read_only}",
962
+ file=sys.stderr,
963
+ )
964
+ from mnemo.stdio_server import run_stdio
965
+ run_stdio(agent=agent, base=base, read_only=read_only)
966
+ return
967
+
968
+ # HTTP transport
890
969
  try:
891
970
  import uvicorn # type: ignore
892
971
  except ImportError:
@@ -896,13 +975,29 @@ def cmd_serve(agent: str, port: int, read_only: bool, mnemo_dir: Path | None) ->
896
975
 
897
976
  app = create_app(agent=agent, base=base, read_only=read_only)
898
977
 
978
+ import json as _json
979
+ desktop_cfg = _json.dumps(
980
+ {
981
+ "mcpServers": {
982
+ f"mnemo-{agent}": {
983
+ "command": "mnemo",
984
+ "args": ["serve", "--agent", agent, "--stdio"],
985
+ }
986
+ }
987
+ },
988
+ indent=2,
989
+ )
990
+
899
991
  console.print(
900
992
  Panel.fit(
901
993
  f"[bold cyan]Agent:[/] {agent}\n"
902
994
  f"[bold cyan]Port:[/] {port}\n"
903
995
  f"[bold cyan]Read-only:[/] {read_only}\n\n"
904
- f"[dim]MCP tools: http://localhost:{port}/mcp/list_tools[/]\n"
905
- f"[dim]Docs: http://localhost:{port}/docs[/]",
996
+ f"[dim]JSON-RPC 2.0: POST http://localhost:{port}/[/]\n"
997
+ f"[dim]Tools list: GET http://localhost:{port}/mcp/list_tools[/]\n"
998
+ f"[dim]Docs: http://localhost:{port}/docs[/]\n\n"
999
+ f"[bold]Claude Desktop config[/] [dim](~/.claude/claude_desktop_config.json)[/]:\n"
1000
+ f"[dim]{desktop_cfg}[/]",
906
1001
  title="🌐 mnemo serve",
907
1002
  border_style="green",
908
1003
  )