@yugabytedb/meko-mcp 1.2.0 → 2.0.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 (44) hide show
  1. package/README.md +28 -19
  2. package/assets/hooks-handlers/before-submit-prompt.sh +2 -2
  3. package/assets/hooks-handlers/lib/capture.js +209 -144
  4. package/assets/hooks-handlers/lib/checkpoint-timer.js +23 -11
  5. package/assets/hooks-handlers/session-start.sh +6 -2
  6. package/assets/scripts/meko-statusline.sh +119 -0
  7. package/assets/scripts/package-desktop-skill.sh +27 -24
  8. package/assets/skills/meko-mcp-tools/SKILL.md +42 -50
  9. package/assets/skills/meko-mcp-tools/references/_sections.md +1 -1
  10. package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +75 -150
  11. package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +30 -36
  12. package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +6 -22
  13. package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +9 -8
  14. package/assets/skills/meko-mcp-tools/references/tools-overview.md +35 -42
  15. package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +28 -92
  16. package/assets/skills/meko-mcp-tools/references/tools-scope-permissions.md +5 -9
  17. package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +1 -81
  18. package/assets/skills/meko-mcp-tools-desktop/SKILL.md +100 -107
  19. package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +41 -11
  20. package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +1 -1
  21. package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +71 -150
  22. package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +12 -18
  23. package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +12 -15
  24. package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +35 -42
  25. package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +28 -92
  26. package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +5 -9
  27. package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +1 -81
  28. package/assets/skills/meko-select-datapack-desktop/SKILL.md +198 -0
  29. package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +121 -0
  30. package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +66 -0
  31. package/bin/create-meko-setup.mjs +29 -0
  32. package/lib/clients/claude-code.mjs +133 -6
  33. package/lib/clients/claude-desktop.mjs +37 -12
  34. package/lib/clients/codex.mjs +306 -5
  35. package/lib/clients/common.mjs +41 -1
  36. package/lib/clients/cursor.mjs +75 -19
  37. package/lib/config.mjs +26 -0
  38. package/lib/installer.mjs +24 -0
  39. package/lib/mcp-transport.mjs +5 -0
  40. package/lib/validate.mjs +0 -1
  41. package/package.json +1 -1
  42. package/scripts/bundle-assets.mjs +8 -0
  43. package/assets/skills/meko-mcp-tools/references/tools-db-query-patterns.md +0 -38
  44. package/assets/skills/meko-mcp-tools/references/tools-write-guardrails.md +0 -30
@@ -1,19 +1,16 @@
1
1
  # Knowledge Base / RAG workflow
2
2
 
3
- As of 2026-05-07, **knowledge-base ingestion on cloud Meko is a UI-only activity**. The MCP ingestion tools exist but are gated off on cloud and rely on an S3-only worker that has no mechanism to read user-supplied buckets. `knowledgebase_search` is the only KB tool agents can rely on today.
4
-
5
- This page tells you which tool to reach for, what's usable where, and where the current gaps are tracked.
3
+ `knowledgebase_search` is the only KB tool exposed via MCP. Index
4
+ creation and ingestion happen out of band — through the **Meko UI**
5
+ (`cloud.mekodata.ai`).
6
6
 
7
7
  ## Decision table
8
8
 
9
9
  | Situation | Use this | Why |
10
10
  |---|---|---|
11
- | Search an existing KB | `knowledgebase_search` | The one KB tool that works on cloud Free Tier and prod today |
12
- | Add documents to a KB on cloud | **Meko UI** — Datapack → Actions menu → **Add Knowledge** | File-upload dialog (PDF/TXT/MD/JSON/MP4, 5MB each, 10/batch). The MCP ingestion path is not usable on cloud |
13
- | Create a new vector index (self-hosted / paid) | `knowledgebase_trigger_index_creation` | Only works when the ingestion worker has IAM access to the S3 bucket you pass |
14
- | Add a source to an existing index (self-hosted / paid) | `knowledgebase_add_source_to_index` | Same caveat as above |
15
- | Poll a build (self-hosted / paid) | `knowledgebase_check_index_status` | Gated on cloud Free Tier |
16
- | Anything that needs KB lifecycle management (list, delete, rename) | **UI** | No MCP tools for KB lifecycle |
11
+ | Search an existing KB | `knowledgebase_search` | The one KB tool exposed via MCP |
12
+ | Add documents to a KB | **Meko UI** — Datapack → Actions → **Add Knowledge** | File-upload dialog (PDF/TXT/MD/JSON/MP4, 5 MB each, 10/batch). No MCP equivalent |
13
+ | Anything else (list / delete / rename / re-index) | **UI** | No MCP tools for KB lifecycle |
17
14
 
18
15
  ## knowledgebase_search — the one you'll actually call
19
16
 
@@ -27,60 +24,16 @@ knowledgebase_search(
27
24
  limit=10)
