@yugabytedb/meko-mcp 2.0.0 → 2.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 (65) hide show
  1. package/README.md +183 -33
  2. package/assets/hooks-handlers/codex-session-end.sh +13 -0
  3. package/assets/hooks-handlers/cursor-session-start.sh +19 -1
  4. package/assets/hooks-handlers/lib/capture-notices.js +321 -0
  5. package/assets/hooks-handlers/lib/capture-state.js +2079 -0
  6. package/assets/hooks-handlers/lib/capture-status-cli.js +82 -0
  7. package/assets/hooks-handlers/lib/capture.js +1653 -225
  8. package/assets/hooks-handlers/lib/checkpoint-timer.js +455 -50
  9. package/assets/hooks-handlers/lib/kiro-handler.js +738 -0
  10. package/assets/hooks-handlers/lib/terminal-handoff.js +418 -0
  11. package/assets/hooks-handlers/session-end.sh +3 -24
  12. package/assets/hooks-handlers/session-start.sh +8 -1
  13. package/assets/hooks-handlers/test_checkpoint_timer.sh +162 -0
  14. package/assets/scripts/meko-statusline.sh +41 -1
  15. package/assets/scripts/package-desktop-skill.sh +32 -1
  16. package/assets/skills/meko-mcp-tools/SKILL.md +90 -137
  17. package/assets/skills/meko-mcp-tools/references/_sections.md +3 -5
  18. package/assets/skills/meko-mcp-tools/references/tools-agent-id-conventions.md +31 -28
  19. package/assets/skills/meko-mcp-tools/references/tools-conversation-dedup.md +5 -7
  20. package/assets/skills/meko-mcp-tools/references/tools-conversation-reasoning.md +50 -0
  21. package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +69 -49
  22. package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +6 -6
  23. package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +29 -5
  24. package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +29 -92
  25. package/assets/skills/meko-mcp-tools/references/tools-overview.md +42 -40
  26. package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +1 -2
  27. package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +18 -11
  28. package/assets/skills/meko-mcp-tools/references/tools-user-id-conventions.md +10 -9
  29. package/assets/skills/meko-mcp-tools-desktop/SKILL.md +51 -44
  30. package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +6 -6
  31. package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +2 -2
  32. package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +34 -25
  33. package/assets/skills/meko-mcp-tools-desktop/references/tools-conversation-reasoning.md +50 -0
  34. package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +63 -37
  35. package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +4 -4
  36. package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +19 -22
  37. package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +42 -40
  38. package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +1 -2
  39. package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +3 -17
  40. package/assets/skills/meko-select-datapack-desktop/SKILL.md +31 -21
  41. package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +8 -8
  42. package/assets/skills/meko-select-datapack-desktop/references/role-display-future.md +41 -0
  43. package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +1 -1
  44. package/bin/create-meko-setup.mjs +93 -16
  45. package/lib/capture-status.mjs +67 -0
  46. package/lib/clients/aliases.mjs +5 -0
  47. package/lib/clients/claude-code.mjs +8 -4
  48. package/lib/clients/claude-desktop.mjs +133 -4
  49. package/lib/clients/codex.mjs +29 -11
  50. package/lib/clients/common.mjs +2 -0
  51. package/lib/clients/cursor.mjs +7 -2
  52. package/lib/clients/index.mjs +2 -0
  53. package/lib/clients/kiro.mjs +705 -0
  54. package/lib/config.mjs +90 -0
  55. package/lib/detect.mjs +7 -2
  56. package/lib/installer.mjs +1 -0
  57. package/lib/migrate/adapters/codex.mjs +490 -0
  58. package/lib/migrate/id.mjs +28 -3
  59. package/lib/migrate/orchestrator.mjs +34 -7
  60. package/lib/migrate.mjs +4 -2
  61. package/lib/prompt.mjs +17 -2
  62. package/lib/url-selection.mjs +55 -0
  63. package/package.json +4 -4
  64. package/assets/skills/meko-mcp-tools/references/tools-scope-permissions.md +0 -71
  65. package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +0 -71
@@ -8,13 +8,13 @@
8
8
  - A **preference**: "Customer budget is $50k"
9
9
  - An **entity relationship**: "Alice works at Acme Corp"
10
10
 
11
- Memory is backed by pgvector (semantic search) and Apache AGE (entity-relationship graph). It retrieves relevant facts by meaning, not by conversation order.
11
+ Memory is backed by pgvector. Search is hybrid: semantic similarity, keyword matching, and a boost for memories that mention the entities named in your query (people, projects, tools). It retrieves relevant facts by meaning, not by conversation order.
12
12
 
13
13
  ```
14
- memory_add(scope="write", agent_id="support_bot",
15
- text="Customer Alice (alice@acme.com) prefers email, has Pro plan.", user_id="alice_123")
14
+ memory_add(agent_id="support_bot",
15
+ text="Customer Alice (alice@acme.com) prefers email, has Pro plan.")
16
16
 
17
- memory_search(scope="read", query="What plan does Alice have?", agent_id="support_bot")
17
+ memory_search(query="What plan does Alice have?", agent_id="support_bot")
18
18
  ```
19
19
 
20
20
  ## Use `conversation_create` + `conversation_add_message` when storing:
