@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.
- package/README.md +28 -19
- package/assets/hooks-handlers/before-submit-prompt.sh +2 -2
- package/assets/hooks-handlers/lib/capture.js +209 -144
- package/assets/hooks-handlers/lib/checkpoint-timer.js +23 -11
- package/assets/hooks-handlers/session-start.sh +6 -2
- package/assets/scripts/meko-statusline.sh +119 -0
- package/assets/scripts/package-desktop-skill.sh +27 -24
- package/assets/skills/meko-mcp-tools/SKILL.md +42 -50
- package/assets/skills/meko-mcp-tools/references/_sections.md +1 -1
- package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +75 -150
- package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +30 -36
- package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +6 -22
- package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +9 -8
- package/assets/skills/meko-mcp-tools/references/tools-overview.md +35 -42
- package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +28 -92
- package/assets/skills/meko-mcp-tools/references/tools-scope-permissions.md +5 -9
- package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +1 -81
- package/assets/skills/meko-mcp-tools-desktop/SKILL.md +100 -107
- package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +41 -11
- package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +1 -1
- package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +71 -150
- package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +12 -18
- package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +12 -15
- package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +35 -42
- package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +28 -92
- package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +5 -9
- package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +1 -81
- package/assets/skills/meko-select-datapack-desktop/SKILL.md +198 -0
- package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +121 -0
- package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +66 -0
- package/bin/create-meko-setup.mjs +29 -0
- package/lib/clients/claude-code.mjs +133 -6
- package/lib/clients/claude-desktop.mjs +37 -12
- package/lib/clients/codex.mjs +306 -5
- package/lib/clients/common.mjs +41 -1
- package/lib/clients/cursor.mjs +75 -19
- package/lib/config.mjs +26 -0
- package/lib/installer.mjs +24 -0
- package/lib/mcp-transport.mjs +5 -0
- package/lib/validate.mjs +0 -1
- package/package.json +1 -1
- package/scripts/bundle-assets.mjs +8 -0
- package/assets/skills/meko-mcp-tools/references/tools-db-query-patterns.md +0 -38
- 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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
12
|
-
| Add documents to a KB
|
|
13
|
-
|
|
|
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`.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
56
|
+
## UI ingestion — what the user sees
|
|
124
57
|
|
|
125
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
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:** `
|
|
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 (
|
|
20
|
+
### read scope (9 tools)
|
|
21
21
|
|
|
22
22
|
| Tool | Group |
|
|
23
23
|
|------|-------|
|
|
24
|
-
| `
|
|
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 (
|
|
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.
|
|
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.
|