28
25
  ```
29
26
 
30
- Required: `scope`, `query`, `agent_id`, `conversation_id`, `datapack_id`. Unlike the memory tools, `datapack_id` has no default here — you must pass it explicitly. `agent_id` is **ignored for filtering** on this tool (KB results are team-shared on the datapack), but you still pass it — your session's value is fine.
31
-
32
- Response shape (verified 2026-05-07 against prod):
33
- ```
34
- {"results": [], "count": 0}
35
- ```
36
-
37
- On a populated KB each result carries chunk content and similarity scores. On an empty or nonexistent KB you get an empty array and `count: 0` — not an error.
38
-
39
- ## MCP ingestion tools — what actually happens when you call them
40
-
41
- This applies to `knowledgebase_trigger_index_creation` and `knowledgebase_add_source_to_index`. Understanding the two-layer architecture is the only way to predict whether a call will succeed.
42
-
43
- **MCP layer (`src/tools.py`):** takes `source_uris`, splits on `,`, passes each string to `dist_rag.create_source(<uri>)` with **zero validation**. No scheme check, no reachability probe. Returns `"build_triggered"` immediately.
44
-
45
- **Worker layer (`yugabyte-db/python/ai/rag_agent/rag_pipeline/aws_source_processor.py`):** only handles `s3://` URIs. Any other scheme raises `ValueError("Invalid source URI")` inside the worker — **async, never surfaced to the MCP caller**. The S3 crawler is instantiated with no credentials; it relies on boto3's default chain (env vars, IAM role, `~/.aws/credentials` on the worker host). So the worker can only read buckets its own AWS identity has access to.
46
-
47
- **Implication:** pointing the MCP tool at a user-owned S3 bucket from cloud will return `"build_triggered"` and silently stall at INIT forever. There is no way for the user to supply AWS credentials through the MCP tool.
48
-
49
- ## Free-Tier cloud gating (verified 2026-05-07 against `https://mcp.mekodata.ai/mcp`)
50
-
51
- | Tool | Verbatim response |
52
- |---|---|
53
- | `knowledgebase_trigger_index_creation` | `{"error": "not_available", "detail": "Vector index creation is not available on the free tier."}` |
54
- | `knowledgebase_add_source_to_index` | Same as above |
55
- | `knowledgebase_check_index_status` | `{"error": "not_available", "detail": "Index status check is not available on the free tier."}` |
56
- | `knowledgebase_search` | Works: `{"results": [], "count": 0}` |
57
-
58
- `{"error": "not_available"}` is a server-side tier block. Don't retry.
59
-
60
- ## UI ingestion (cloud Meko) — what the user sees
61
-
62
- From `cloud.mekodata.ai`:
63
-
64
- 1. Open a datapack (`/datapacks/<name>`).
65
- 2. Click **Actions** → **Add Knowledge**.
66
- 3. Drag-and-drop or browse for files. Supported: PDF, TXT, MD, JSON, MP4. Per-file limit 5 MB. Up to 10 files per batch.
67
- 4. The UI handles upload, storage, and index build. The user does not pick a bucket or provide credentials.
68
- 5. Search results from `knowledgebase_search` become available once indexing completes.
69
-
70
- If a user asks you "how do I add a document to my knowledge base?", point them at this flow. Do not suggest `knowledgebase_trigger_index_creation` with an S3 URI — on cloud that path does not work.
27
+ Required: `scope`, `query`, `agent_id`, `conversation_id`, `datapack_id`.
28
+ Unlike the memory tools, `datapack_id` has no default here — you must
29
+ pass it explicitly. `agent_id` is **ignored for filtering** on this
30
+ tool (KB results are team-shared on the datapack), but you still pass
31
+ it — your session's value is fine.
71
32
 
