@yugabytedb/meko-mcp 2.0.0 → 2.1.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 (48) hide show
  1. package/README.md +31 -6
  2. package/assets/hooks-handlers/codex-session-end.sh +78 -0
  3. package/assets/hooks-handlers/lib/capture.js +163 -14
  4. package/assets/hooks-handlers/lib/checkpoint-timer.js +44 -2
  5. package/assets/hooks-handlers/session-end.sh +10 -0
  6. package/assets/hooks-handlers/session-start.sh +8 -1
  7. package/assets/hooks-handlers/test_checkpoint_timer.sh +162 -0
  8. package/assets/scripts/package-desktop-skill.sh +32 -1
  9. package/assets/skills/meko-mcp-tools/SKILL.md +47 -41
  10. package/assets/skills/meko-mcp-tools/references/_sections.md +2 -4
  11. package/assets/skills/meko-mcp-tools/references/tools-agent-id-conventions.md +29 -25
  12. package/assets/skills/meko-mcp-tools/references/tools-conversation-dedup.md +5 -7
  13. package/assets/skills/meko-mcp-tools/references/tools-conversation-reasoning.md +50 -0
  14. package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +63 -46
  15. package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +6 -6
  16. package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +4 -4
  17. package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +17 -22
  18. package/assets/skills/meko-mcp-tools/references/tools-overview.md +40 -39
  19. package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +1 -2
  20. package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +4 -18
  21. package/assets/skills/meko-mcp-tools/references/tools-user-id-conventions.md +9 -8
  22. package/assets/skills/meko-mcp-tools-desktop/SKILL.md +49 -42
  23. package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +6 -6
  24. package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +2 -2
  25. package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +28 -24
  26. package/assets/skills/meko-mcp-tools-desktop/references/tools-conversation-reasoning.md +50 -0
  27. package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +61 -36
  28. package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +4 -4
  29. package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +16 -19
  30. package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +40 -39
  31. package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +1 -2
  32. package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +3 -17
  33. package/assets/skills/meko-select-datapack-desktop/SKILL.md +31 -21
  34. package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +8 -8
  35. package/assets/skills/meko-select-datapack-desktop/references/role-display-future.md +41 -0
  36. package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +1 -1
  37. package/bin/create-meko-setup.mjs +45 -6
  38. package/lib/clients/claude-code.mjs +8 -4
  39. package/lib/clients/claude-desktop.mjs +14 -0
  40. package/lib/clients/codex.mjs +23 -8
  41. package/lib/clients/common.mjs +2 -0
  42. package/lib/clients/cursor.mjs +7 -2
  43. package/lib/detect.mjs +7 -2
  44. package/lib/prompt.mjs +1 -1
  45. package/lib/url-selection.mjs +55 -0
  46. package/package.json +1 -1
  47. package/assets/skills/meko-mcp-tools/references/tools-scope-permissions.md +0 -71
  48. package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +0 -71
@@ -1,13 +1,13 @@
1
1
  # Complete Tool Catalog and Decision Tree
2
2
 
3
- **22 tools on Cloud Meko** (verified against `https://mcp.mekodata.ai/mcp`). Grouped: Memory (8), Conversation (6), Knowledge Base (1), Datapack (5), Artifacts (2), Observability (1).
3
+ **23 tools on Cloud Meko** (verified against `https://mcp.mekodata.ai/mcp`). Grouped: Memory (8), Conversation (6), Knowledge Base (1), Datapack (5), Artifacts (2), Observability (1).
4
4
 
5
5
  ## Quick health check before using tools
6
6
 
7
7
  Before starting a session, verify that the Meko MCP tools are working:
8
8
 
9
- 1. `memory_search(scope="read", query="test", agent_id="<your-session-agent-id>", conversation_id="<session-conversation-id>")` — Confirms memory subsystem is up. Use the `agent_id` and `conversation_id` from the SessionStart `additionalContext`. May fail with "connection already closed" — see `tools-troubleshooting.md`.
10
- 2. `datapack_list(scope="read")` — Confirms Meko API connectivity. Returns the datapacks your token has access to.
9
+ 1. `memory_search(query="test", agent_id="<your-session-agent-id>", conversation_id="<session-conversation-id>")` — Confirms memory subsystem is up. Use the `agent_id` and `conversation_id` from the SessionStart `additionalContext`. May fail with "connection already closed" — see `tools-troubleshooting.md`.
10
+ 2. `datapack_list()` — 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
 
@@ -20,6 +20,7 @@ User wants to...
20
20
  │ ├── Store a fact/preference/entity? -----------> memory_add (write)
21
21
  │ ├── Store a full conversation (multi-turn)? ---> conversation_create + conversation_add_message (write)
