@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.
- package/README.md +183 -33
- package/assets/hooks-handlers/codex-session-end.sh +13 -0
- package/assets/hooks-handlers/cursor-session-start.sh +19 -1
- package/assets/hooks-handlers/lib/capture-notices.js +321 -0
- package/assets/hooks-handlers/lib/capture-state.js +2079 -0
- package/assets/hooks-handlers/lib/capture-status-cli.js +82 -0
- package/assets/hooks-handlers/lib/capture.js +1653 -225
- package/assets/hooks-handlers/lib/checkpoint-timer.js +455 -50
- package/assets/hooks-handlers/lib/kiro-handler.js +738 -0
- package/assets/hooks-handlers/lib/terminal-handoff.js +418 -0
- package/assets/hooks-handlers/session-end.sh +3 -24
- package/assets/hooks-handlers/session-start.sh +8 -1
- package/assets/hooks-handlers/test_checkpoint_timer.sh +162 -0
- package/assets/scripts/meko-statusline.sh +41 -1
- package/assets/scripts/package-desktop-skill.sh +32 -1
- package/assets/skills/meko-mcp-tools/SKILL.md +90 -137
- package/assets/skills/meko-mcp-tools/references/_sections.md +3 -5
- package/assets/skills/meko-mcp-tools/references/tools-agent-id-conventions.md +31 -28
- package/assets/skills/meko-mcp-tools/references/tools-conversation-dedup.md +5 -7
- package/assets/skills/meko-mcp-tools/references/tools-conversation-reasoning.md +50 -0
- package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +69 -49
- package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +6 -6
- package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +29 -5
- package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +29 -92
- package/assets/skills/meko-mcp-tools/references/tools-overview.md +42 -40
- package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +1 -2
- package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +18 -11
- package/assets/skills/meko-mcp-tools/references/tools-user-id-conventions.md +10 -9
- package/assets/skills/meko-mcp-tools-desktop/SKILL.md +51 -44
- package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +6 -6
- package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +2 -2
- package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +34 -25
- package/assets/skills/meko-mcp-tools-desktop/references/tools-conversation-reasoning.md +50 -0
- package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +63 -37
- package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +4 -4
- package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +19 -22
- package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +42 -40
- package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +1 -2
- package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +3 -17
- package/assets/skills/meko-select-datapack-desktop/SKILL.md +31 -21
- package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +8 -8
- package/assets/skills/meko-select-datapack-desktop/references/role-display-future.md +41 -0
- package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +1 -1
- package/bin/create-meko-setup.mjs +93 -16
- package/lib/capture-status.mjs +67 -0
- package/lib/clients/aliases.mjs +5 -0
- package/lib/clients/claude-code.mjs +8 -4
- package/lib/clients/claude-desktop.mjs +133 -4
- package/lib/clients/codex.mjs +29 -11
- package/lib/clients/common.mjs +2 -0
- package/lib/clients/cursor.mjs +7 -2
- package/lib/clients/index.mjs +2 -0
- package/lib/clients/kiro.mjs +705 -0
- package/lib/config.mjs +90 -0
- package/lib/detect.mjs +7 -2
- package/lib/installer.mjs +1 -0
- package/lib/migrate/adapters/codex.mjs +490 -0
- package/lib/migrate/id.mjs +28 -3
- package/lib/migrate/orchestrator.mjs +34 -7
- package/lib/migrate.mjs +4 -2
- package/lib/prompt.mjs +17 -2
- package/lib/url-selection.mjs +55 -0
- package/package.json +4 -4
- package/assets/skills/meko-mcp-tools/references/tools-scope-permissions.md +0 -71
- package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +0 -71
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
# Tool Cookbook — Complete Usage Examples
|
|
2
2
|
|
|
3
|
-
Every tool example below shows the correct parameters, expected response, and common errors.
|
|
3
|
+
Every tool example below shows the correct parameters, expected response, and common errors.
|
|
4
4
|
|
|
5
|
-
> **Note on `agent_id` in the examples below.** The string `"agent"` appears in these examples as a placeholder shorthand; it is NOT a required constant. Substitute **your** client's
|
|
5
|
+
> **Note on `agent_id` in the examples below.** The string `"agent"` appears in these examples as a placeholder shorthand; it is NOT a required constant. Substitute **your** client's value: for Claude Desktop, use `claude_desktop`; for Claude Code/Cursor, use `<client>:<repo-basename>` (e.g. `claude_code:meko-mcp-server`). Write genuinely cross-project facts with `agent_id="meko_agent"`. On memory reads, `agent_id` attributes the trace but does not filter results. See `tools-agent-id-conventions.md`.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## Memory Tools
|
|
10
10
|
|
|
11
|
-
**Critical:** Pass your session's `agent_id` on every memory call. For Claude Desktop that's `claude_desktop` for
|
|
11
|
+
**Critical:** Pass your session's `agent_id` on every memory call. For Claude Desktop that's `claude_desktop` for normal attribution, or `meko_agent` when writing genuinely cross-project facts. Writes retain that attribution, while personal memory reads span all of this user's agents.
|
|
12
12
|
|
|
13
13
|
### memory_add
|
|
14
14
|
|
|
15
15
|
**When to use:** NOT for facts the user states — those are captured when you post the turn with `conversation_add_message` and the server extracts them. Reserve `memory_add` for the three cases per-turn capture can't reach: (1) the user explicitly says "remember this"; (2) a durable fact only in your output or a tool result, never in a user turn; (3) overwriting a negated/corrected fact (pair with `memory_delete_by_id`, since extraction is additive).
|
|
16
16
|
|
|
17
17
|
```
|
|
18
|
-
memory_add(
|
|
18
|
+
memory_add(agent_id="claude_desktop",
|
|
19
19
|
text="Remember: deploy scripts must be run from the repo root, never a subdir.")
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -26,7 +26,7 @@ memory_add(scope="write", agent_id="claude_desktop",
|
|
|
26
26
|
|
|
27
27
|
**With user scoping:**
|
|
28
28
|
```
|
|
29
|
-
memory_add(
|
|
29
|
+
memory_add(agent_id="agent", user_id="amiram",
|
|
30
30
|
text="Prefers Python for backend, Go for infrastructure.")
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -44,8 +44,12 @@ memory_add(scope="write", agent_id="agent", user_id="amiram",
|
|
|
44
44
|
|
|
45
45
|
**When to use:** Find relevant memories by meaning. Always try this before asking the user to repeat information.
|
|
46
46
|
|
|
47
|
+
**Cross-conversation discovery.** `memory_search` returns hits across every stored conversation for the `(datapack_id, user_id)` pair, across all agents (the tool intentionally passes `meko_agent_id=None` to mem0, so the `agent_id` argument scopes the trace but does not filter results). When a hit references an interesting conversation, follow up with `conversation_list` to browse the associated threads and `conversation_get` to read a specific one — the memory hit's `meko_conversation_id` field is the id to fetch. Pass `run_id` on the search itself to narrow to a single conversation up front.
|
|
48
|
+
|
|
49
|
+
**Relevance floor.** Vector-matched `results` below `MEMORY_SEARCH_MIN_SCORE` (mem0's cosine-similarity score, default `0.5`) are dropped as weak matches rather than returned. A query can come back with fewer results than `limit`, or an empty list, when nothing clears the bar; that means no sufficiently relevant memory, not a broken call.
|
|
50
|
+
|
|
47
51
|
```
|
|
48
|
-
memory_search(
|
|
52
|
+
memory_search(query="What programming language does the team use?", agent_id="agent")
|
|
49
53
|
```
|
|
50
54
|
|
|
51
55
|
**Response:**
|
|
@@ -55,7 +59,7 @@ memory_search(scope="read", query="What programming language does the team use?"
|
|
|
55
59
|
|
|
56
60
|
**With limit:**
|
|
57
61
|
```
|
|
58
|
-
memory_search(
|
|
62
|
+
memory_search(query="user preferences", agent_id="agent", limit=5)
|
|
59
63
|
```
|
|
60
64
|
|
|
61
65
|
---
|
|
@@ -65,12 +69,12 @@ memory_search(scope="read", query="user preferences", agent_id="agent", limit=5)
|
|
|
65
69
|
**When to use:** List everything stored for an agent. Useful at session start to load context.
|
|
66
70
|
|
|
67
71
|
```
|
|
68
|
-
memory_get_all(
|
|
72
|
+
memory_get_all(agent_id="agent")
|
|
69
73
|
```
|
|
70
74
|
|
|
71
75
|
**With user scoping:**
|
|
72
76
|
```
|
|
73
|
-
memory_get_all(
|
|
77
|
+
memory_get_all(agent_id="agent", user_id="amiram")
|
|
74
78
|
```
|
|
75
79
|
|
|
76
80
|
---
|
|
@@ -78,7 +82,7 @@ memory_get_all(scope="read", agent_id="agent", user_id="amiram")
|
|
|
78
82
|
### memory_get_by_id
|
|
79
83
|
|
|
80
84
|
```
|
|
81
|
-
memory_get_by_id(
|
|
85
|
+
memory_get_by_id(memory_id="mem-uuid-123", agent_id="agent")
|
|
82
86
|
```
|
|
83
87
|
|
|
84
88
|
---
|
|
@@ -88,7 +92,7 @@ memory_get_by_id(scope="read", memory_id="mem-uuid-123", agent_id="agent")
|
|
|
88
92
|
**When to use:** Overwrite a specific memory's text. Requires the memory UUID.
|
|
89
93
|
|
|
90
94
|
```
|
|
91
|
-
memory_update(
|
|
95
|
+
memory_update(memory_id="mem-uuid-123", text="Updated: Team uses Go for all new services", agent_id="agent")
|
|
92
96
|
```
|
|
93
97
|
|
|
94
98
|
---
|
|
@@ -96,19 +100,39 @@ memory_update(scope="write", memory_id="mem-uuid-123", text="Updated: Team uses
|
|
|
96
100
|
### memory_delete_by_id
|
|
97
101
|
|
|
98
102
|
```
|
|
99
|
-
memory_delete_by_id(
|
|
103
|
+
memory_delete_by_id(memory_id="mem-uuid-123", agent_id="agent")
|
|
100
104
|
```
|
|
101
105
|
|
|
102
106
|
---
|
|
103
107
|
|
|
104
108
|
### memory_delete_all
|
|
105
109
|
|
|
106
|
-
**Destructive
|
|
110
|
+
**Destructive.** Deletes all memories for the agent. For removing a single memory, prefer `memory_delete_by_id` — `memory_delete_all` wipes the entire agent scope in the datapack.
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
memory_delete_all(agent_id="agent")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### memory_promote
|
|
119
|
+
|
|
120
|
+
**When to use:** The user asks to share or promote specific memories to the team's knowledge base — the MCP counterpart of the Cloud UI's "Promote to Knowledge" flow. One-way: promoted memories become team-visible via `knowledgebase_search` and are evicted from the private memory store. Requires the caller to be a datapack **owner or maintainer**; viewers and contributors get a 403 permission error.
|
|
107
121
|
|
|
108
122
|
```
|
|
109
|
-
|
|
123
|
+
memory_promote(memory_ids=["mem-uuid-123", "mem-uuid-456"],
|
|
124
|
+
agent_id="claude_desktop", conversation_id="<uuid from conversation_create>")
|
|
110
125
|
```
|
|
111
126
|
|
|
127
|
+
**Response:**
|
|
128
|
+
```json
|
|
129
|
+
{"inserted_ids": ["mem-uuid-123"], "updated_ids": ["mem-uuid-456"], "not_found_ids": []}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Get exact UUIDs from the `id` field of `memory_search` / `memory_get_all` results; never pass any other identifier. Before the call, show the user each exact memory and UUID, explain that promotion is team-visible, one-way, and evicts the private records, then obtain explicit confirmation for those candidates. Pass the active `conversation_id` and intended `agent_id` / `datapack_id`; on legacy schemas that expose `scope`, use `write`, not `admin`.
|
|
133
|
+
|
|
134
|
+
On 403, authentication, or permission failure, report the error and stop — do not escalate scope, change datapacks, or alter identity. After a successful call, `knowledgebase_search` may verify shared visibility, but it is not a rollback mechanism.
|
|
135
|
+
|
|
112
136
|
---
|
|
113
137
|
|
|
114
138
|
## Conversation Tools
|
|
@@ -118,7 +142,7 @@ memory_delete_all(scope="admin", agent_id="agent")
|
|
|
118
142
|
**When to use:** Start storing a multi-turn exchange. Do this when the session contains valuable dialog worth preserving.
|
|
119
143
|
|
|
120
144
|
```
|
|
121
|
-
conversation_create(
|
|
145
|
+
conversation_create(agent_id="agent", user_id="amiram",
|
|
122
146
|
title="Debugging the auth middleware")
|
|
123
147
|
```
|
|
124
148
|
|
|
@@ -133,23 +157,19 @@ conversation_create(scope="write", agent_id="agent", user_id="amiram",
|
|
|
133
157
|
|
|
134
158
|
**When to use:** Add a user/assistant exchange to an existing conversation. **All fields must be verbatim** — never summarize or rephrase.
|
|
135
159
|
|
|
136
|
-
Pass `index_for_search=True` on substantive turns so this conversation is embedded into the shared conversation-history cache (searchable later via `conversation_search` / `contextual_prominence_search`). This isn't limited to WorkbenchLM — pass it for agent-harness conversations too.
|
|
137
|
-
|
|
138
160
|
```
|
|
139
|
-
conversation_add_message(
|
|
161
|
+
conversation_add_message(conversation_id="conv-uuid-123", agent_id="agent",
|
|
140
162
|
input="Why is the auth middleware returning 401?",
|
|
141
163
|
output="The token validation is checking the wrong issuer claim...",
|
|
142
|
-
reasoning="Checked the middleware source, found issuer mismatch between config and JWT..."
|
|
143
|
-
index_for_search=True)
|
|
164
|
+
reasoning="Checked the middleware source, found issuer mismatch between config and JWT...")
|
|
144
165
|
```
|
|
145
166
|
|
|
146
167
|
**With dedup seed (use when client hooks also write to Langfuse):**
|
|
147
168
|
```
|
|
148
|
-
conversation_add_message(
|
|
169
|
+
conversation_add_message(conversation_id="conv-uuid-123", agent_id="agent",
|
|
149
170
|
input="Why is the auth middleware returning 401?",
|
|
150
171
|
output="The token validation is checking the wrong issuer claim...",
|
|
151
|
-
seed="conv-uuid-123:agent:Why is the auth middleware returning 401?"
|
|
152
|
-
index_for_search=True)
|
|
172
|
+
seed="conv-uuid-123:agent:Why is the auth middleware returning 401?")
|
|
153
173
|
```
|
|
154
174
|
|
|
155
175
|
---
|
|
@@ -157,7 +177,7 @@ conversation_add_message(scope="write", conversation_id="conv-uuid-123", agent_i
|
|
|
157
177
|
### conversation_get
|
|
158
178
|
|
|
159
179
|
```
|
|
160
|
-
conversation_get(
|
|
180
|
+
conversation_get(conversation_id="conv-uuid-123", agent_id="agent",
|
|
161
181
|
include_messages=True, limit=50)
|
|
162
182
|
```
|
|
163
183
|
|
|
@@ -165,8 +185,12 @@ conversation_get(scope="read", conversation_id="conv-uuid-123", agent_id="agent"
|
|
|
165
185
|
|
|
166
186
|
### conversation_list
|
|
167
187
|
|
|
188
|
+
Target the datapack by `datapack_id` (UUID). It's optional — omit it to use the
|
|
189
|
+
pinned/default datapack, or pass the pinned `datapack_id`. Use `datapack_list`
|
|
190
|
+
to look up the id.
|
|
191
|
+
|
|
168
192
|
```
|
|
169
|
-
conversation_list(
|
|
193
|
+
conversation_list(datapack_id="<datapack-uuid>", agent_id="agent", limit=20)
|
|
170
194
|
```
|
|
171
195
|
|
|
172
196
|
---
|
|
@@ -174,7 +198,7 @@ conversation_list(scope="read", agent_id="agent", limit=20)
|
|
|
174
198
|
### conversation_update
|
|
175
199
|
|
|
176
200
|
```
|
|
177
|
-
conversation_update(
|
|
201
|
+
conversation_update(conversation_id="conv-uuid-123", agent_id="agent",
|
|
178
202
|
title="Resolved: Auth middleware issuer mismatch")
|
|
179
203
|
```
|
|
180
204
|
|
|
@@ -182,10 +206,10 @@ conversation_update(scope="write", conversation_id="conv-uuid-123", agent_id="ag
|
|
|
182
206
|
|
|
183
207
|
### conversation_delete
|
|
184
208
|
|
|
185
|
-
**Destructive
|
|
209
|
+
**Destructive.**
|
|
186
210
|
|
|
187
211
|
```
|
|
188
|
-
conversation_delete(
|
|
212
|
+
conversation_delete(conversation_id="conv-uuid-123", agent_id="agent")
|
|
189
213
|
```
|
|
190
214
|
|
|
191
215
|
---
|
|
@@ -202,7 +226,6 @@ See `tools-rag-workflow.md` for the decision table.
|
|
|
202
226
|
|
|
203
227
|
```
|
|
204
228
|
knowledgebase_search(
|
|
205
|
-
scope="read",
|
|
206
229
|
query="natural-language question",
|
|
207
230
|
agent_id="agent",
|
|
208
231
|
conversation_id="<uuid from conversation_create>",
|
|
@@ -229,7 +252,6 @@ Populated KBs return results with chunk content and similarity scores. An empty/
|
|
|
229
252
|
|
|
230
253
|
```
|
|
231
254
|
artifact_put(
|
|
232
|
-
scope="write",
|
|
233
255
|
filename="analysis_report.csv",
|
|
234
256
|
content_type="text/csv",
|
|
235
257
|
content_base64="<base64-encoded bytes>",
|
|
@@ -265,7 +287,6 @@ Save the `content_hash` — it's the retrieval key for `artifact_get`.
|
|
|
265
287
|
**Small files (< 1 MB) — content returned inline:**
|
|
266
288
|
```
|
|
267
289
|
artifact_get(
|
|
268
|
-
scope="read",
|
|
269
290
|
content_hash="a665a45920422f9d417e4867efdc4fb8a5f1f89ea5b8440b1ad4c4c6b8a7e4d3",
|
|
270
291
|
conversation_id="<uuid from conversation_create>",
|
|
271
292
|
agent_id="claude_desktop"
|
|
@@ -305,16 +326,21 @@ For large files use `local_path` to read the file. S3 URLs are never exposed.
|
|
|
305
326
|
### datapack_create / datapack_list / datapack_describe
|
|
306
327
|
|
|
307
328
|
```
|
|
308
|
-
datapack_create(
|
|
309
|
-
datapack_list(
|
|
310
|
-
datapack_describe(
|
|
329
|
+
datapack_create(name="analytics_prod")
|
|
330
|
+
datapack_list(conversation_id="<this conversation's id>") # returns datapack_id for each datapack
|
|
331
|
+
datapack_describe(datapack_id="<uuid>", include_status=True)
|
|
311
332
|
```
|
|
312
333
|
|
|
313
334
|
### datapack_update / datapack_delete
|
|
314
335
|
|
|
336
|
+
`datapack_update` renames a datapack, edits its description, and/or opts it in/out of conversation-search embedding. At least one of `name` / `description` / `conversation_search_opt_out` must be provided (or the tool returns `nothing_to_update`). The server refuses to rename `meko_default_datapack` — the error is `"meko_default_datapack cannot be renamed"`. Passing `description=""` does NOT clear an existing description. `conversation_search_opt_out=True` stops future turns from being indexed and is restricted to the datapack's owner or a maintainer. This is best-effort, not a hard guarantee — a transient database error on the live embed path's opt-out check fails open, so a turn can occasionally still get embedded for an opted-out datapack.
|
|
337
|
+
|
|
315
338
|
```
|
|
316
|
-
datapack_update(
|
|
317
|
-
|
|
339
|
+
datapack_update(datapack_id="<uuid>", name="renamed-datapack")
|
|
340
|
+
datapack_update(datapack_id="<uuid>", description="new description")
|
|
341
|
+
datapack_update(datapack_id="<uuid>", name="x", description="y")
|
|
342
|
+
datapack_update(datapack_id="<uuid>", conversation_search_opt_out=True)
|
|
343
|
+
datapack_delete(datapack_id="<uuid>") # Irreversible!
|
|
318
344
|
```
|
|
319
345
|
|
|
320
346
|
### Agent and knowledge-base management (NOT MCP — control-plane only)
|
|
@@ -322,6 +348,6 @@ datapack_delete(scope="admin", datapack_id="<uuid>") # Irreversible!
|
|
|
322
348
|
Agent and KB lifecycle does not ship as MCP tools. If the user asks to create/list/delete agents or KB sources, don't invent a tool call — point them at the control plane:
|
|
323
349
|
|
|
324
350
|
- REST: `POST/GET/DELETE /datapacks/:datapack_id/agents` (agents), `POST/GET/DELETE /datapacks/:datapack_id/knowledge-bases` (KB sources).
|
|
325
|
-
- Or the Meko UI at `
|
|
351
|
+
- Or the Meko Cloud UI at `cloud.mekodata.ai` → Datapacks → agents / knowledge-bases.
|
|
326
352
|
|
|
327
353
|
KB ingestion specifically lives in the UI today: Datapack → Actions → **Add Knowledge** (PDF/TXT/MD/JSON/MP4, 5 MB each, 10/batch). Agents querying a populated index use `knowledgebase_search` — see `tools-rag-workflow.md`.
|
|
@@ -36,7 +36,7 @@ It works **poorly** for:
|
|
|
36
36
|
|
|
37
37
|
1. **Write a single narrative summary** as a memory — one call, not row-by-row:
|
|
38
38
|
```
|
|
39
|
-
memory_add(
|
|
39
|
+
memory_add(
|
|
40
40
|
agent_id="<your session agent_id>",
|
|
41
41
|
conversation_id="<session conversation_id>",
|
|
42
42
|
text="Customer Complaints dataset has fields: Complaint ID (int PK), Date Submitted (date), Product (text, e.g. Mortgage/Credit Card), Sub-product (text), Issue (text), Company Response (text), State (text, 2-letter code), ZIP Code (text)")
|
|
@@ -56,8 +56,8 @@ The skill description is tuned to its 1024-char ceiling; broadening it further d
|
|
|
56
56
|
2. **Server-side guidance** — the `agent_id` tool docstrings and the server's connect-time instructions name `claude_desktop` as this client's bucket, so even a skill-less tool-search call avoids the wrong-bucket (`claude_code`) write.
|
|
57
57
|
3. Nothing is lost when the *user* explicitly recalls or saves — only passive capture of in-passing facts is best-effort.
|
|
58
58
|
|
|
59
|
-
## No conversation
|
|
59
|
+
## No semantic search over conversation content
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Semantic (meaning-based) search across stored conversation content is not part of the public tool surface today. To find a past conversation, browse with `conversation_list` (by datapack) and inspect candidates with `conversation_get`.
|
|
62
62
|
|
|
63
|
-
For finding past knowledge by meaning,
|
|
63
|
+
For finding past knowledge by meaning, `memory_search` remains the most reliable path — which is why storing key facts via `memory_add` alongside conversations is important.
|
|
@@ -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
|
|
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(
|
|
14
|
+
memory_add(agent_id="support_bot",
|
|
15
15
|
text="Customer Alice (alice@acme.com) prefers email, has Pro plan.", user_id="alice_123")
|
|
16
16
|
|
|
17
|
-
memory_search(
|
|
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(
|
|
29
|
+
conversation_create(agent_id="support_bot", user_id="alice_123", title="Pricing discussion")
|
|
30
30
|
-- Returns: {"id": "conv-uuid-here"}
|
|
31
31
|
|
|
32
|
-
conversation_add_message(
|
|
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,44 @@ 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
|
|
68
|
+
## Personal memory vs. team-shared knowledge — the two read paths
|
|
69
69
|
|
|
70
|
-
Agents on Cloud Meko have
|
|
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
|
|
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)` and not by `agent_id` | `memory_search(agent_id="claude_desktop", query="...", ...)` — `agent_id` attributes the trace |
|
|
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
|
-
|
|
77
|
+
One `memory_search` already spans this user's memories across all agents; no per-agent fan-out is needed.
|
|
79
78
|
|
|
80
79
|
### How content gets into each surface
|
|
81
80
|
|
|
82
|
-
- **Personal memories** — written by `memory_add`.
|
|
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 and no other user can.
|
|
83
82
|
- **Team-shared Shared Knowledge** — arrives two ways:
|
|
84
|
-
1.
|
|
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
|
-
|
|
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
|
|
92
|
+
A full sweep is two calls (budget for it — each is 2-6 seconds):
|
|
94
93
|
|
|
95
94
|
```
|
|
96
95
|
memory_search(agent_id="claude_desktop", query="X", conversation_id=..., ...)
|
|
97
|
-
memory_search(agent_id="meko_agent", query="X", conversation_id=..., ...) # cross-project common bucket
|
|
98
96
|
knowledgebase_search(agent_id="<anything>", datapack_id="<uuid>", query="X", conversation_id=..., ...)
|
|
99
97
|
```
|
|
100
98
|
|
|
99
|
+
The single `memory_search` includes everything this user and their other agents wrote in the datapack.
|
|
100
|
+
|
|
101
101
|
When you answer, be explicit about scope so the user knows why something is or isn't there:
|
|
102
102
|
|
|
103
|
-
- "I found this in your
|
|
104
|
-
- "I found this in your common cross-project bucket (something you or another agent wrote under `meko_agent`)…"
|
|
103
|
+
- "I found this in your personal memories (something you or another of your agents saved earlier)…"
|
|
105
104
|
- "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
|
|
107
|
-
|
|
108
|
-
For a true fan-out across other clients' buckets (e.g. also see what was written from `claude_code:some-repo` or `cursor:foo`), call `memory_search` once per known agent_id — empty string just resolves to `meko_agent`, not a fan-out.
|
|
105
|
+
- "I couldn't find anything in your personal memories. You might want to check the Cloud UI's Memory Summary tab."
|
|
109
106
|
|
|
110
107
|
## Memory limitations for structured data
|
|
111
108
|
|
|
@@ -197,7 +194,7 @@ From real traces, not the docstring happy path:
|
|
|
197
194
|
|
|
198
195
|
| Tool | Latency per call |
|
|
199
196
|
|---|---|
|
|
200
|
-
| `memory_add` | 12-21 seconds (calls OpenAI for extraction, then writes to vector
|
|
197
|
+
| `memory_add` | 12-21 seconds (calls OpenAI for extraction, then writes to the vector store) |
|
|
201
198
|
| `memory_search` | 1.8-5.9 seconds |
|
|
202
199
|
| `knowledgebase_search` | 0.6 seconds (empty index; will grow with index size) |
|
|
203
200
|
| `conversation_add_message` | 0.11 seconds (fire-and-forget; queued to Langfuse) |
|
|
@@ -211,4 +208,4 @@ If a user reports "my memory_add didn't seem to save", open the Observe hub for
|
|
|
211
208
|
- 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).
|
|
212
209
|
- No span at all → the call never reached the server. Likely a transport / auth / `conversation_id` mismatch issue.
|
|
213
210
|
|
|
214
|
-
The mem0 extractor is lossy — it extracts atomic facts and
|
|
211
|
+
The mem0 extractor is lossy — it extracts atomic facts and may drop prose that doesn't look fact-shaped to it. Observed 2026-05-07 (on the graph-era server): 2 of 5 `memory_add` calls returned `"results": []`. 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
|
-
**
|
|
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(
|
|
10
|
-
2. `datapack_list(
|
|
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="<this conversation's 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
|
|
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 |
|
|
36
|
-
|
|
37
|
-
| `knowledgebase_search(
|
|
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 |
|
|
42
|
-
|
|
43
|
-
| `memory_add(
|
|
44
|
-
| `memory_search(
|
|
45
|
-
| `memory_get_by_id(
|
|
46
|
-
| `memory_get_all(
|
|
47
|
-
| `memory_update(
|
|
48
|
-
| `memory_delete_by_id(
|
|
49
|
-
| `memory_delete_all(
|
|
50
|
-
| `
|
|
43
|
+
| Tool | Purpose |
|
|
44
|
+
|------|---------|
|
|
45
|
+
| `memory_add(text, agent_id, conversation_id, user_id=None, app_id=None, 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="", user_id=None, 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, user_id=None, app_id=None, run_id=None, datapack_id=None)` | List all memories for agent |
|
|
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, user_id=None, app_id=None, 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 |
|
|
55
|
-
|
|
56
|
-
| `conversation_create(
|
|
57
|
-
| `conversation_add_message(
|
|
58
|
-
| `conversation_get(
|
|
59
|
-
| `conversation_list(
|
|
60
|
-
| `conversation_update(
|
|
61
|
-
| `conversation_delete(
|
|
56
|
+
| Tool | Purpose |
|
|
57
|
+
|------|---------|
|
|
58
|
+
| `conversation_create(agent_id, user_id=None, app_id=None, 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, trace_id="")` | 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 conversations for agent within a datapack. `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 |
|
|
66
|
-
|
|
67
|
-
| `datapack_create(
|
|
68
|
-
| `datapack_list(
|
|
69
|
-
| `datapack_describe(
|
|
70
|
-
| `datapack_update(
|
|
71
|
-
| `datapack_delete(
|
|
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)` | Rename a datapack and/or update its description. Pass at least one of `name` / `description`. |
|
|
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 |
|
|
78
|
-
|
|
79
|
-
| `artifact_put(
|
|
80
|
-
| `artifact_get(
|
|
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 |
|
|
85
|
-
|
|
86
|
-
| `track_token_usage(
|
|
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
|
-
|
|
107
|
+
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`).
|
|
@@ -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="claude_desktop",
|
|
22
21
|
conversation_id="<uuid from conversation_create>",
|
|
@@ -24,7 +23,7 @@ knowledgebase_search(
|
|
|
24
23
|
limit=10)
|
|
25
24
|
```
|
|
26
25
|
|
|
27
|
-
Required: `
|
|
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.** For Claude Desktop the default is `claude_desktop`;
|
|
10
|
+
4. **Use your session's `agent_id` consistently on writes.** For Claude Desktop the default is `claude_desktop`; use `meko_agent` when writing cross-project facts. Other clients use `<client>:<repo-basename>` (e.g. `claude_code:meko-mcp-server`). Memory reads and `conversation_list` span all of this user's agents regardless of the value passed; `conversation_get` requires the creating agent's exact value.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -26,20 +26,6 @@ These patterns are extracted from real agent sessions. Follow them to avoid wast
|
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
-
## Scope parameter errors
|
|
30
|
-
|
|
31
|
-
**Error:** `Insufficient scope: 'all'. This tool requires 'read' or higher.`
|
|
32
|
-
|
|
33
|
-
The only valid scope values are: `"read"`, `"write"`, `"admin"`.
|
|
34
|
-
|
|
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
|
|
38
|
-
|
|
39
|
-
**Never use:** `"all"`, `"readwrite"`, `"rw"`, or any other value.
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
29
|
## agent_id errors
|
|
44
30
|
|
|
45
31
|
On the Cloud multi-tenant schema, `agent_id` is a TEXT column value — not a PostgreSQL identifier — so arbitrary strings are accepted.
|
|
@@ -54,7 +40,7 @@ Fix: always pass a concrete non-empty `agent_id`. For Claude Desktop use `agent_
|
|
|
54
40
|
|
|
55
41
|
### "My reads return fewer results than I expected"
|
|
56
42
|
|
|
57
|
-
`memory_search` and `memory_get_all`
|
|
43
|
+
`memory_search` and `memory_get_all` do not filter by `agent_id`: one call returns this user's memories across every agent. If results seem thin, check the `datapack_id`, confirm the write landed, and remember that mem0 extraction can be lossy. `conversation_get` is agent-owned and requires the exact creating `agent_id`; `conversation_list` is not agent-filtered.
|
|
58
44
|
|
|
59
45
|
### "I'm seeing rows tagged with legacy agent_id values"
|
|
60
46
|
|
|
@@ -66,7 +52,7 @@ Pre-existing data may be tagged `"agent"`, `"claude_code"`, `"cursor:<slug>"`, o
|
|
|
66
52
|
|
|
67
53
|
| Tool | Safe to retry? | Why |
|
|
68
54
|
|------|---------------|-----|
|
|
69
|
-
| All
|
|
55
|
+
| All read-only tools | Yes | Reads are idempotent |
|
|
70
56
|
| `memory_add` | Yes (once) | Mem0 has dedup logic |
|
|
71
57
|
| `memory_update` | Yes (once) | Overwrites same ID |
|
|
72
58
|
| `memory_delete_by_id` | Yes | Deleting already-deleted is a no-op |
|