72
- ## Tracked gaps
73
-
74
- - **Upstream GitHub**: [yugabyte/meko-mcp-server#89](https://github.com/yugabyte/meko-mcp-server/issues/89) — disable or gate KB ingestion tools on cloud until the S3 path works end-to-end; return a clearer error than "not_available"; validate schemes at MCP-call time.
75
- - **Upstream Jira**: MEKO-96 (same issue, cross-linked).
76
-
77
- When either lands, the free-tier-gate responses above will change and this page should be revisited.
78
-
79
- ## Response-shape reference (for agents that need to parse)
80
-
81
- ### knowledgebase_search
33
+ ## Response shape
82
34
 
83
35
  Populated KB:
36
+
84
37
  ```
85
38
  {
86
39
  "results": [
@@ -97,38 +50,21 @@ Populated KB:
97
50
  }
98
51
  ```
99
52
 
100
- Empty or nonexistent KB: `{"results": [], "count": 0}` (no error).
101
-
102
- ### knowledgebase_trigger_index_creation (when not gated)
103
-
104
- Success shape:
105
- ```
106
- {
107
- "source_ids": ["<uuid>", ...],
108
- "index_id": "<uuid>",
109
- "index_name": "<name>",
110
- "status": "build_triggered",
111
- "message": "N source(s) registered, index '...' initialised, and build triggered."
112
- }
113
- ```
114
-
115
- Do NOT retry this on failure. Each call creates a new index.
116
-
117
- ### knowledgebase_add_source_to_index (when not gated)
118
-
119
- The historical `r_index_name` vs `r_index_id` parameter-mismatch bug is fixed on main (`src/tools.py` resolves `index_name → index_id` via `SELECT id FROM dist_rag.vector_indexes WHERE index_name = %s` before calling the dist_rag function). If `index_name` doesn't exist you get `{"error": "index_not_found", "detail": "No vector index found with name '...'"}`.
120
-
121
- Param is `source_uri` (URI string like `s3://bucket/path/`), **not** `source_id` as older docs may claim.
53
+ Empty or nonexistent KB: `{"results": [], "count": 0}` (no error,
54
+ just an empty array).
122
55
 
123
- ### knowledgebase_check_index_status (when not gated)
56
+ ## UI ingestion — what the user sees
124
57
 
125
- Returns `pipeline_details` (per-document status) and `pipeline_stats` (aggregated). Check `completion_rate_percent` in `pipeline_stats` and `document_status` in `pipeline_details`.
126
-
127
- Stuck at INIT for >5 minutes under self-hosted usually means the worker couldn't reach the bucket — check AWS credentials on the worker host, not the MCP server.
128
-
129
- ## Prerequisites (self-hosted)
58
+ From `cloud.mekodata.ai`:
130
59
 
131
- - `dist_rag` PostgreSQL extension enabled on the target YugabyteDB database
132
- - Ingestion workers running (`yugabyte-db/python/ai/rag_agent/`)
133
- - AWS credentials on the worker host for any S3 bucket you intend to ingest from
134
- - `datapack_id` passed on every call when targeting a non-default datapack
60
+ 1. Open a datapack (`/datapacks/<name>`).
61
+ 2. Click **Actions** → **Add Knowledge**.
62
+ 3. Drag-and-drop or browse for files. Supported: PDF, TXT, MD, JSON,
63
+ MP4. Per-file limit 5 MB. Up to 10 files per batch.
64
+ 4. The UI handles upload, storage, and index build. The user does not
65
+ pick a bucket or provide credentials.
66
+ 5. Search results from `knowledgebase_search` become available once
67
+ indexing completes.
68
+
69
+ If a user asks you "how do I add a document to my knowledge base?",
70
+ point them at this flow.
@@ -1,6 +1,6 @@
1
1
  # Scope Permission Hierarchy and Per-Tool Requirements
2
2
 
3
- **WRONG:** `db_run_write_query(scope="read", ...)` → PermissionError: requires 'write' or higher.
3
+ **WRONG:** `memory_add(scope="read", ...)` → PermissionError: requires 'write' or higher.
4
4
  **WRONG:** `memory_delete_all(scope="write", ...)` → PermissionError: requires 'admin'.
5
5
 
6
6
  ## Scope Hierarchy
@@ -17,13 +17,11 @@ Higher scopes include lower scopes. An unrecognized scope string always fails.
17
17
 
18
18
  ## Per-Tool Requirements
19
19
 
20
- ### read scope (11 tools)
20
+ ### read scope (9 tools)
21
21
 
22
22
  | Tool | Group |
23
23
  |------|-------|
24
- | `db_summarize_database` | Database |
25
- | `db_run_read_only_query` | Database |
26
- | `knowledgebase_check_index_status` | RAG |
24
+ | `knowledgebase_search` | RAG |
27
25
  | `memory_search` | Memory |
28
26
  | `memory_get_by_id` | Memory |
29
27
  | `memory_get_all` | Memory |
@@ -33,13 +31,10 @@ Higher scopes include lower scopes. An unrecognized scope string always fails.
33
31
  | `datapack_list` | Datapack |
34
32
  | `datapack_describe` | Datapack |
35
33
 
36
- ### write scope (11 tools)
34
+ ### write scope (9 tools)
37
35
 
38
36
  | Tool | Group |
39
37
  |------|-------|
40
- | `db_run_write_query` | Database |
41
- | `knowledgebase_trigger_index_creation` | RAG |
42
- | `knowledgebase_add_source_to_index` | RAG |
43
38
  | `memory_add` | Memory |
44
39
  | `memory_update` | Memory |
45
40
  | `memory_delete_by_id` | Memory |
@@ -48,6 +43,7 @@ Higher scopes include lower scopes. An unrecognized scope string always fails.
48
43
  | `conversation_update` | Conversation |
49
44
  | `datapack_create` | Datapack |
50
45
  | `datapack_update` | Datapack |
46
+ | `track_token_usage` | Observability |
51
47
 
52
48
  ### admin scope (3 tools — destructive, irreversible)
53
49
 
@@ -13,7 +13,7 @@ These patterns are extracted from real agent sessions. Follow them to avoid wast
13
13
 
14
14
  ## Connection errors: "connection already closed"
15
15
 
16
- **Affects:** Memory tools (`memory_add`, `memory_search`, `memory_get_all`) most frequently. DB tools less often.
16
+ **Affects:** Memory tools (`memory_add`, `memory_search`, `memory_get_all`) most frequently.
17
17
 
18
18
  **Why it happens:** The Python memory subsystem uses a connection cache that doesn't recover from dropped connections (unlike the Go API server which has retry logic with 5 retries and 3-second delays).
19
19
 
@@ -21,73 +21,11 @@ These patterns are extracted from real agent sessions. Follow them to avoid wast
21
21
 
22
22
  1. Retry the same call **once**.
23
23
  2. If it fails again, the memory subsystem is unhealthy for this session — stop trying memory calls and tell the user. Do not waste tokens re-sending the same payload.
24
- 3. Direct-SQL fallback **does not apply** on cloud Free Tier (`db_run_read_only_query` returns `{"error": "not_available", "detail": "Free Tier: DB Tools access is not enabled. coming soon."}`). On self-hosted / paid deployments where DB tools are available, inspect memory rows via the multi-tenant schema — tables are namespaced by datapack, not by agent_id. The old `{agent_id}_mem0_collection` per-agent-table layout is gone.
25
24
 
26
25
  **Anti-pattern from real sessions:** Agent called `memory_add` with 500+ tokens of text, got "connection already closed", retried the identical call 3 more times (each 500+ tokens). Wasted ~2000 tokens on guaranteed failures.
27
26
 
28
27
  ---
29
28
 
30
- ## RAG pipeline stuck at INIT
31
-
32
- **Affects:** `knowledgebase_trigger_index_creation` — the build triggers but never completes.
33
-
34
- **Symptoms:**
35
- - `knowledgebase_check_index_status` returns empty `pipeline_details` and `pipeline_stats`
36
- - Documents remain at `INIT` status indefinitely
37
- - Work queue tasks are `IN_PROGRESS` with `lease_expires_at` in the past
38
-
39
- **Diagnosis:** Query the work queue directly:
40
- ```
41
- db_run_read_only_query(scope="read",
42
- query="SELECT task_type, status, lease_expires_at FROM dist_rag.work_queue WHERE index_name = 'your_index' ORDER BY created_at DESC LIMIT 10")
43
- ```
44
-
45
- Look for tasks stuck `IN_PROGRESS` with expired leases — these indicate crashed or hung workers.
46
-
47
- **What to do:**
48
-
49
- 1. Poll status **at most 3 times** with 1-2 minute intervals
50
- 2. If no progress after 3 checks, **stop polling** — the pipeline is stuck
51
- 3. There is **no way to reset or retry stuck tasks via MCP tools**
52
- 4. Creating a new index with a different name (e.g., `_v2`) is a workaround, but if the workers themselves are down, this will also get stuck
53
- 5. Recommend the user check worker logs or restart the pipeline service
54
-
55
- **Anti-pattern from real sessions:** Agent created indexes `v1`, `v2`, `v3` — all stuck with the same symptoms. Even making the S3 bucket public didn't help. The issue was worker infrastructure, not permissions.
56
-
57
- ---
58
-
59
- ## db_run_write_query guardrail error
60
-
61
- **Error:** `'dict' object has no attribute 'guardrail_config'`
62
-
63
- **Affects:** `db_run_write_query` in some deployments.
64
-
65
- **What to do:**
66
-
67
- 1. Try the call once. If it fails with this specific error, write operations are unavailable in this deployment.
68
- 2. For reads, always use `db_run_read_only_query` (never `db_run_write_query` for SELECT).
69
- 3. Report the issue — it's a server-side bug, not a parameter problem.
70
-
71
- ---
72
-
73
- ## knowledgebase_add_source_to_index failure
74
-
75
- **Error shape:** `{"error": "not_available", "detail": "Vector index creation is not available on the free tier."}`
76
-
77
- You're on cloud Free Tier; the ingestion tools are gated off. There is no retry that will succeed. Point the user at the Meko UI's **Add Knowledge** file-upload flow (Datapack → Actions → Add Knowledge) instead.
78
-
79
- **Error shape:** `{"error": "index_not_found", "detail": "No vector index found with name '...'"}`
80
-
81
- The `index_name` doesn't exist in this datapack's `dist_rag.vector_indexes` table. Either you're targeting the wrong datapack (check `datapack_id`), or the index hasn't been created yet — call `knowledgebase_trigger_index_creation` first.
82
-
83
- **Error shape:** tool returns `"build_triggered"` but `knowledgebase_check_index_status` shows documents stuck at `INIT` for >5 minutes.
84
-
85
- Most common cause on self-hosted deployments: the worker can't reach the S3 bucket. `S3BucketCrawler()` uses boto3's default credential chain — check `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / IAM role / `~/.aws/credentials` on the worker host, not on the MCP server. Second cause: the URI isn't `s3://`-prefixed (`aws_source_processor.py` rejects other schemes with `ValueError("Invalid source URI")` async).
86
-
87
- **Historical note:** the old `r_index_name` vs `r_index_id` parameter-mismatch bug is fixed on main — the MCP tool now resolves `index_name → index_id` internally via a `SELECT id FROM dist_rag.vector_indexes WHERE index_name = %s` step. Older docs referencing this bug are stale.
88
-
89
- ---
90
-
91
29
  ## Scope parameter errors
92
30
 
93
31
  **Error:** `Insufficient scope: 'all'. This tool requires 'read' or higher.`
@@ -124,21 +62,6 @@ Pre-existing data may be tagged `"agent"`, `"claude_code"`, `"cursor:<slug>"`, o
124
62
 
125
63
  ---
126
64
 
127
- ## Permission denied on data tables
128
-
129
- **Error:** `permission denied for table customer_complaints`
130
-
131
- **Why:** The MCP database user may not have read access to all tables, particularly data tables created by the provisioning system within a datapack.
132
-
133
- **What to do:**
134
-
135
- 1. This is a server-side configuration issue — not fixable via MCP tools.
136
- 2. Don't retry. Don't try different query formulations.
137
- 3. Use `db_summarize_database` to see which tables you CAN access.
138
- 4. Report the permission gap to the Meko admin.
139
-
140
- ---
141
-
142
65
  ## Safe-to-retry vs not
143
66
 
144
67
  | Tool | Safe to retry? | Why |
@@ -147,9 +70,6 @@ Pre-existing data may be tagged `"agent"`, `"claude_code"`, `"cursor:<slug>"`, o
147
70
  | `memory_add` | Yes (once) | Mem0 has dedup logic |
148
71
  | `memory_update` | Yes (once) | Overwrites same ID |
149
72
  | `memory_delete_by_id` | Yes | Deleting already-deleted is a no-op |
150
- | `db_run_write_query` (INSERT) | **No** | May double-insert rows |
151
- | `db_run_write_query` (CREATE TABLE) | **No** | "Table already exists" error |
152
- | `knowledgebase_trigger_index_creation` | **No** | Creates duplicate indexes |
153
73
  | `conversation_create` | **No** | Creates duplicate conversations |
154
74
  | `conversation_add_message` with `seed` | Yes | Seed-based dedup prevents duplicates |
155
75
  | `conversation_add_message` without `seed` | **No** | Creates duplicate messages |
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: meko-select-datapack-desktop
3
+ description: >-
4
+ Lists the user's Meko datapacks, shows authorization, supports search and
5
+ pagination, and pins one as the active datapack on Claude Desktop. Triggers
6
+ when the user says things like "switch datapack", "use datapack <name>",
7
+ "list my datapacks", "which datapack am I on", "pin <name>", or any request
8
+ to change or inspect the active datapack. Persistence is via memory_add
9
+ (Desktop has no SessionStart hook), so the pin survives across sessions
10
+ until the user clears it.
11
+ license: MIT
12
+ metadata:
13
+ author: Meko
14
+ version: "1.1.0"
15
+ tags: meko, datapack, selection, project, claude-desktop, desktop
16
+ ---
17
+
18
+ # meko-select-datapack-desktop — Pin a datapack for Claude Desktop
19
+
20
+ Claude Desktop has no SessionStart hook, so the Claude Code variant's sidecar/hook design doesn't apply here. Instead, the pin is stored as a **memory** with a structured tag, scoped to `agent_id="claude_desktop"` (Desktop's project bucket), and you re-read it on every Desktop turn before issuing Meko MCP tool calls. The pin outlives a single Desktop conversation — it sticks until the user explicitly clears it. Same project-scoped semantic as the Claude Code variant; different mechanism.
21
+
22
+ ## How persistence works (Desktop)
23
+
24
+ A single memory under `agent_id="claude_desktop"` carries the pin:
25
+
26
+ ```
27
+ text: "meko_active_datapack=<uuid> name=<name> selected_at=<iso8601>"
28
+ metadata: { "type": "active-datapack-pin" }
29
+ ```
30
+
31
+ You discover it by calling `memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)` at the start of any turn that's about to make a Meko MCP write. If you find a pin, pass `datapack_id="<uuid>"` to every Meko call that accepts it. If you don't find one, fall back to the server default (omit the parameter).
32
+
33
+ ## When to invoke this skill
34
+
35
+ Trigger immediately on any of these signals:
36
+
37
+ - User says "switch datapack", "change datapack", "use datapack X", "select datapack", "pick a datapack".
38
+ - User says "list my datapacks", "what datapacks do I have", "show me my datapacks".
39
+ - User says "which datapack am I on", "which datapack is active", "what's pinned".
40
+ - User says "clear datapack", "unpin datapack", "stop using <name>".
41
+ - The first Meko MCP write of a turn AND `memory_search` for `meko_active_datapack` returned no result AND the user has more than one datapack — proactively offer to pin one.
42
+
43
+ ## On every Meko-touching turn (not just when this skill triggers)
44
+
45
+ Before issuing a Meko MCP write tool call (memory_add, db_run_write_query, conversation_add_message, etc.), if you don't already know the active pin from this turn's earlier work:
46
+
47
+ ```
48
+ memory_search(scope="read",
49
+ query="meko_active_datapack",
50
+ agent_id="claude_desktop",
51
+ conversation_id="<this conversation's id>",
52
+ limit=1)
53
+ ```
54
+
55
+ Parse the result. If a pin row is present, extract the UUID from the text body (after `meko_active_datapack=`) and pass it as `datapack_id` to every Meko MCP call this turn. If not, omit `datapack_id` and let the server resolve the default.
56
+
57
+ This is once per turn, not once per call. Cache within the turn.
58
+
59
+ ## The flow
60
+
61
+ ### 1. List
62
+
63
+ Use a **temporary** conversation for browsing only. Call
64
+ `conversation_create(scope="write", agent_id="claude_desktop", title="Claude
65
+ Desktop datapack selection")` and use its returned ID for the read-only
66
+ `datapack_list` below and for the pin `memory_add`/`memory_update` in step 4.
67
+
68
+ **Do not reuse this conversation for later capture.** It lives in whatever
69
+ datapack was active when it was created (typically the default), and a
70
+ conversation cannot move between datapacks. Once a datapack is pinned, capture
71
+ must flow through a *new* conversation created in the selected datapack — see
72
+ step 5.
73
+
74
+ ```
75
+ datapack_list(scope="read", conversation_id="<this conversation's id>")
76
+ ```
77
+
78
+ The response is an array; the fields you'll use are `datapack_id`,
79
+ `datapack_name`, `created_at`, and `account_id`.
80
+
81
+ If the response has zero entries, tell the user: *"You don't have any datapacks yet. Run `datapack_create(scope='admin', name='<name>')` to make one, or visit the Meko Cloud console."* Stop here.
82
+
83
+ If the response has exactly **one** entry, **auto-select it**. Print: *"Only one datapack: `<name>`. Auto-selecting. Run `meko-select-datapack-desktop` again with `clear` to unset."* Skip the table; jump to step 4 (persist) and step 5 (confirm).
84
+
85
+ ### 2. Render the table
86
+
87
+ For 2+ datapacks, render a numbered table. **Page size 10.** Default sort: `created_at DESC` (newest first).
88
+
89
+ ```
90
+ Your datapacks (showing 1-10 of 14):
91
+
92
+ # Name Role Created Status Active?
93
+ 1 meko-local-setup Owner 2026-04-30 ready ← (pinned)
94
+ 2 meko_default_datapack Owner 2026-04-30 ready
95
+ 3 prod-research Owner 2026-04-12 ready
96
+ …
97
+
98
+ Reply with: a number (1-10) to pin · a substring to search · `next` / `prev` to page
99
+ · `clear` to unset · `cancel` to leave the current pin alone
100
+
101
+ Note: Role is hardcoded to "Owner" today. Shared / Viewer / Contributor /
102
+ Maintainer roles are pending upstream backend work — see references/role-display-future.md.
103
+ ```
104
+
105
+ Mark the currently-pinned row with `← (pinned)` based on the `memory_search` result (if any).
106
+
107
+ ### 3. Parse the user's reply
108
+
109
+ - **Integer in range** → that row is the choice.
110
+ - **Substring** → re-render filtered. If it narrows to one row, that's the choice.
111
+ - **`next` / `prev`** → re-render next/previous page.
112
+ - **`cancel`** → leave any existing pin in place; print *"Cancelled. Active datapack unchanged."* Stop.
113
+ - **`clear` / `none` / `unpin`** → see step 4's clear path.
114
+ - **Unrecognized** → re-prompt with the legend.
115
+
116
+ ### 4. Persist
117
+
118
+ **On first pin (no existing pin memory):**
119
+
120
+ ```
121
+ memory_add(scope="write",
122
+ text="meko_active_datapack=<uuid> name=<name> selected_at=<iso8601>",
123
+ agent_id="claude_desktop",
124
+ conversation_id="<this conversation's id>",
125
+ metadata='{"type":"active-datapack-pin"}')
126
+ ```
127
+
128
+ Verify the write with `memory_search` (the standard Meko verify-after-write rule from `meko-mcp-tools-desktop`). Capture the returned memory `id`.
129
+
130
+ **On switch (existing pin memory found):**
131
+
132
+ ```
133
+ memory_update(scope="write",
134
+ memory_id="<existing pin id>",
135
+ text="meko_active_datapack=<new uuid> name=<new name> selected_at=<iso8601>",
136
+ agent_id="claude_desktop",
137
+ conversation_id="<this conversation's id>")
138
+ ```
139
+
140
+ Always update — never `memory_add` a second pin. Two pin memories will produce ambiguous reads.
141
+
142
+ **On clear:**
143
+
144
+ ```
145
+ memory_delete_by_id(scope="write",
146
+ memory_id="<existing pin id>",
147
+ agent_id="claude_desktop",
148
+ conversation_id="<this conversation's id>")
149
+ ```
150
+
151
+ If `memory_search` returned no pin, there's nothing to delete — print *"No pin was set; nothing to clear."* and stop.
152
+
153
+ **Conversation transition after a clear (same rule as a switch).** The pin memory is now gone, but the session's current conversation still physically lives in the datapack that was pinned when it was created — a conversation cannot move to the default. You must pick exactly one of these; do not mix them:
154
+
155
+ - **Option A — keep capturing to the current conversation this session.** It stays in its original (now-unpinned) datapack, so you must keep passing that **same old `datapack_id`** on its `conversation_add_message` calls — do NOT switch it to the default (that would fail). The clear only affects *new* sessions. Disclose: *"Pin cleared. This chat keeps saving to `<old datapack name>`; new chats will use the default datapack."*
156
+ - **Option B — start default-datapack capture immediately.** Create a **new** conversation with no `datapack_id` (`conversation_create(scope="write", agent_id="claude_desktop")`) and post all subsequent turns there. Stop posting to the old conversation. Disclose: *"Pin cleared. Started a new chat context saving to the default datapack."*
157
+
158
+ Default to Option A (least disruptive) unless the user wants the switch to take effect right now. Either way, never send `datapack_id=default` to the pre-existing pinned conversation.
159
+
160
+ ### 5. Confirm
161
+
162
+ ```
163
+ Pinned datapack `<name>` (`<uuid>`) for Claude Desktop.
164
+
165
+ This is stored as a memory under agent_id="claude_desktop". It will persist
166
+ across sessions until you run this skill again with `clear` (or `switch` to
167
+ a different datapack).
168
+
169
+ I'll read this pin via `memory_search` before each Meko MCP write so the
170
+ right `datapack_id` flows through automatically.
171
+ ```
172
+
173
+ **Then start a fresh conversation for capture in the selected datapack.** The
174
+ temporary selection conversation from step 1 belongs to the datapack that was
175
+ active when it was created and cannot be moved. For subsequent capture this
176
+ session, call `conversation_create(scope="write", agent_id="claude_desktop",
177
+ datapack_id="<the newly pinned uuid>", title="<topic>")` and post turns
178
+ (`conversation_add_message`) to *that* conversation with the same
179
+ `datapack_id`. Do not keep posting to the selection conversation — doing so
180
+ would split capture across the old and new datapacks or fail outright.
181
+
182
+ ## Edge cases
183
+
184
+ - **Multiple pin memories somehow exist** — the user (or a buggy earlier run) created duplicates. Use the most recent (highest `selected_at`); offer to delete the others.
185
+ - **The pinned datapack no longer exists on the server** — the next Meko MCP call will fail with a not-found error. Re-run the skill to pick a new one or `clear` the stale pin.
186
+ - **memory_search times out or fails** — fall back to the server default (omit `datapack_id`) and tell the user *"Couldn't read the active-datapack pin. Using server default for this call."* Don't guess a UUID.
187
+ - **User explicitly overrides for a single call** — pass the user's `datapack_id` for that one call. Do NOT update the pin memory.
188
+
189
+ ## Critical: do NOT use other persistence mechanisms
190
+
191
+ Don't write to local files, don't use environment variables, don't ask the user to paste the UUID into every prompt. A single tagged memory is the entire mechanism. Anything else creates drift between what you think is pinned and what `memory_search` will report.
192
+
193
+ ## Reference sections
194
+
195
+ | File | What it covers |
196
+ |------|---------------|
197
+ | `references/cookbook.md` | Sample list / search / select / clear transcripts on Desktop |
198
+ | `references/troubleshooting.md` | Pin not recalled, duplicate pins, stale UUID recovery |
@@ -0,0 +1,121 @@
1
+ # Cookbook — meko-select-datapack-desktop
2
+
3
+ Concrete transcripts of the four flows the skill supports on Claude Desktop.
4
+
5
+ ## Flow 1 — First pin (no existing pin memory)
6
+
7
+ **User:** `pin prod-research as my active datapack`
8
+
9
+ **Skill:**
10
+ ```
11
+ > memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
12
+ [empty]
13
+
14
+ > datapack_list(scope="read")
15
+ [14 entries returned, filtering by "prod-research"]
16
+
17
+ One match: `prod-research`. Pinning.
18
+
19
+ > memory_add(scope="write",
20
+ text="meko_active_datapack=8b1c…f4a2 name=prod-research selected_at=2026-05-25T18:42:00Z",
21
+ agent_id="claude_desktop",
22
+ conversation_id="<conv-id>",
23
+ metadata='{"type":"active-datapack-pin"}')
24
+ [returns id=mem_abc123]
25
+
26
+ > memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
27
+ [verifies the row exists]
28
+
29
+ Pinned datapack `prod-research` (`8b1c…f4a2`) for Claude Desktop.
30
+
31
+ Stored as a memory under agent_id="claude_desktop"; persists across sessions
32
+ until you run this skill again with `clear` or `switch`.
33
+
34
+ I'll read this pin via memory_search before each Meko MCP write so the right
35
+ datapack_id flows through automatically.
36
+ ```
37
+
38
+ ## Flow 2 — Switch (pin memory already exists)
39
+
40
+ **User:** `switch to the q2-roadmap-deck datapack`
41
+
42
+ **Skill:**
43
+ ```
44
+ > memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
45
+ [returns id=mem_abc123, text="meko_active_datapack=8b1c…f4a2 name=prod-research …"]
46
+
47
+ > datapack_list(scope="read")
48
+ [14 entries returned, filtering by "q2-roadmap-deck"]
49
+
50
+ One match: `q2-roadmap-deck`. Switching pin.
51
+
52
+ > memory_update(scope="write",
53
+ memory_id="mem_abc123",
54
+ text="meko_active_datapack=4c2e…a991 name=q2-roadmap-deck selected_at=2026-05-25T19:05:00Z",
55
+ agent_id="claude_desktop",
56
+ conversation_id="<conv-id>")
57
+
58
+ Switched pin from `prod-research` to `q2-roadmap-deck` (`4c2e…a991`).
59
+ ```
60
+
61
+ The `memory_update` is critical here — using `memory_add` again would create a duplicate pin memory.
62
+
63
+ ## Flow 3 — Single-datapack auto-select
64
+
65
+ **User:** (first Meko write attempt, no pin yet)
66
+
67
+ **Skill:**
68
+ ```
69
+ > memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
70
+ [empty]
71
+
72
+ > datapack_list(scope="read")
73
+ [1 entry returned]
74
+
75
+ Only one datapack: `meko-local-setup`. Auto-selecting.
76
+
77
+ > memory_add(scope="write",
78
+ text="meko_active_datapack=a17d…cc91 name=meko-local-setup selected_at=…",
79
+ agent_id="claude_desktop",
80
+ conversation_id="<conv-id>",
81
+ metadata='{"type":"active-datapack-pin"}')
82
+
83
+ Pinned datapack `meko-local-setup` (`a17d…cc91`). Run this skill again with
84
+ `clear` to unset.
85
+ ```
86
+
87
+ ## Flow 4 — Clear
88
+
89
+ **User:** `unpin datapack`
90
+
91
+ **Skill:**
92
+ ```
93
+ > memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
94
+ [returns id=mem_abc123]
95
+
96
+ > memory_delete_by_id(scope="write",
97
+ memory_id="mem_abc123",
98
+ agent_id="claude_desktop",
99
+ conversation_id="<conv-id>")
100
+
101
+ Pin cleared. Subsequent Meko MCP calls will use the server default.
102
+ ```
103
+
104
+ If `memory_search` returned empty: *"No pin was set; nothing to clear."*
105
+
106
+ ## Flow 5 — Read-the-pin pattern (every Meko-touching turn)
107
+
108
+ This isn't a user-triggered flow — it's the per-turn lookup the skill teaches you to do silently before any Meko MCP write:
109
+
110
+ ```
111
+ > memory_search(query="meko_active_datapack", agent_id="claude_desktop",
112
+ conversation_id="<conv-id>", limit=1)
113
+ [returns text="meko_active_datapack=8b1c…f4a2 name=prod-research …"]
114
+
115
+ # Extract UUID after "meko_active_datapack=" → 8b1c…f4a2
116
+
117
+ > memory_add(scope="write", text="…", datapack_id="8b1c…f4a2",
118
+ agent_id="claude_desktop", conversation_id="<conv-id>")
119
+ ```
120
+
121
+ Cache the UUID for the rest of the turn — don't call `memory_search` once per Meko tool call.