22
22
  │ ├── Search past knowledge? --------------------> memory_search (read)
23
+ │ ├── Share private memories with the team? -----> memory_promote (write; owner/maintainer only)
23
24
  │ └── Retrieve a past conversation? -------------> conversation_get (read)
24
25
  ├── Persist or retrieve a file?
25
26
  │ ├── Upload a generated file (report, CSV, PDF)? -> artifact_put (write)
@@ -32,58 +33,58 @@ Agent / knowledge-base lifecycle is managed **outside** the MCP surface — typi
32
33
 
33
34
  ## Knowledge Base Tools (1)
34
35
 
35
- | Tool | Scope | Purpose |
36
- |------|-------|---------|
37
- | `knowledgebase_search(scope, query, agent_id, conversation_id, datapack_id, limit=10)` | read | Semantic search across KB chunks. `datapack_id` is REQUIRED (no default). |
36
+ | Tool | Purpose |
37
+ |------|---------|
38
+ | `knowledgebase_search(query, agent_id, conversation_id, datapack_id, limit=10)` | Semantic search across KB chunks. `datapack_id` is REQUIRED (no default). |
38
39
 
39
40
  ## Memory Tools (8)
40
41
 
41
- | Tool | Scope | Purpose |
42
- |------|-------|---------|
43
- | `memory_add(scope, text, agent_id, conversation_id, user_id=None, app_id=None, run_id=None, metadata=None, messages=None, datapack_id=None)` | write | Store fact/preference/entity as long-term memory |
44
- | `memory_search(scope, query, agent_id, conversation_id="", user_id=None, limit=10, datapack_id=None)` | read | Semantic search across memories + graph relations. `conversation_id` optional (omit or pass `""` for cross-conversation discovery). |
45
- | `memory_get_by_id(scope, memory_id, agent_id, conversation_id, datapack_id=None)` | read | Direct pgvector row lookup by UUID. Preferred over `memory_search` for exact-id verification. |
46
- | `memory_get_all(scope, agent_id, conversation_id, user_id=None, app_id=None, run_id=None, datapack_id=None)` | read | List all memories for agent |
47
- | `memory_update(scope, memory_id, text, agent_id, conversation_id, datapack_id=None)` | write | Overwrite memory text |
48
- | `memory_delete_by_id(scope, memory_id, agent_id, conversation_id, datapack_id=None)` | write | Delete a single memory |
49
- | `memory_delete_all(scope, agent_id, conversation_id, user_id=None, app_id=None, run_id=None, datapack_id=None)` | admin | Delete all memories for agent (destructive) |
50
- | `flush_pending_memory_candidates(scope, agent_id)` | read | **Deprecated.** Superseded by automatic extraction from posted turns — post substantive turns with `conversation_add_message` and the server extracts memories from them. Now returns a `deprecated` payload pointing at `conversation_add_message`; kept one release for backward compatibility. |
42
+ | Tool | Purpose |
43
+ |------|---------|
44
+ | `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 |
45
+ | `memory_search(query, agent_id, conversation_id="", user_id=None, limit=10, datapack_id=None)` | Semantic search across memories + graph relations. `conversation_id` optional (omit or pass `""` for cross-conversation discovery). |
46
+ | `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. |
47
+ | `memory_get_all(agent_id, conversation_id, user_id=None, app_id=None, run_id=None, datapack_id=None)` | List all memories for agent |
48
+ | `memory_update(memory_id, text, agent_id, conversation_id, datapack_id=None)` | Overwrite memory text |
49
+ | `memory_delete_by_id(memory_id, agent_id, conversation_id, datapack_id=None)` | Delete a single memory |
50
+ | `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) |
51
+ | `memory_promote(conversation_id, memory_ids, agent_id=None, datapack_id=None)` | Promote private memories into the datapack's shared knowledge base (moves them + graph context out of mem0, then evicts from mem0 — one-way). Owners/maintainers only; viewers/contributors get 403.
51
52
 
52
53
  ## Conversation Tools (6)
53
54
 
54
- | Tool | Scope | Purpose |
55
- |------|-------|---------|
56
- | `conversation_create(scope, agent_id, user_id=None, app_id=None, run_id=None, title=None, metadata=None, session_id="")` | write | Create conversation container (Langfuse session) |
57
- | `conversation_add_message(scope, conversation_id, agent_id, input, output=None, reasoning=None, metadata=None, seed=None, trace_id="")` | write | Add a message turn (Langfuse trace) |
58
- | `conversation_get(scope, conversation_id, agent_id, include_messages=False, limit=100, offset=0)` | read | Retrieve conversation, optionally with messages |
59
- | `conversation_list(scope, agent_id, conversation_id="", user_id=None, limit=20, offset=0)` | read | List conversations for agent. `conversation_id` optional (omit for browse). |
60
- | `conversation_update(scope, conversation_id, agent_id, title=None, metadata=None)` | write | Update title or metadata |
61
- | `conversation_delete(scope, conversation_id, agent_id)` | admin | Delete entire conversation (destructive) |
55
+ | Tool | Purpose |
56
+ |------|---------|
57
+ | `conversation_create(agent_id, user_id=None, app_id=None, run_id=None, title=None, metadata=None, session_id="")` | Create conversation container (Langfuse session) |
58
+ | `conversation_add_message(conversation_id, agent_id, input, output=None, reasoning=None, metadata=None, seed=None, trace_id="")` | Add a message turn (Langfuse trace) |
59
+ | `conversation_get(conversation_id, agent_id, include_messages=False, limit=100, offset=0)` | Retrieve conversation, optionally with messages |
60
+ | `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). |
61
+ | `conversation_update(conversation_id, agent_id, title=None, metadata=None)` | Update title or metadata |
62
+ | `conversation_delete(conversation_id, agent_id)` | Delete entire conversation (destructive) |
62
63
 