@@ -26,10 +26,10 @@ memory_search(scope="read", query="What plan does Alice have?", agent_id="suppor
26
26
  Conversations are backed by Langfuse sessions and traces. They preserve full structure: who said what, in what order, with what reasoning.
27
27
 
28
28
  ```
29
- conversation_create(scope="write", agent_id="support_bot", user_id="alice_123", title="Pricing discussion")
29
+ conversation_create(agent_id="support_bot", title="Pricing discussion")
30
30
  -- Returns: {"id": "conv-uuid-here"}
31
31
 
32
- conversation_add_message(scope="write", conversation_id="conv-uuid-here", agent_id="support_bot",
32
+ conversation_add_message(conversation_id="conv-uuid-here", agent_id="support_bot",
33
33
  input="What are your pricing tiers?",
34
34
  output="We offer Starter ($10/mo), Pro ($50/mo), and Enterprise (custom).",
35
35
  reasoning="Retrieved pricing page data. No special discounts apply.")
@@ -65,47 +65,31 @@ All three fields — `input`, `output`, and `reasoning` — must contain the **e
65
65
 
66
66
  The purpose of conversation storage is to create a faithful, replayable record. A rephrased summary loses the original wording, tone, and detail — making the stored conversation useless for review, debugging, or audit.
67
67
 
68
- ## Personal memory vs. team-shared knowledge — the three read paths
68
+ ## Personal memory vs. team-shared knowledge — the two read paths
69
69
 
70
- Agents on Cloud Meko have three distinct read surfaces. Pick the right one for the question.
70
+ Agents on Cloud Meko have two distinct read surfaces. Pick the right one for the question.
71
71
 
72
72
  | Read path | What it returns | How to call |
73
73
  |---|---|---|
74
- | Your own project memories | Memories you wrote under this `agent_id`, filtered by `(datapack_id, user_id, agent_id)` | `memory_search(agent_id="<your agent_id>", query="...", ...)` |
75
- | Cross-project common bucket | Memories any agent (including this one) wrote with `agent_id="meko_agent"` — the empty-default bucket for genuinely cross-project facts | `memory_search(agent_id="meko_agent", query="...", ...)` |
74
+ | Your personal memories | Everything you and any of your agents wrote for this user — filtered by `(datapack_id, user_id)`, **not** by `agent_id`; one call covers every bucket | `memory_search(agent_id="<your agent_id>", query="...", ...)` |
76
75
  | Team's shared knowledge | Promoted memories + uploaded documents, visible to every member of the datapack | `knowledgebase_search(agent_id="<anything>", datapack_id="<datapack UUID>", query="...")` — `agent_id` is ignored |
77
76
 
78
- For a true fan-out across multiple specific agent_ids (e.g. also see what was written from `claude_code:other-repo` or `cursor:foo`), call `memory_search` once per agent_id — there's no single-call shortcut. Empty string is rewritten to `meko_agent` server-side; it does not drop the filter.
77
+ `agent_id` does not filter memory reads, so a single `memory_search` already spans all of your agents; there is no per-agent fan-out to do.
79
78
 
80
79
  ### How content gets into each surface
81
80
 
82
- - **Personal memories** — written by `memory_add`. Scoped per-user and per-agent from the moment of write. Only the writer sees them via MCP reads.
81
+ - **Personal memories** — written by `memory_add`. The `agent_id` records the writer but does not restrict reads; memories are scoped **per-user**, so any of that user's agents can read them via MCP, and no other user can.
83
82
  - **Team-shared Shared Knowledge** — arrives two ways:
84
- 1. The **user** (never the agent) promotes a personal memory to Knowledge via the Cloud UI's Learnings tab. That copies the row into the shared `knowledge_base` table with the `user_id` stripped, making it team-wide. Originating `agent_id` is preserved in metadata for provenance.
83
+ 1. An agent calls `memory_promote` for exact, user-confirmed memory UUIDs, or the user promotes them from the Cloud UI's Learnings tab. Promotion moves the memories into shared knowledge, strips `user_id`, preserves originating `agent_id` as provenance, and evicts the private mem0 records.
85
84
  2. The user uploads a file via Datapack → Actions → **Add Knowledge** in the Cloud UI. PDF/TXT/MD/JSON/MP4 up to 5MB each.
86
85
 
87
86
  Both show up in `knowledgebase_search`, tagged `metadata_filters.source: "memory"` vs other values so the agent can distinguish provenance in responses.
88
87
 
89
- **No MCP tool promotes to Shared Knowledge.** The agent can flag memories it thinks are worth promoting and suggest the user do it from the UI — but the action is user-initiated.
88
+ `memory_promote` is a one-way, destructive MCP path. Before calling it, retrieve exact UUIDs with `memory_search` or `memory_get_all`, show the exact candidates, explain team visibility and private-record eviction, and obtain explicit user confirmation. Only owners and maintainers may promote; report 403/auth failures without escalating scope or switching datapacks. The Cloud UI remains an alternative.
90
89
 
91
90
  ### When the user asks "what do you know about X?"
92
91
 
93
- A full sweep is three calls (budget for it — each is 2-6 seconds):
94
-
95
- ```
96
- memory_search(agent_id="<your-agent-id>", query="X", conversation_id=..., ...)
97
- memory_search(agent_id="meko_agent", query="X", conversation_id=..., ...) # cross-project common bucket
98
- knowledgebase_search(agent_id="<anything>", datapack_id="<uuid>", query="X", conversation_id=..., ...)
99
- ```
100
-
101
- When you answer, be explicit about scope so the user knows why something is or isn't there:
102
-
103
- - "I found this in your own project memories…"
104
- - "I found this in your common cross-project bucket (something you or another agent wrote under `meko_agent`)…"
105
- - "I found this in your team's Shared Knowledge — someone (you or a teammate) promoted it earlier…"
106
- - "I couldn't find anything in your personal memories. You might want to check the Cloud UI's Memory Summary tab for a cross-agent view."
107
-
108
- For a true fan-out across other specific buckets (e.g. also see what was written from `claude_code:other-repo` or `cursor:foo`), call `memory_search` once per known agent_id — empty string just resolves to `meko_agent`, not a fan-out.
92
+ A full sweep is two calls: `memory_search` (all your personal memories, every agent — no per-agent fan-out) then `knowledgebase_search` (team-shared knowledge). When you answer, name which surface each finding came from, so the user knows why something is or isn't there.
109
93
 
110
94
  ## Memory limitations for structured data
111
95
 
@@ -119,72 +103,28 @@ There is no MCP-side "just put it in a database table" option — raw SQL access
119
103
 
120
104
  ## Memory Storage: automatic, not proactive
121
105
 
122
- On Claude Code, do **not** proactively `memory_add` facts the user states. Conversation capture is automatic (see below) and the server extracts durable memories from the captured turns on its own — personal info, team conventions, domain knowledge the user states, and corrections are all saved without a tool call. Re-storing them with `memory_add` just duplicates what extraction already holds.
123
-
124
- Explicit `memory_add` is reserved for the three cases automatic extraction cannot reach:
125
-
126
- - **Explicit request** → the user says "remember this" / "save this."
127
- - **Output-only / tool-derived fact** → a durable fact that lives only in your output or a tool result, never in a user turn. Extraction reads only the user turn, so it never sees these.
128
- - **Overwriting correction** → a prior fact was negated and the stale memory must not survive. Extraction is additive (it adds the new fact but leaves the old one live), so `memory_search` for the stale memory and `memory_update` / `memory_delete_by_id` it.
129
-
130
- Use `memory_search` at the start of sessions to recall what you already know before asking the user to repeat themselves.
131
-
132
- ## Using Both Together
133
-
134
- Capture stores the full conversation (preserves structure) and extraction derives searchable memories from it — both happen automatically. Add an explicit `memory_add` only for the three cases above.
106
+ On Claude Code, do **not** proactively `memory_add` facts the user states — capture + server-side extraction stores them automatically. Explicit `memory_add` is reserved for the three cases extraction cannot reach (explicit "remember this"; output-only/tool-derived facts; overwriting a negated fact) — the canonical list and rules live in SKILL.md. Use `memory_search` at session start to recall what you already know.
135
107
 
136
108
  ## Key Retrieval Difference
137
109
 
138
- - `memory_search`: **semantic similarity** across all memories via pgvector. Returns relevant facts regardless of when stored.
139
- - `conversation_get`: **ordered list of message turns** for a specific conversation. No semantic search. Requires `conversation_id`.
110
+ - `memory_search`: **semantic similarity** across all memories — relevant facts regardless of when stored.
111
+ - `conversation_get`: **ordered message turns** for one conversation. No semantic search; requires `conversation_id`.
140
112
 
141
113
  ## Automatic Conversation Capture
142
114
 
143
- The Meko plugin captures conversations automatically via three mechanisms:
144
-
145
- 1. **Periodic checkpoint (~10 min)** — Agent-driven via CronCreate, uses `conversation_add_message` MCP tool directly
146
- 2. **PreCompact hook** — Shell script that fires before Claude Code's auto-compaction, captures via MCP JSON-RPC
147
- 3. **SessionEnd hook** — Shell script that fires at session termination, captures final exchanges
148
-
149
- ### What gets captured
150
-
151
- The full transcript including:
152
- - **User prompts** — verbatim text (the `input` field)
153
- - **Assistant responses** — all text blocks (the `output` field)
154
- - **Tool calls** — tool name + input parameters (the `reasoning` field)
155
- - **Tool results** — output from tool executions (appended to `reasoning`)
156
-
157
- This is more than just text — tool calls and results are critical context for session replay, cross-agent sharing, and compaction.
115
+ The plugin captures conversations via three mechanisms — a periodic checkpoint (~10 min), a PreCompact hook, and a SessionEnd hook — storing the full transcript verbatim (user prompts as `input`, assistant text as `output`, tool calls and results as `reasoning`).
158
116
 
159
- ### Seed-based deduplication
117
+ Each message's seed is `<conversation_id>:<user_message_uuid>`; all three mechanisms derive the same deterministic trace ID from it, so overlapping captures are idempotent. A shared watermark file tracks what has been saved.
160
118
 
161
- Each message's seed is `<conversation_id>:<user_message_uuid>`. All three capture mechanisms generate the same seed for the same exchange, producing the same deterministic trace ID via `sha256(seed)[:16].hex()`. This means duplicate writes from overlapping captures are idempotent.
162
-
163
- ### When to manually store conversations
164
-
165
- Automatic capture handles the raw exchange. You should still manually use `conversation_create` + `conversation_add_message` when:
166
- - The user explicitly asks to "save this conversation"
167
- - You want to add curated `reasoning` traces beyond raw tool calls
168
- - You want to store a selected subset of the conversation with a descriptive title
169
-
170
- ### Watermark coordination
171
-
172
- A shared watermark file at `~/.claude/meko-capture/<session-id>.watermark.json` tracks what has been saved. Both hooks and the agent's periodic checkpoint use this to avoid reprocessing already-captured exchanges.
119
+ Manually use `conversation_create` + `conversation_add_message` only when the user explicitly asks to save a conversation, or you want curated `reasoning` traces or a titled subset.
173
120
 
174
121
  ## Observability: conversation_id IS the Langfuse trace ID
175
122
 
176
- This is the feature agents tend to miss. Verified live 2026-05-07 in the Meko UI's Observe hub.
123
+ This is the feature agents tend to miss.
177
124
 
178
- - `conversation_create` returns a `conversation_id` (e.g. `d71fc8b61a7f45d585eea9a6436e0c3f`).
179
- - That ID is also the **Langfuse trace_id** for the conversation. Not a separate trace — literally the same identifier.
180
- - Every subsequent MCP tool call made while passing that `conversation_id` becomes a **span under that trace**, named after the tool: `Memory Add (Meko MCP)`, `Memory Search (Meko MCP)`, `Knowledgebase Search (Meko MCP)`, `Conversation Add Message (Meko MCP)`, etc.
181
- - Child spans reveal internals. Observed structure:
182
- - `memory_add`: `Memory Add (Meko MCP)` → `Create a new memory in Mem0` → `Bulk Payload Update (yugabytedb / vector)` → `Execute Query (yugabytedb / vector)`
183
- - `memory_search`: `Memory Search (Meko MCP)` → `Search for memories based on a query in Mem0` (Retriever)
184
- - `conversation_add_message`: `Conversation Add Message (Meko MCP)` → `Conversation Message Added` span → `Reasoning` generation (the `reasoning` field is stored as a Langfuse *generation*-typed span, not a plain text field)
185
- - `knowledgebase_search`: `Knowledgebase Search (Meko MCP)` → multiple `Execute Query (yugabytedb)` child spans
186
- - Trace tags include `agent:<agent_id>` and the Meko org UUID. Both are searchable/filterable in Langfuse.
187
- - Each datapack has its own Langfuse project — traces for a tool call land in the project for whichever `datapack_id` was on the call (or the default datapack if omitted).
125
+ - `conversation_create` returns a `conversation_id`; that ID **is** the Langfuse trace_id — not a separate trace, the same identifier.
126
+ - Every MCP tool call made with that `conversation_id` becomes a span under the trace, named after the tool (e.g. `Memory Search (Meko MCP)`), with child spans exposing the storage steps.
127
+ - Trace tags include `agent:<agent_id>` and are filterable. Each datapack has its own Langfuse project — traces land in the project of whichever `datapack_id` was on the call.
188
128
 
189
129
  ### Finding a trace in the UI
190
130
 
@@ -195,14 +135,14 @@ Two navigation paths, both verified in the Meko UI:
195
135
 
196
136
  The Conversations-tab row preview shows the first user turn verbatim, plus the agent_id tag, message count, and started-at timestamp. If you want a conversation to be recognizable in the UI, call `conversation_add_message` with a clean first-turn `input` early in the session.
197
137
 
198
- ### Observed latencies on prod (2026-05-07)
138
+ ### Typical latencies
199
139
 
200
140
  From real traces, not the docstring happy path:
201
141
 
202
142
  | Tool | Latency per call |
203
143
  |---|---|
204
- | `memory_add` | 12-21 seconds (calls OpenAI for extraction, then writes to vector + graph) |
205
- | `memory_search` | 1.8-5.9 seconds |
144
+ | `memory_add` | ~10-30 seconds (calls an LLM for extraction, then writes to the vector store; scales with payload size) |
145
+ | `memory_search` | ~2-6 seconds (small, focused memories; whole-document stores read ~4x slower — chunk on write) |
206
146
  | `knowledgebase_search` | 0.6 seconds (empty index; will grow with index size) |
207
147
  | `conversation_add_message` | 0.11 seconds (fire-and-forget; queued to Langfuse) |
208
148
 
@@ -210,9 +150,6 @@ From real traces, not the docstring happy path:
210
150
 
211
151
  ### Proactive diagnostic pattern
212
152
 
213
- If a user reports "my memory_add didn't seem to save", open the Observe hub for their datapack, filter on their `conversation_id`, and look at the span tree. You will see either:
214
-
215
- - A `Memory Add (Meko MCP)` span with `Create a new memory in Mem0` child → the call reached Mem0. The fact extractor may have returned `"results": []` (common on short or non-fact-shaped text; not a bug, but lossy).
216
- - No span at all → the call never reached the server. Likely a transport / auth / `conversation_id` mismatch issue.
153
+ If a user reports "my memory_add didn't seem to save", open the Observe hub, filter on their `conversation_id`, and check the span tree: a memory-add span present means the call arrived (the extractor may still have returned `"results": []` on non-fact-shaped text); no span means the call never reached the server (transport / auth / `conversation_id` mismatch).
217
154
 
218
- The mem0 extractor is lossy — it extracts atomic facts and graph triples, and may drop prose that doesn't look fact-shaped to it. Observed 2026-05-07: 2 of 5 `memory_add` calls returned `"results": []` and only the graph side landed. When preserving authoritative text matters, store it via `conversation_add_message` (verbatim in `input`/`output`/`reasoning`) — the conversation path preserves text without LLM re-extraction.
155
+ The mem0 extractor is lossy — it extracts atomic facts and may drop prose that doesn't look fact-shaped to it; a `memory_add` can return `"results": []` on non-fact-shaped text. When preserving authoritative text matters, store it via `conversation_add_message` (verbatim in `input`/`output`/`reasoning`) — the conversation path preserves text without LLM re-extraction.
@@ -1,25 +1,27 @@
1
1
  # Complete Tool Catalog and Decision Tree
2
2
 
3
- **22 tools on Cloud Meko** (verified against `https://mcp.mekodata.ai/mcp`). Grouped: Memory (8), Conversation (6), Knowledge Base (1), Datapack (5), Artifacts (2), Observability (1).
3
+ **23 Meko data tools covered by this skill.** These are the tools available in production, grouped as: Memory (8), Conversation (6), Knowledge Base (1), Datapack (5), Artifacts (2), Observability (1). Combined Meko + AMP endpoints may expose additional infrastructure tools; those are outside this skill's scope.
4
4
 
5
5
  ## Quick health check before using tools
6
6
 
7
7
  Before starting a session, verify that the Meko MCP tools are working:
8
8
 
9
- 1. `memory_search(scope="read", query="test", agent_id="<your-session-agent-id>", conversation_id="<session-conversation-id>")` — Confirms memory subsystem is up. Use the `agent_id` and `conversation_id` from the SessionStart `additionalContext`. May fail with "connection already closed" — see `tools-troubleshooting.md`.
10
- 2. `datapack_list(scope="read")` — Confirms Meko API connectivity. Returns the datapacks your token has access to.
9
+ 1. `memory_search(query="test", agent_id="<your-session-agent-id>", conversation_id="<session-conversation-id>")` — Confirms memory subsystem is up. Use the `agent_id` and `conversation_id` from the SessionStart `additionalContext`. May fail with "connection already closed" — see `tools-troubleshooting.md`.
10
+ 2. `datapack_list(conversation_id="<session-conversation-id>")` — Confirms Meko API connectivity. Returns the datapacks your token has access to.
11
11
 
12
12
  ## Decision tree: which tool do I need?
13
13
 
14
14
  ```
15
15
  User wants to...
16
- ├── Search a knowledge base? ---------------> knowledgebase_search (read)
16
+ ├── Search personal context? ---------------> memory_search (read)
17
+ ├── Search shared datapack knowledge? ------> knowledgebase_search (read)
17
18
  ├── Add documents to a knowledge base? -----> point user at Meko UI: Datapack → Actions → Add Knowledge
18
19
  │ (no MCP tool — Cloud uses UI ingestion only)
19
20
  ├── Store or recall information?
20
21
  │ ├── Store a fact/preference/entity? -----------> memory_add (write)
21
22
  │ ├── Store a full conversation (multi-turn)? ---> conversation_create + conversation_add_message (write)
22
23
  │ ├── Search past knowledge? --------------------> memory_search (read)
24
+ │ ├── Share private memories with the team? -----> memory_promote (write; owner/maintainer only)
23
25
  │ └── Retrieve a past conversation? -------------> conversation_get (read)
24
26
  ├── Persist or retrieve a file?
25
27
  │ ├── Upload a generated file (report, CSV, PDF)? -> artifact_put (write)
@@ -32,58 +34,58 @@ Agent / knowledge-base lifecycle is managed **outside** the MCP surface — typi
32
34
 
33
35
  ## Knowledge Base Tools (1)
34
36
 
35
- | Tool | Scope | Purpose |
36
- |------|-------|---------|
37
- | `knowledgebase_search(scope, query, agent_id, conversation_id, datapack_id, limit=10)` | read | Semantic search across KB chunks. `datapack_id` is REQUIRED (no default). |
37
+ | Tool | Purpose |
38
+ |------|---------|
39
+ | `knowledgebase_search(query, agent_id, conversation_id, datapack_id, limit=10)` | Semantic search across KB chunks. `datapack_id` is REQUIRED (no default). |
38
40
 
39
41
  ## Memory Tools (8)
40
42
 
41
- | Tool | Scope | Purpose |
42
- |------|-------|---------|
43
- | `memory_add(scope, text, agent_id, conversation_id, user_id=None, app_id=None, run_id=None, metadata=None, messages=None, datapack_id=None)` | write | Store fact/preference/entity as long-term memory |
44
- | `memory_search(scope, query, agent_id, conversation_id="", user_id=None, limit=10, datapack_id=None)` | read | Semantic search across memories + graph relations. `conversation_id` optional (omit or pass `""` for cross-conversation discovery). |
45
- | `memory_get_by_id(scope, memory_id, agent_id, conversation_id, datapack_id=None)` | read | Direct pgvector row lookup by UUID. Preferred over `memory_search` for exact-id verification. |
46
- | `memory_get_all(scope, agent_id, conversation_id, user_id=None, app_id=None, run_id=None, datapack_id=None)` | read | List all memories for agent |
47
- | `memory_update(scope, memory_id, text, agent_id, conversation_id, datapack_id=None)` | write | Overwrite memory text |
48
- | `memory_delete_by_id(scope, memory_id, agent_id, conversation_id, datapack_id=None)` | write | Delete a single memory |
49
- | `memory_delete_all(scope, agent_id, conversation_id, user_id=None, app_id=None, run_id=None, datapack_id=None)` | admin | Delete all memories for agent (destructive) |
50
- | `flush_pending_memory_candidates(scope, agent_id)` | read | **Deprecated.** Superseded by automatic extraction from posted turns — post substantive turns with `conversation_add_message` and the server extracts memories from them. Now returns a `deprecated` payload pointing at `conversation_add_message`; kept one release for backward compatibility. |
43
+ | Tool | Purpose |
44
+ |------|---------|
45
+ | `memory_add(text, agent_id, conversation_id, run_id=None, metadata=None, messages=None, datapack_id=None)` | Store fact/preference/entity as long-term memory |
46
+ | `memory_search(query, agent_id, conversation_id="", limit=10, datapack_id=None)` | Hybrid search across memories (semantic + keyword + entity boost). `conversation_id` optional (omit or pass `""` for cross-conversation discovery). |
47
+ | `memory_get_by_id(memory_id, agent_id, conversation_id, datapack_id=None)` | Direct pgvector row lookup by UUID. Preferred over `memory_search` for exact-id verification. |
48
+ | `memory_get_all(agent_id, conversation_id, run_id=None, datapack_id=None)` | List all of the user's memories (every agent — `agent_id` does not filter) |
49
+ | `memory_update(memory_id, text, agent_id, conversation_id, datapack_id=None)` | Overwrite memory text |
50
+ | `memory_delete_by_id(memory_id, agent_id, conversation_id, datapack_id=None)` | Delete a single memory |
51
+ | `memory_delete_all(agent_id, conversation_id, run_id=None, datapack_id=None)` | Delete all memories for agent (destructive) |
52
+ | `memory_promote(conversation_id, memory_ids, agent_id=None, datapack_id=None)` | Promote private memories into the datapack's shared knowledge base (moves them out of mem0, then evicts from mem0 — one-way). Owners/maintainers only; viewers/contributors get 403. |
51
53
 
52
54
  ## Conversation Tools (6)
53
55
 
54
- | Tool | Scope | Purpose |
55
- |------|-------|---------|
56
- | `conversation_create(scope, agent_id, user_id=None, app_id=None, run_id=None, title=None, metadata=None, session_id="")` | write | Create conversation container (Langfuse session) |
57
- | `conversation_add_message(scope, conversation_id, agent_id, input, output=None, reasoning=None, metadata=None, seed=None, trace_id="")` | write | Add a message turn (Langfuse trace) |
58
- | `conversation_get(scope, conversation_id, agent_id, include_messages=False, limit=100, offset=0)` | read | Retrieve conversation, optionally with messages |
59
- | `conversation_list(scope, agent_id, conversation_id="", user_id=None, limit=20, offset=0)` | read | List conversations for agent. `conversation_id` optional (omit for browse). |
60
- | `conversation_update(scope, conversation_id, agent_id, title=None, metadata=None)` | write | Update title or metadata |
61
- | `conversation_delete(scope, conversation_id, agent_id)` | admin | Delete entire conversation (destructive) |
56
+ | Tool | Purpose |
57
+ |------|---------|
58
+ | `conversation_create(agent_id, run_id=None, title=None, metadata=None, session_id="")` | Create conversation container (Langfuse session) |
59
+ | `conversation_add_message(conversation_id, agent_id, input, output=None, reasoning=None, metadata=None, seed=None)` | Add a message turn (Langfuse trace) |
60
+ | `conversation_get(conversation_id, agent_id, include_messages=False, limit=100, offset=0)` | Retrieve conversation, optionally with messages |
61
+ | `conversation_list(agent_id=None, limit=20, offset=0, conversation_id="", datapack_id=None)` | List the user's conversations within a datapack (`agent_id` does not filter — trace attribution only). `datapack_id` (UUID) is optional — defaults to the pinned/tenant datapack; use `datapack_list` to look it up. `conversation_id` optional (omit for browse). |
62
+ | `conversation_update(conversation_id, agent_id, title=None, metadata=None)` | Update title or metadata |
63
+ | `conversation_delete(conversation_id, agent_id)` | Delete entire conversation (destructive) |
62
64
 
63
65
  ## Datapack Management Tools (5)
64
66
 
65
- | Tool | Scope | Purpose |
66
- |------|-------|---------|
67
- | `datapack_create(scope, name)` | write | Create new datapack |
68
- | `datapack_list(scope)` | read | List all datapacks |
69
- | `datapack_describe(scope, datapack_id, include_status=False)` | read | Describe datapack by UUID |
70
- | `datapack_update(scope, datapack_id, connection_string)` | write | Update connection string |
71
- | `datapack_delete(scope, datapack_id)` | admin | Delete datapack by UUID (irreversible) |
67
+ | Tool | Purpose |
68
+ |------|---------|
69
+ | `datapack_create(name)` | Create new datapack |
70
+ | `datapack_list(conversation_id, datapack_id=None)` | List all datapacks. `conversation_id` is required for trace nesting; omit `datapack_id` to use the endpoint default. |
71
+ | `datapack_describe(datapack_id, include_status=False)` | Describe datapack by UUID |
72
+ | `datapack_update(datapack_id, name=None, description=None, conversation_search_opt_out=None)` | Rename a datapack, update its description, and/or opt out of (or back into) conversation-search caching. Pass at least one field. Opt-out is owner/maintainer only. |
73
+ | `datapack_delete(datapack_id)` | Delete datapack by UUID (irreversible) |
72
74
 
73
75
  ## Artifact Tools (2)
74
76
 
75
77
  Content-addressed blob store scoped to a datapack. Files < 1 MB go inline in the DB; files ≥ 1 MB go to S3. Identity is SHA-256 — uploading the same bytes twice returns the same `content_hash`. Free-tier artifacts expire after 30 days of inactivity; pro-tier artifacts have no TTL.
76
78
 
77
- | Tool | Scope | Purpose |
78
- |------|-------|---------|
79
- | `artifact_put(scope, filename, content_base64, content_type, conversation_id, datapack_id=None, agent_id=None)` | write | Upload a file to the datapack. Returns `{artifact_id, content_hash, filename, size_bytes, stored_in}`. Max 5 MiB (configurable via `MEKO_MAX_ARTIFACT_UPLOAD_BYTES`). |
80
- | `artifact_get(scope, content_hash, conversation_id, datapack_id=None, agent_id=None)` | read | Retrieve a file by SHA-256 hash. Small files return `content_base64` inline; large files (S3) are written to `~/.meko/artifacts/<hash>/<filename>` and `local_path` is returned. |
79
+ | Tool | Purpose |
80
+ |------|---------|
81
+ | `artifact_put(filename, content_base64, content_type, conversation_id, datapack_id=None, agent_id=None)` | Upload a file to the datapack. Returns `{artifact_id, content_hash, filename, size_bytes, stored_in}`. Max 5 MiB (configurable via `MEKO_MAX_ARTIFACT_UPLOAD_BYTES`). |
82
+ | `artifact_get(content_hash, conversation_id, datapack_id=None, agent_id=None)` | Retrieve a file by SHA-256 hash. Small files return `content_base64` inline; large files (S3) are written to `~/.meko/artifacts/<hash>/<filename>` and `local_path` is returned. |
81
83
 
82
84
  ## Observability Tools (1)
83
85
 
84
- | Tool | Scope | Purpose |
85
- |------|-------|---------|
86
- | `track_token_usage(scope, conversation_id, name, input_tokens=0, output_tokens=0, total_tokens=None, model=None, message_id=None, datapack_id=None)` | write | Record an LLM-cost GENERATION observation on the conversation's trace. Primarily called by first-party Meko services (e.g. inference_gateway) that know their LLM's exact token counts. Most end-agents (Cursor, Claude Desktop) don't have those counts at the MCP call layer, so this tool is rarely useful for third-party agents. |
86
+ | Tool | Purpose |
87
+ |------|---------|
88
+ | `track_token_usage(conversation_id, name, input_tokens=0, output_tokens=0, total_tokens=None, model=None, message_id=None, datapack_id=None)` | Record an LLM-cost GENERATION observation on the conversation's trace. Primarily called by first-party Meko services (e.g. inference_gateway) that know their LLM's exact token counts. Most end-agents (Cursor, Claude Desktop) don't have those counts at the MCP call layer, so this tool is rarely useful for third-party agents. |
87
89
 
88
90
  ## Platform capabilities NOT exposed via MCP
89
91
 
@@ -102,4 +104,4 @@ For knowledge-base content on Cloud, the canonical path is **UI upload**: Datapa
102
104
 
103
105
  ## Common Parameter Patterns
104
106
 
105
- Every tool accepts `scope` as the first parameter. Memory and KB tools accept optional `datapack_id` to target a specific datapack. Memory and conversation tools require `agent_id` for namespace isolation (see `tools-agent-id-conventions.md`).
107
+ Memory and KB tools accept optional `datapack_id` to target a specific datapack. `agent_id` attributes writes and is enforced only on `conversation_get` (agent-owned — a wrong value returns `agent_id_mismatch`); it does **not** filter memory reads (`memory_search`/`memory_get_all`) or `conversation_list`, which return all your agents' rows for the user. See `tools-agent-id-conventions.md`.
@@ -16,7 +16,6 @@ creation and ingestion happen out of band — through the **Meko UI**
16
16
 
17
17
  ```
18
18
  knowledgebase_search(
19
- scope="read",
20
19
  query="natural-language question",
21
20
  agent_id="<your agent_id>",
22
21
  conversation_id="<uuid from conversation_create>",
@@ -24,7 +23,7 @@ knowledgebase_search(
24
23
  limit=10)
25
24
  ```
26
25
 
27
- Required: `scope`, `query`, `agent_id`, `conversation_id`, `datapack_id`.
26
+ Required: `query`, `agent_id`, `conversation_id`, `datapack_id`.
28
27
  Unlike the memory tools, `datapack_id` has no default here — you must
29
28
  pass it explicitly. `agent_id` is **ignored for filtering** on this
30
29
  tool (KB results are team-shared on the datapack), but you still pass
@@ -7,7 +7,7 @@ These patterns are extracted from real agent sessions. Follow them to avoid wast
7
7
  1. **Never retry the identical failed call more than once.** If it fails twice with the same error, it's not transient — diagnose the cause.
8
8
  2. **Distinguish transient vs persistent failures.** "Connection already closed" is transient (retry once). "Permission denied" is persistent (stop, don't retry).
9
9
  3. **Don't guess parameters sequentially.** If a tool fails with one parameter format, don't try 4 variations. Check this skill's docs for the correct format first.
10
- 4. **Use your session's `agent_id` consistently.** Pass the value the SessionStart hook injected (e.g. `claude_code:<repo-basename>` for Claude Code; `claude_desktop` for Claude Desktop) on every write and every personal read. Use `agent_id="meko_agent"` deliberately on `memory_search`/`memory_get_all` only when you want the cross-project common bucket — empty string also resolves to `meko_agent`, not a cross-agent fan-out. Don't switch between forms mid-session.
10
+ 4. **Use your session's `agent_id` consistently on writes** (the SessionStart-injected value), and on `conversation_get` — conversations are owned by the creating `agent_id`; a wrong value returns `agent_id_mismatch`. Memory reads and `conversation_list` are unaffected by `agent_id`. Don't switch write forms mid-session.
11
11
 
12
12
  ---
13
13
 
@@ -26,17 +26,24 @@ These patterns are extracted from real agent sessions. Follow them to avoid wast
26
26
 
27
27
  ---
28
28
 
29
- ## Scope parameter errors
29
+ ## An error payload is a FAILED search, never an empty one
30
30
 
31
- **Error:** `Insufficient scope: 'all'. This tool requires 'read' or higher.`
31
+ Quota errors, rate-limit errors, and tool refusals come back as an error object with **no `results` key** — reading `response.get("results", [])` first silently converts "you were refused" into "the store is empty." Follow SKILL.md operating contract 1: report it as "search failed: `<error code>` — findings unknown," never as "no results."
32
32
 
33
- The only valid scope values are: `"read"`, `"write"`, `"admin"`.
33
+ ---
34
+
35
+ ## Rate limiting: `PAT_RATE_LIMITED`
36
+
37
+ Bursts of requests on one token return:
34
38
 
35
- - Use `"read"` for all read operations (default choice)
36
- - Use `"write"` for inserts, updates, creates, memory writes
37
- - Use `"admin"` only for destructive deletes
39
+ ```json
40
+ {"error": "PAT_RATE_LIMITED", "detail": "too many requests for this token; retry after 60s"}
41
+ ```
38
42
 
39
- **Never use:** `"all"`, `"readwrite"`, `"rw"`, or any other value.
43
+ - Throttling is **endpoint-specific**: `artifact_put` bursts and high-concurrency `memory_get_all` trigger it readily; `memory_search` tolerates far more. Do not assume one safe concurrency level for every tool.
44
+ - A rate-limited `artifact_put` returns this error **instead of** a `content_hash` — checking only for the hash misreads throttling as silent write loss.
45
+ - Honor the retry-after (~60s) or back off exponentially; pace sustained work. Retry only idempotent operations or hash-protected writes.
46
+ - Distinct from the lifetime quota error (`free_tier_limit_reached`) — waiting never recovers that one.
40
47
 
41
48
  ---
42
49
 
@@ -52,9 +59,9 @@ If you call `memory_add` with empty / None / whitespace `agent_id`, the server d
52
59
 
53
60
  Fix: always pass a concrete non-empty `agent_id`. Fetch the value the SessionStart hook injected into `additionalContext` and pass it verbatim. Use `agent_id="meko_agent"` only when you genuinely want a fact in the cross-project pool (user identity, global preferences).
54
61
 
55
- ### "My reads return fewer results than I expected"
62
+ ### "My memory reads return fewer results than I expected"
56
63
 
57
- `memory_search` and `memory_get_all` filter strictly on your passed `agent_id`. A memory written by `claude-code:<slug-A>` is invisible to a search with `agent_id="cursor:<slug-B>"`. If you need a broader view, call repeatedly across the agent_ids you want to cover, or point the user at the UI's Memory Summary page (which shows every row regardless of agent_id).
64
+ `memory_search` and `memory_get_all` do **not** filter by `agent_id` — a single call returns all of your memories for this user across every agent, so a mismatched `agent_id` is not the cause. If results seem thin, check that you're on the right datapack (`datapack_id`), that the write actually landed (mem0 extraction is lossy — see `tools-memory-vs-conversation.md`), and that the query is semantically close to the stored text. `conversation_get`, by contrast, *is* agent-owned: pass the exact `agent_id` the conversation was created under, or you'll get `agent_id_mismatch`. `conversation_list` is not agent-filtered — it returns the datapack's conversations for your user regardless of the `agent_id` passed.
58
65
 
59
66
  ### "I'm seeing rows tagged with legacy agent_id values"
60
67
 
@@ -66,7 +73,7 @@ Pre-existing data may be tagged `"agent"`, `"claude_code"`, `"cursor:<slug>"`, o
66
73
 
67
74
  | Tool | Safe to retry? | Why |
68
75
  |------|---------------|-----|
69
- | All `read` scope tools | Yes | Reads are idempotent |
76
+ | All read-only tools | Yes | Reads are idempotent |
70
77
  | `memory_add` | Yes (once) | Mem0 has dedup logic |
71
78
  | `memory_update` | Yes (once) | Overwrites same ID |
72
79
  | `memory_delete_by_id` | Yes | Deleting already-deleted is a no-op |
@@ -1,6 +1,6 @@
1
1
  # user_id — identity scoping on Cloud Meko
2
2
 
3
- On Cloud Meko, every write and every personal read carries a `user_id` that identifies **who** (the cognito account) is calling. The filter tuple for personal memories and conversations is `(datapack_id, user_id, agent_id)` — you only ever see memories your own user_id wrote (or content other team members have explicitly promoted to Shared Knowledge, which is separate — see `tools-agent-id-conventions.md`).
3
+ On Cloud Meko, every write and every personal read carries a `user_id` that identifies **who** (the cognito account) is calling. The read filter for personal **memories** is `(datapack_id, user_id)` — you see all your own memories for this user regardless of which agent wrote them (`agent_id` labels the writer but does not filter memory reads). Personal **conversation fetches** (`conversation_get`) additionally enforce `agent_id` — conversations are owned by the agent that created them; `conversation_list` does not filter by it. Either way you only ever see content your own user_id wrote (or content other team members have explicitly promoted to Shared Knowledge, which is separate — see `tools-agent-id-conventions.md`).
4
4
 
5
5
  ## The default behavior: automatic user_id from cognito
6
6
 
@@ -9,7 +9,7 @@ On Cloud, the server resolves your `user_id` automatically from the cognito acco
9
9
  This is the common case for Claude Code / Claude Desktop / Cursor:
10
10
 
11
11
  ```
12
- memory_add(scope="write",
12
+ memory_add(
13
13
  text="User prefers concise responses, no emojis.",
14
14
  agent_id="claude_code:meko-mcp-server",
15
15
  conversation_id="<session conversation_id>")
@@ -24,21 +24,21 @@ Explicit `user_id` is an **optional sub-scope** for agents that serve multiple e
24
24
 
25
25
  ```
26
26
  # Serving Alice
27
- memory_add(scope="write",
27
+ memory_add(
28
28
  text="Alice prefers email communication. Has Pro plan.",
29
29
  agent_id="support-bot",
30
30
  user_id="alice_123",
31
31
  conversation_id="<conv>")
32
32
 
33
33
  # Serving Bob — completely separate partition, same cognito account
34
- memory_add(scope="write",
34
+ memory_add(
35
35
  text="Bob prefers Slack. On Enterprise plan.",
36
36
  agent_id="support-bot",
37
37
  user_id="bob_456",
38
38
  conversation_id="<conv>")
39
39
 
40
40
  # Search only returns Alice's memories
41
- memory_search(scope="read",
41
+ memory_search(
42
42
  query="communication preference",
43
43
  agent_id="support-bot",
44
44
  user_id="alice_123",
@@ -60,11 +60,12 @@ Mixing creates invisible data.
60
60
 
61
61
  | Layer | Filter | Role of user_id |
62
62
  |---|---|---|
63
- | Personal memories (`memory_add`, `memory_search`, `memory_get_all`) | `(datapack_id, user_id, agent_id)` | Enforced on every read. One user cannot see another's un-promoted memories. |
64
- | Personal conversations (`conversation_list`, `conversation_get`) | Same tuple | Enforced. Cross-user reads require promotion. |
63
+ | Personal memories (`memory_add`, `memory_search`, `memory_get_all`) | `(datapack_id, user_id)` | Enforced on every read. `agent_id` is not applied — one user sees all their own memories across every agent, but cannot see another user's un-promoted memories. |
64
+ | Conversation listing (`conversation_list`) | `(datapack_id, user_id)` | Enforced. `agent_id` is not applied — you see all your own conversations across every agent. |
65
+ | Conversation fetch (`conversation_get`) | `(datapack_id, user_id, agent_id)` | Enforced. Agent-owned: pass the creating `agent_id` or you get `agent_id_mismatch`. Cross-user reads require promotion. |
65
66
  | Team Shared Knowledge (`knowledgebase_search`) | `(datapack_id)` only | **Not used** — `user_id` is stripped when a memory is promoted to Knowledge. All team members on the datapack see the same Shared Knowledge. |
66
67
 
67
- The "Promote to Knowledge" action in the Cloud UI is the one way personal content crosses the user_id boundary. There is no MCP tool for promotion (user-initiated via UI only).
68
+ Personal content crosses the `user_id` boundary through `memory_promote` or the Cloud UI's "Promote to Knowledge" action. The MCP path requires exact memory UUIDs, explicit user confirmation, and an owner/maintainer role; it moves the records into team-visible Shared Knowledge and evicts the private copies.
68
69
 
69
70
  ## Parameter summary
70
71
 
@@ -74,6 +75,6 @@ The "Promote to Knowledge" action in the Cloud UI is the one way personal conten
74
75
  | `user_id` | The cognito account or end-user (for multi-user agents) | auto from cognito for single-human clients; explicit string for multi-user agents | Optional (server auto-fills from cognito context) |
75
76
  | `agent_id` | Which agent wrote / is reading | `"claude_code:<repo>"`, `"claude_desktop"`, `"cursor:<repo>"`, `"meko_agent"` (common bucket) | Optional (empty/missing → `meko_agent` server-side); pass the per-session value verbatim for project-scoped reads/writes. Ignored on `knowledgebase_search`. |
76
77
  | `app_id` | Optional application/product sub-scope | `"second_brain"`, `"helpdesk"` | Optional |
77
- | `run_id` | Optional specific execution run | `"run_20260507_001"` | Optional |
78
+ | `run_id` | A conversation id, used as a read/delete filter against the row's `meko_conversation_id` | `"374c70f3b95745ac92724d98475cc2d6"` | Optional. On `memory_add` it is trace metadata only and does not scope the write — see `tools-agent-id-conventions.md`. |
78
79
 
79
80
  See `tools-agent-id-conventions.md` for the full `agent_id` model.