63
64
  ## Datapack Management Tools (5)
64
65
 
65
- | Tool | Scope | Purpose |
66
- |------|-------|---------|
67
- | `datapack_create(scope, name)` | write | Create new datapack |
68
- | `datapack_list(scope)` | read | List all datapacks |
69
- | `datapack_describe(scope, datapack_id, include_status=False)` | read | Describe datapack by UUID |
70
- | `datapack_update(scope, datapack_id, connection_string)` | write | Update connection string |
71
- | `datapack_delete(scope, datapack_id)` | admin | Delete datapack by UUID (irreversible) |
66
+ | Tool | Purpose |
67
+ |------|---------|
68
+ | `datapack_create(name)` | Create new datapack |
69
+ | `datapack_list()` | List all datapacks |
70
+ | `datapack_describe(datapack_id, include_status=False)` | Describe datapack by UUID |
71
+ | `datapack_update(datapack_id, name=None, description=None)` | Rename a datapack and/or update its description. Pass at least one of `name` / `description`. |
72
+ | `datapack_delete(datapack_id)` | Delete datapack by UUID (irreversible) |
72
73
 
73
74
  ## Artifact Tools (2)
74
75
 
75
76
  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
77
 
77
- | Tool | Scope | Purpose |
78
- |------|-------|---------|
79
- | `artifact_put(scope, filename, content_base64, content_type, conversation_id, datapack_id=None, agent_id=None)` | write | Upload a file to the datapack. Returns `{artifact_id, content_hash, filename, size_bytes, stored_in}`. Max 5 MiB (configurable via `MEKO_MAX_ARTIFACT_UPLOAD_BYTES`). |
80
- | `artifact_get(scope, content_hash, conversation_id, datapack_id=None, agent_id=None)` | read | Retrieve a file by SHA-256 hash. Small files return `content_base64` inline; large files (S3) are written to `~/.meko/artifacts/<hash>/<filename>` and `local_path` is returned. |
78
+ | Tool | Purpose |
79
+ |------|---------|
80
+ | `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`). |
81
+ | `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
82
 
82
83
  ## Observability Tools (1)
83
84
 
84
- | Tool | Scope | Purpose |
85
- |------|-------|---------|
86
- | `track_token_usage(scope, conversation_id, name, input_tokens=0, output_tokens=0, total_tokens=None, model=None, message_id=None, datapack_id=None)` | write | Record an LLM-cost GENERATION observation on the conversation's trace. Primarily called by first-party Meko services (e.g. inference_gateway) that know their LLM's exact token counts. Most end-agents (Cursor, Claude Desktop) don't have those counts at the MCP call layer, so this tool is rarely useful for third-party agents. |
85
+ | Tool | Purpose |
86
+ |------|---------|
87
+ | `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
88
 
88
89
  ## Platform capabilities NOT exposed via MCP
89
90
 
@@ -102,4 +103,4 @@ For knowledge-base content on Cloud, the canonical path is **UI upload**: Datapa
102
103
 
103
104
  ## Common Parameter Patterns
104
105
 
105
- Every tool accepts `scope` as the first parameter. Memory and KB tools accept optional `datapack_id` to target a specific datapack. Memory and conversation tools require `agent_id` for namespace isolation (see `tools-agent-id-conventions.md`).
106
+ 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: `scope`, `query`, `agent_id`, `conversation_id`, `datapack_id`.
26
+ Required: `query`, `agent_id`, `conversation_id`, `datapack_id`.
28
27
  Unlike the memory tools, `datapack_id` has no default here — you must
29
28
  pass it explicitly. `agent_id` is **ignored for filtering** on this
30
29
  tool (KB results are team-shared on the datapack), but you still pass
@@ -7,7 +7,7 @@ These patterns are extracted from real agent sessions. Follow them to avoid wast
7
7
  1. **Never retry the identical failed call more than once.** If it fails twice with the same error, it's not transient — diagnose the cause.
8
8
  2. **Distinguish transient vs persistent failures.** "Connection already closed" is transient (retry once). "Permission denied" is persistent (stop, don't retry).
9
9
  3. **Don't guess parameters sequentially.** If a tool fails with one parameter format, don't try 4 variations. Check this skill's docs for the correct format first.
10
- 4. **Use your session's `agent_id` consistently.** For Claude Desktop the default is `claude_desktop`; for cross-project common facts use `meko_agent`. Other clients use `<client>:<repo-basename>` (e.g. `claude_code:meko-mcp-server`). Use `agent_id="meko_agent"` deliberately on `memory_search`/`memory_get_all` only when you want the common bucket — empty string also resolves to `meko_agent`, not a cross-agent fan-out. Don't switch between forms mid-session.
10
+ 4. **Use your session's `agent_id` consistently on writes.** 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` filter strictly on your passed `agent_id`. A memory written by `claude-code:<slug-A>` is invisible to a search with `agent_id="cursor:<slug-B>"`. If you need a broader view, call repeatedly across the agent_ids you want to cover, or point the user at the UI's Memory Summary page (which shows every row regardless of agent_id).
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 `read` scope tools | Yes | Reads are idempotent |
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 |
@@ -3,15 +3,15 @@ name: meko-select-datapack-desktop
3
3
  description: >-
4
4
  Lists the user's Meko datapacks, shows authorization, supports search and
5
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
6
+ when the user says things like "switch datapack", "use the Acme datapack",
7
+ "list my datapacks", "which datapack am I on", "pin Acme", or any request
8
8
  to change or inspect the active datapack. Persistence is via memory_add
9
9
  (Desktop has no SessionStart hook), so the pin survives across sessions
10
10
  until the user clears it.
11
11
  license: MIT
12
12
  metadata:
13
13
  author: Meko
14
- version: "1.1.0"
14
+ version: "1.2.0"
15
15
  tags: meko, datapack, selection, project, claude-desktop, desktop
16
16
  ---
17
17
 
@@ -42,10 +42,10 @@ Trigger immediately on any of these signals:
42
42
 
43
43
  ## On every Meko-touching turn (not just when this skill triggers)
44
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:
45
+ Before issuing a Meko MCP write tool call (memory_add, conversation_add_message, artifact_put, etc.), if you don't already know the active pin from this turn's earlier work:
46
46
 
47
47
  ```
48
- memory_search(scope="read",
48
+ memory_search(
49
49
  query="meko_active_datapack",
50
50
  agent_id="claude_desktop",
51
51
  conversation_id="<this conversation's id>",
@@ -61,7 +61,7 @@ This is once per turn, not once per call. Cache within the turn.
61
61
  ### 1. List
62
62
 
63
63
  Use a **temporary** conversation for browsing only. Call
64
- `conversation_create(scope="write", agent_id="claude_desktop", title="Claude
64
+ `conversation_create(agent_id="claude_desktop", title="Claude
65
65
  Desktop datapack selection")` and use its returned ID for the read-only
66
66
  `datapack_list` below and for the pin `memory_add`/`memory_update` in step 4.
67
67
 
@@ -72,13 +72,20 @@ must flow through a *new* conversation created in the selected datapack — see
72
72
  step 5.
73
73
 
74
74
  ```
75
- datapack_list(scope="read", conversation_id="<this conversation's id>")
75
+ datapack_list(conversation_id="<this conversation's id>")
76
76
  ```
77
77
 
78
78
  The response is an array; the fields you'll use are `datapack_id`,
79
- `datapack_name`, `created_at`, and `account_id`.
79
+ `datapack_name`, `created_at`, `account_id`, and `grant`.
80
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.
81
+ `grant` is the caller's actual authorization on the datapack — one of
82
+ `"owner"`, `"maintainer"`, `"contributor"`, `"viewer"`. Display it verbatim in
83
+ the Role column. Do not hardcode "Owner", do not invent or normalize the value
84
+ (render `"maintainer"` as `maintainer`, not `Maintainer`). The deployed
85
+ response is the source of truth — see `references/role-display-future.md` for
86
+ the taxonomy.
87
+
88
+ If the response has zero entries, tell the user: *"You don't have any datapacks yet. Run `datapack_create(name='<name>')` to make one, or visit the Meko Cloud console."* Stop here.
82
89
 
83
90
  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
91
 
@@ -89,19 +96,22 @@ For 2+ datapacks, render a numbered table. **Page size 10.** Default sort: `crea
89
96
  ```
90
97
  Your datapacks (showing 1-10 of 14):
91
98
 
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
99
+ # Name Role Created Status Active?
100
+ 1 meko-local-setup owner 2026-04-30 ready ← (pinned)
101
+ 2 meko_default_datapack owner 2026-04-30 ready
102
+ 3 team-onboarding maintainer 2026-04-08 ready
103
+ 4 q2-roadmap viewer 2026-03-22 ready
96
104
  …
97
105
 
98
106
  Reply with: a number (1-10) to pin · a substring to search · `next` / `prev` to page
99
107
  · `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
108
  ```
104
109
 
110
+ The **Role** column shows each datapack's `grant` value verbatim (`owner`,
111
+ `maintainer`, `contributor`, `viewer`). Never hardcode "Owner" — shared
112
+ datapacks legitimately carry other grants, and misreporting them (e.g. showing
113
+ a `maintainer` grant as "Owner") misleads the user about their own access.
114
+
105
115
  Mark the currently-pinned row with `← (pinned)` based on the `memory_search` result (if any).
106
116
 
107
117
  ### 3. Parse the user's reply
@@ -118,7 +128,7 @@ Mark the currently-pinned row with `← (pinned)` based on the `memory_search`
118
128
  **On first pin (no existing pin memory):**
119
129
 
120
130
  ```
121
- memory_add(scope="write",
131
+ memory_add(
122
132
  text="meko_active_datapack=<uuid> name=<name> selected_at=<iso8601>",
123
133
  agent_id="claude_desktop",
124
134
  conversation_id="<this conversation's id>",
@@ -130,7 +140,7 @@ Verify the write with `memory_search` (the standard Meko verify-after-write rule
130
140
  **On switch (existing pin memory found):**
131
141
 
132
142
  ```
133
- memory_update(scope="write",
143
+ memory_update(
134
144
  memory_id="<existing pin id>",
135
145
  text="meko_active_datapack=<new uuid> name=<new name> selected_at=<iso8601>",
136
146
  agent_id="claude_desktop",
@@ -142,7 +152,7 @@ Always update — never `memory_add` a second pin. Two pin memories will produce
142
152
  **On clear:**
143
153
 
144
154
  ```
145
- memory_delete_by_id(scope="write",
155
+ memory_delete_by_id(
146
156
  memory_id="<existing pin id>",
147
157
  agent_id="claude_desktop",
148
158
  conversation_id="<this conversation's id>")
@@ -153,7 +163,7 @@ If `memory_search` returned no pin, there's nothing to delete — print *"No pin
153
163
  **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
164
 
155
165
  - **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."*
166
+ - **Option B — start default-datapack capture immediately.** Create a **new** conversation with no `datapack_id` (`conversation_create(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
167
 
158
168
  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
169
 
@@ -173,7 +183,7 @@ right `datapack_id` flows through automatically.
173
183
  **Then start a fresh conversation for capture in the selected datapack.** The
174
184
  temporary selection conversation from step 1 belongs to the datapack that was
175
185
  active when it was created and cannot be moved. For subsequent capture this
176
- session, call `conversation_create(scope="write", agent_id="claude_desktop",
186
+ session, call `conversation_create(agent_id="claude_desktop",
177
187
  datapack_id="<the newly pinned uuid>", title="<topic>")` and post turns
178
188
  (`conversation_add_message`) to *that* conversation with the same
179
189
  `datapack_id`. Do not keep posting to the selection conversation — doing so
@@ -11,12 +11,12 @@ Concrete transcripts of the four flows the skill supports on Claude Desktop.
11
11
  > memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
12
12
  [empty]
13
13
 
14
- > datapack_list(scope="read")
14
+ > datapack_list()
15
15
  [14 entries returned, filtering by "prod-research"]
16
16
 
17
17
  One match: `prod-research`. Pinning.
18
18
 
19
- > memory_add(scope="write",
19
+ > memory_add(
20
20
  text="meko_active_datapack=8b1c…f4a2 name=prod-research selected_at=2026-05-25T18:42:00Z",
21
21
  agent_id="claude_desktop",
22
22
  conversation_id="<conv-id>",
@@ -44,12 +44,12 @@ datapack_id flows through automatically.
44
44
  > memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
45
45
  [returns id=mem_abc123, text="meko_active_datapack=8b1c…f4a2 name=prod-research …"]
46
46
 
47
- > datapack_list(scope="read")
47
+ > datapack_list()
48
48
  [14 entries returned, filtering by "q2-roadmap-deck"]
49
49
 
50
50
  One match: `q2-roadmap-deck`. Switching pin.
51
51
 
52
- > memory_update(scope="write",
52
+ > memory_update(
53
53
  memory_id="mem_abc123",
54
54
  text="meko_active_datapack=4c2e…a991 name=q2-roadmap-deck selected_at=2026-05-25T19:05:00Z",
55
55
  agent_id="claude_desktop",
@@ -69,12 +69,12 @@ The `memory_update` is critical here — using `memory_add` again would create a
69
69
  > memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
70
70
  [empty]
71
71
 
72
- > datapack_list(scope="read")
72
+ > datapack_list()
73
73
  [1 entry returned]
74
74
 
75
75
  Only one datapack: `meko-local-setup`. Auto-selecting.
76
76
 
77
- > memory_add(scope="write",
77
+ > memory_add(
78
78
  text="meko_active_datapack=a17d…cc91 name=meko-local-setup selected_at=…",
79
79
  agent_id="claude_desktop",
80
80
  conversation_id="<conv-id>",
@@ -93,7 +93,7 @@ Pinned datapack `meko-local-setup` (`a17d…cc91`). Run this skill again with
93
93
  > memory_search(query="meko_active_datapack", agent_id="claude_desktop", limit=1)
94
94
  [returns id=mem_abc123]
95
95
 
96
- > memory_delete_by_id(scope="write",
96
+ > memory_delete_by_id(
97
97
  memory_id="mem_abc123",
98
98
  agent_id="claude_desktop",
99
99
  conversation_id="<conv-id>")
@@ -114,7 +114,7 @@ This isn't a user-triggered flow — it's the per-turn lookup the skill teaches
114
114
 
115
115
  # Extract UUID after "meko_active_datapack=" → 8b1c…f4a2
116
116
 
117
- > memory_add(scope="write", text="…", datapack_id="8b1c…f4a2",
117
+ > memory_add(text="…", datapack_id="8b1c…f4a2",
118
118
  agent_id="claude_desktop", conversation_id="<conv-id>")
119
119
  ```
120
120
 
@@ -0,0 +1,41 @@
1
+ # Role display — current state
2
+
3
+ ## Today: render `grant` verbatim
4
+
5
+ The deployed Meko `GET /datapacks` response includes a `grant` field on every datapack. Possible values (per maintainer guidance, 2026-05-26):
6
+
7
+ - `"owner"` — datapacks the caller created.
8
+ - `"maintainer"` — write access to a shared datapack including memories AND collective learnings/knowledge.
9
+ - `"contributor"` — write access to a shared datapack: can generate own memories. (Cannot promote / publish collective learnings — that's the maintainer's job.)
10
+ - `"viewer"` — read-only on a shared datapack.
11
+
12
+ Use the field directly in the **Role** column:
13
+
14
+ ```
15
+ # Name Role Created Active?
16
+ 1 prod-research owner 2026-04-12 ← (pinned)
17
+ 2 team-onboarding contributor 2026-04-08
18
+ 3 q2-roadmap viewer 2026-03-22
19
+ ```
20
+
21
+ Do not invent values, do not normalize the casing, do not display `"Owner"` if the field says `"owner"`. The deployed schema is the source of truth.
22
+
23
+ Note that the upstream Go source (the `Datapack` struct in `api_server/internal/models/models.go` of the `meko` API-server repo) does NOT declare `grant` — the deployed server enriches the response beyond what the public Go code lists. Don't rely on the public struct for the wire schema; trust the actual response.
24
+
25
+ ## Other fields the live response includes
26
+
27
+ Beyond the columns the skill renders by default, `datapack_list` returns counts that may be useful in some flows:
28
+
29
+ - `memory_count` — total memories the caller has under this datapack (per `(user_id, agent_id)` scoping).
30
+ - `knowledge_count` — knowledge-base entries on the datapack.
31
+ - `learnings_count` — promoted-to-shared memories on the datapack.
32
+
33
+ These aren't in the spec column set. Don't add them silently. If the user asks for counts, render them on request — and read the numbers from the live response, never invent them.
34
+
35
+ ## Sharing UI vs API state
36
+
37
+ The Cloud console "Share <datapack>" page (`meko_ui/src/features/datapacks/pages/DatapackSharePage.tsx`) shows interactive controls with "Coming Soon" overlays on a few specific actions (e.g. invite-by-email link sharing, transfer-ownership). Those overlays mean *those specific UI affordances* aren't shipped — NOT that the underlying `grant`/sharing model is absent. Contributor/maintainer/viewer grants are functional in the data layer today; only the user-facing self-serve invite flow is gated.
38
+
39
+ ## Why the skill ships without Mine / Shared / All filter tabs
40
+
41
+ The tagged pin memory (re-read via `memory_search` before each Meko write) delivers the user's actual ask ("don't make me remember a UUID"). Filter tabs are useful when the response is genuinely mixed-grant; for users with only owner-grant rows they're decorative. We add tabs when there's evidence users with mixed-grant responses want them.
@@ -40,7 +40,7 @@ The user has zero datapacks. Print:
40
40
  ```
41
41
  You don't have any datapacks yet. Create one with:
42
42
 
43
- datapack_create(scope="admin", name="<your-datapack-name>")
43
+ datapack_create(name="<your-datapack-name>")
44
44
 
45
45
  Or visit the Meko Cloud console (Datapacks → New datapack).
46
46
  ```
@@ -7,9 +7,14 @@ import { dirname, join } from 'node:path';
7
7
 
8
8
  import { install, uninstall } from '../lib/installer.mjs';
9
9
  import { askChoice, askMultiChoice, askText, askSecret, askYesNo, close as closePrompts } from '../lib/prompt.mjs';
10
- import { banner, error, dim, info } from '../lib/logger.mjs';
11
- import { isPackagedInstall, selfInvocation } from '../lib/clients/common.mjs';
10
+ import { banner, error, dim, info, warn } from '../lib/logger.mjs';
11
+ import {
12
+ isPackagedInstall,
13
+ MEKO_PRODUCTION_CONSOLE_URL,
14
+ selfInvocation,
15
+ } from '../lib/clients/common.mjs';
12
16
  import { getClientAdapters } from '../lib/clients/index.mjs';
17
+ import { resolveSuggestedMcpUrl } from '../lib/url-selection.mjs';
13
18
  import { parseClientList } from '../lib/clients/aliases.mjs';
14
19
  import { startMigration, runChildMigration, readStatus, stopMigration } from '../lib/migrate.mjs';
15
20
  import { checkLatest } from '../lib/version-check.mjs';
@@ -81,7 +86,7 @@ Examples:
81
86
  create-meko-setup
82
87
 
83
88
  # Cloud Meko for Claude Code (non-interactive):
84
- create-meko-setup --url https://your-mcp.mekodev.com/mcp --api-key "$MEKO_API_KEY" --yes
89
+ create-meko-setup --url https://mcp.mekodata.ai/mcp --api-key "$MEKO_API_KEY" --yes
85
90
 
86
91
  # Local Meko with Cursor config:
87
92
  create-meko-setup --client cursor --local --scope project --yes
@@ -104,7 +109,7 @@ Options:
104
109
  Comma-separated, or "all" to target every supported client (default: claude-code)
105
110
  codex covers BOTH the OpenAI Codex CLI and the Codex Desktop App
106
111
  (they share ~/.codex/config.toml).
107
- --url <url> MCP server URL (from app.mekodev.com > Datapacks > Connect)
112
+ --url <url> MCP server URL (from cloud.mekodata.ai > Datapacks > Connect)
108
113
  --api-key <key> API key for Cloud Meko authentication
109
114
  --local Use local Meko at http://localhost:8000/mcp
110
115
  --scope <scope> Config scope: user or project (default: user)
@@ -465,6 +470,28 @@ async function main() {
465
470
  }
466
471
  }
467
472
 
473
+ let suggestedUrl = null;
474
+ if (!url && !flags.local) {
475
+ suggestedUrl = resolveSuggestedMcpUrl({
476
+ adapters: getClientAdapters(),
477
+ clients,
478
+ name: flags.name,
479
+ scope: flags.scope,
480
+ projectRoot,
481
+ });
482
+ if (flags.yes) {
483
+ if (suggestedUrl.source === 'conflict') {
484
+ const conflicts = suggestedUrl.existingUrls.map((existingUrl) => `\n - ${existingUrl}`).join('');
485
+ error(
486
+ `Selected clients have different existing Meko URLs:${conflicts}\n` +
487
+ 'Pass --url explicitly to choose which endpoint to install.',
488
+ );
489
+ process.exit(1);
490
+ }
491
+ url = suggestedUrl.url;
492
+ }
493
+ }
494
+
468
495
  // Interactive mode: URL not provided and not auto-accepting defaults
469
496
  if (!url && !flags.yes) {
470
497
  banner('Meko Setup');
@@ -475,7 +502,7 @@ async function main() {
475
502
  // When only Cloud is available, skip the prompt entirely — asking to
476
503
  // pick between one option is pointless UX.
477
504
  const connectionOptions = [
478
- { label: 'Cloud Meko', description: 'Hosted at mekodev.com (requires URL + API key)' },
505
+ { label: 'Cloud Meko', description: 'Hosted at mekodata.ai (requires an API key)' },
479
506
  ];
480
507
  if (!isPackagedInstall()) {
481
508
  connectionOptions.push({
@@ -490,7 +517,19 @@ async function main() {
490
517
 
491
518
  if (connectionType === 'Cloud Meko') {
492
519
  // Step 2 — MCP URL
493
- url = await askText('MCP server URL (from app.mekodev.com > Datapacks > Connect):');
520
+ if (suggestedUrl?.source === 'existing') {
521
+ dim(`Using the existing Meko endpoint as the default: ${suggestedUrl.url}`);
522
+ } else if (suggestedUrl?.source === 'conflict') {
523
+ const conflicts = suggestedUrl.existingUrls.map((existingUrl) => `\n - ${existingUrl}`).join('');
524
+ warn(
525
+ `Selected clients use different Meko endpoints:${conflicts}\n` +
526
+ 'Enter the endpoint to use for this install.',
527
+ );
528
+ }
529
+ url = await askText(
530
+ `MCP server URL (from ${MEKO_PRODUCTION_CONSOLE_URL} > Datapacks > Connect):`,
531
+ suggestedUrl?.url ?? undefined,
532
+ );
494
533
 
495
534
  // Step 3 — API key. Normalize: terminal paste can drag in trailing
496
535
  // whitespace/CR or accidental surrounding quotes, all of which would
@@ -288,13 +288,17 @@ export function createClaudeCodeAdapter(options = {}) {
288
288
  // leaving the marketplace.json missing. Full clone is small
289
289
  // (~few MB, mostly markdown) and is the only way that reliably
290
290
  // succeeds today.
291
- // Marketplace source is normally the upstream GitHub repo. A dev/e2e
291
+ // Marketplace source is normally the public generated skills repo. Use
292
+ // an explicit HTTPS URL: GitHub's owner/repo shorthand makes Claude Code
293
+ // clone over SSH, which fails for users without GitHub SSH credentials.
294
+ // A dev/e2e
292
295
  // override lets a test install the plugin from a LOCAL working-tree
293
296
  // checkout (a directory path) so unreleased skill changes can be
294
- // exercised end-to-end before they land on `yugabyte/meko-mcp-server`.
295
- // Default is unchanged; only set MEKO_PLUGIN_MARKETPLACE_SOURCE in tests.
297
+ // exercised end-to-end before they are published to `yugabyte/meko-skills`.
298
+ // Only set MEKO_PLUGIN_MARKETPLACE_SOURCE in tests.
296
299
  const marketplaceSource =
297
- process.env.MEKO_PLUGIN_MARKETPLACE_SOURCE || "yugabyte/meko-mcp-server";
300
+ process.env.MEKO_PLUGIN_MARKETPLACE_SOURCE
301
+ || "https://github.com/yugabyte/meko-skills.git";
298
302
  const marketplaceCmd =
299
303
  `claude plugin marketplace add ${shellQuote(marketplaceSource)} --scope ${shellQuote(scope)}`;
300
304
  // `claude plugin list` dropped --scope in CLI 2.1.x; it prints all
@@ -99,9 +99,11 @@ export function createClaudeDesktopAdapter(options = {}) {
99
99
  const fromMcpb =
100
100
  /\bClaude\b[^ ]*\bExtensions\b/i.test(commandJoined) ||
101
101
  /mcp-remote-launcher\.js/.test(commandJoined);
102
+ const url = getMcpRemoteEntryUrl(entry);
102
103
  return {
103
104
  found: true,
104
105
  status: fromMcpb ? "Configured (via .mcpb extension)" : "Configured",
106
+ ...(url ? { url } : {}),
105
107
  };
106
108
  },
107
109
  removeRegistration(name) {
@@ -300,6 +302,18 @@ export function buildMcpRemoteEntry({ url, apiKey }) {
300
302
  return entry;
301
303
  }
302
304
 
305
+ /** Read the remote URL from installer-managed or MCPB Claude Desktop entries. */
306
+ export function getMcpRemoteEntryUrl(entry) {
307
+ if (typeof entry?.env?.MEKO_MCP_URL === "string") {
308
+ return entry.env.MEKO_MCP_URL;
309
+ }
310
+ if (typeof entry?.url === "string") return entry.url;
311
+ if (!Array.isArray(entry?.args)) return undefined;
312
+ const remoteIndex = entry.args.indexOf("mcp-remote");
313
+ const candidate = remoteIndex >= 0 ? entry.args[remoteIndex + 1] : undefined;
314
+ return typeof candidate === "string" ? candidate : undefined;
315
+ }
316
+
303
317
  function defaultRunPackageScript(scriptPath) {
304
318
  if (!statSync(scriptPath).isFile()) {
305
319
  throw new Error(`Not a file: ${scriptPath}`);