@yugabytedb/meko-mcp 2.1.0 → 2.3.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 (70) hide show
  1. package/README.md +233 -40
  2. package/assets/hooks-handlers/codex-session-end.sh +4 -69
  3. package/assets/hooks-handlers/cursor-session-start.sh +19 -1
  4. package/assets/hooks-handlers/lib/capture-cleanup-cli.js +1473 -0
  5. package/assets/hooks-handlers/lib/capture-notices.js +438 -0
  6. package/assets/hooks-handlers/lib/capture-state.js +2173 -0
  7. package/assets/hooks-handlers/lib/capture-status-cli.js +86 -0
  8. package/assets/hooks-handlers/lib/capture.js +2782 -403
  9. package/assets/hooks-handlers/lib/checkpoint-timer.js +485 -81
  10. package/assets/hooks-handlers/lib/journal-bridge.js +218 -0
  11. package/assets/hooks-handlers/lib/kiro-handler.js +760 -0
  12. package/assets/hooks-handlers/lib/opencode-handler.js +620 -0
  13. package/assets/hooks-handlers/lib/opencode-normalizer.js +145 -0
  14. package/assets/hooks-handlers/lib/terminal-handoff.js +427 -0
  15. package/assets/hooks-handlers/session-end.sh +3 -34
  16. package/assets/opencode-plugin/meko.js +213 -0
  17. package/assets/scripts/meko-statusline.sh +123 -16
  18. package/assets/skills/meko-mcp-tools/SKILL.md +116 -126
  19. package/assets/skills/meko-mcp-tools/references/_sections.md +3 -3
  20. package/assets/skills/meko-mcp-tools/references/tools-agent-id-conventions.md +25 -18
  21. package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +99 -24
  22. package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +1 -1
  23. package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +53 -6
  24. package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +25 -79
  25. package/assets/skills/meko-mcp-tools/references/tools-overview.md +37 -26
  26. package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +14 -8
  27. package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +66 -1
  28. package/assets/skills/meko-mcp-tools/references/tools-user-id-conventions.md +7 -40
  29. package/assets/skills/meko-mcp-tools-desktop/SKILL.md +56 -20
  30. package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +1 -1
  31. package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +1 -1
  32. package/assets/skills/meko-mcp-tools-desktop/references/tools-agent-id-conventions.md +15 -8
  33. package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +99 -36
  34. package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +22 -5
  35. package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +18 -14
  36. package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +39 -28
  37. package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +14 -8
  38. package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +67 -1
  39. package/assets/skills/meko-select-datapack-desktop/SKILL.md +4 -4
  40. package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +8 -8
  41. package/assets/skills/meko-select-datapack-desktop/references/role-display-future.md +7 -5
  42. package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +4 -4
  43. package/bin/create-meko-setup.mjs +113 -12
  44. package/lib/capture-cleanup.mjs +63 -0
  45. package/lib/capture-status.mjs +67 -0
  46. package/lib/clients/aliases.mjs +5 -0
  47. package/lib/clients/claude-code.mjs +1 -0
  48. package/lib/clients/claude-desktop.mjs +120 -5
  49. package/lib/clients/codex.mjs +85 -37
  50. package/lib/clients/common.mjs +7 -0
  51. package/lib/clients/index.mjs +4 -0
  52. package/lib/clients/kiro.mjs +705 -0
  53. package/lib/clients/opencode.mjs +532 -0
  54. package/lib/config.mjs +158 -1
  55. package/lib/installer.mjs +18 -1
  56. package/lib/migrate/adapters/claude-code.mjs +11 -2
  57. package/lib/migrate/adapters/codex.mjs +497 -0
  58. package/lib/migrate/adapters/memory-md.mjs +27 -8
  59. package/lib/migrate/adapters/opencode.mjs +148 -0
  60. package/lib/migrate/claude-parser.mjs +153 -9
  61. package/lib/migrate/id.mjs +217 -10
  62. package/lib/migrate/mcp-client.mjs +25 -1
  63. package/lib/migrate/opencode-normalizer.mjs +132 -0
  64. package/lib/migrate/orchestrator.mjs +299 -56
  65. package/lib/migrate/progress.mjs +239 -5
  66. package/lib/migrate/registry.mjs +72 -0
  67. package/lib/migrate.mjs +100 -10
  68. package/lib/prompt.mjs +16 -1
  69. package/package.json +4 -4
  70. package/scripts/bundle-assets.mjs +4 -0
package/README.md CHANGED
@@ -1,12 +1,34 @@
1
1
  # Meko Setup Installer
2
2
 
3
- A zero-dependency Node.js CLI that configures [Meko MCP Server](https://mekodata.netlify.app/) for Claude Code, Claude Desktop, Cursor, and OpenAI Codex (CLI + Desktop App) in one command. It registers MCP server settings, configures hook/runtime environment where supported, installs skills support, and validates the setup.
3
+ A zero-dependency Node.js CLI that configures [Meko MCP Server](https://mekodata.netlify.app/) for Claude Code, Claude Desktop, Cursor, OpenAI Codex, Kiro, and OpenCode in one command. It registers MCP server settings, configures hook/runtime environment where supported, installs skills support, and validates the setup.
4
4
 
5
5
  Reduces manual setup to a single command.
6
6
 
7
+ ## What this installer authenticates with
8
+
9
+ The installer configures **API-key authentication only**. Every supported
10
+ client sends `Authorization: Bearer <your-api-key>` on the wire, plus the
11
+ `User-Agent` the Cloud WAF requires. Most adapters store the header value in
12
+ client config. Codex stores `bearer_token_env_var = "MEKO_API_KEY"` and resolves
13
+ the key from its runtime environment; Claude Desktop passes the same variable
14
+ through `mcp-remote` header interpolation.
15
+
16
+ Cloud Meko also accepts a browser **OAuth** flow, but that path is out of scope
17
+ here: you turn it on inside a client's own UI, and it uses no API key. Don't
18
+ configure both against the same client — see [Two authentication
19
+ modes](../README.md#two-authentication-modes--pick-one-dont-mix-them) for the
20
+ comparison and the failure modes, including why OAuth with a loopback callback
21
+ still fails against the DEV environment.
22
+
23
+ **Note:** The server accepts the key only as `Authorization: Bearer …` on the
24
+ wire. An `API_KEY` or `MEKO_API_KEY` environment variable does nothing unless
25
+ the client config references it. The installer creates that reference for
26
+ Codex and Claude Desktop; installer-managed capture hooks also read
27
+ `MEKO_API_KEY` when they send data directly.
28
+
7
29
  ## Prerequisites
8
30
 
9
- - **Node.js 18 or newer** on your PATH. Get it from [nodejs.org](https://nodejs.org/) or via a version manager (`nvm`, `fnm`, `asdf`). `npx`, which comes bundled with Node, is how you invoke the installer.
31
+ - **Node.js 20.18.1 or newer** on your PATH. Get it from [nodejs.org](https://nodejs.org/) or via a version manager (`nvm`, `fnm`, `asdf`). `npx`, which comes bundled with Node, is how you invoke the installer. (The Claude Desktop `mcp-remote` bridge depends on `undici@^7`, whose every release declares `engines.node >= 20.18.1`; the installer enforces that exact floor so the bridge can start.)
10
32
 
11
33
  ## Quick Start
12
34
 
@@ -40,6 +62,16 @@ npx @yugabytedb/meko-mcp --local --yes
40
62
  npx @yugabytedb/meko-mcp --client cursor --local --scope project --yes
41
63
  ```
42
64
 
65
+ **OpenCode CLI/TUI 1.18.x install:**
66
+
67
+ ```bash
68
+ npx @yugabytedb/meko-mcp --client opencode --scope user --yes
69
+ # Project-scoped plugin, runtime, and skill:
70
+ npx @yugabytedb/meko-mcp --client opencode --scope project --yes
71
+ ```
72
+
73
+ Restart OpenCode after either install.
74
+
43
75
  **Claude Desktop install:**
44
76
 
45
77
  Claude Desktop has two install paths — pick based on whether you want the agent skill (the behavioral guide that teaches the agent *when* to call Meko tools — how to capture turns via per-turn `conversation_add_message` posting so the server can extract memories, and when the narrow explicit `memory_add` cases apply):
@@ -62,7 +94,7 @@ You can also combine both: install via `.mcpb` for the MCP wiring, then run Opti
62
94
  4. Quit and relaunch Claude Desktop.
63
95
  5. Verify in chat: *"Is Claude configured with an MCP Server for Meko?"*
64
96
 
65
- Prerequisite: Node 18+ on `PATH` (the bundle launches `npx mcp-remote` under the hood). See [`mcpb/README.md`](../mcpb/README.md) for what's in the bundle.
97
+ Prerequisite: Node 20.18.1+ on `PATH` (the bundle launches `npx mcp-remote` under the hood). See [`mcpb/README.md`](../mcpb/README.md) for what's in the bundle.
66
98
 
67
99
  **Option B — `create-meko-setup` installer:**
68
100
 
@@ -98,7 +130,7 @@ Re-run the installer to refresh the bundles when skill content updates; you'll n
98
130
  # Target two clients in one run:
99
131
  npx @yugabytedb/meko-mcp --client claude-code,claude-desktop --local --yes
100
132
 
101
- # Target every supported client (claude-code, claude-desktop, cursor):
133
+ # Target every supported client:
102
134
  npx @yugabytedb/meko-mcp --client all --local --yes
103
135
  ```
104
136
 
@@ -151,7 +183,7 @@ The canary used to do a `memory_add` → `memory_get_by_id` → `memory_delete_b
151
183
  - MCP config: `~/.codex/config.toml` (TOML; `[mcp_servers.<name>]` block).
152
184
  - Skills path: `~/.agents/skills/meko-mcp-tools/` (the shared agent-skills convention).
153
185
  - System prompt: `~/.codex/AGENTS.md` (a Meko-managed sentinel block is upserted; user content outside the fence is preserved).
154
- - **Cloud auth (MCP server)**: Codex's TOML stores `bearer_token_env_var = "MEKO_API_KEY"` rather than the key itself. You must export `MEKO_API_KEY` in your shell before launching Codex. The installer offers (interactively, never with `--yes`) to append the export line to your shell rc (`~/.zshrc` / `~/.bashrc` / `~/.config/fish/config.fish`). On macOS, when the desktop app is detected and an API key is supplied, the installer runs `launchctl setenv MEKO_API_KEY ...` so Codex.app launched from Finder/Dock can authenticate in the current login session.
186
+ - **Cloud auth (MCP server)**: Codex's TOML stores `bearer_token_env_var = "MEKO_API_KEY"` rather than the key itself. You must export `MEKO_API_KEY` in your shell before launching Codex. The installer offers (interactively, never with `--yes`) to append the export line to your shell rc (`~/.zshrc` / `~/.bashrc` / `~/.config/fish/config.fish`). On macOS, when an API key is supplied, the installer offers to run `launchctl setenv MEKO_API_KEY ...` (default Yes) so GUI-launched Codex - the desktop app from Finder/Dock, or the CLI from Spotlight/Alfred/`open` - can authenticate in the current login session. The prompt calls out that `launchctl setenv` writes into the login-session env inherited by every subsequently GUI-launched process (visible via `launchctl getenv`, `ps e` on descendants, and crash reports); decline the offer if that exposure is not acceptable for the host and rely on shell rc instead.
155
187
  - **Cloud WAF User-Agent** (cloud installs only): Cloud Meko sits behind a WAF that returns an HTML `403 Forbidden` for any HTTP request with no `User-Agent`. Codex's native MCP client sends none by default, so its `initialize` handshake would 403 and the `meko` server would fail to start with `MCP client for 'meko' failed to start ... HTTP 403`. `codex mcp add` exposes no header flag, so the installer writes a per-server header into `~/.codex/config.toml`:
156
188
  ```toml
157
189
  [mcp_servers.meko]
@@ -159,9 +191,34 @@ The canary used to do a `memory_add` → `memory_get_by_id` → `memory_delete_b
159
191
  ```
160
192
  This is **scoped to the Meko server block only** — it affects requests Codex makes to the Meko MCP server and nothing else (other MCP servers, other Codex traffic are untouched). The installer **merges** rather than clobbers: it reads whatever headers you already have — in either the inline `http_headers = { … }` form or the table form `[mcp_servers.meko.http_headers]` — keeps every one of them, and only *adds* a `User-Agent` if you don't already have one (a custom User-Agent is left exactly as you set it). If you had the table form, it's folded into the single inline line so there's never a conflicting duplicate definition. This holds **across a reinstall** too: re-registration deletes and rewrites the whole server block, so the installer snapshots your headers beforehand and merges them back. It **backs up `config.toml` to `config.toml.bak`** before writing. Local installs skip this (no WAF in front of `localhost`). Related: the capture-hook side of the same WAF issue is [#174](https://github.com/yugabyte/meko-mcp-server/issues/174).
161
193
  - **No project scope**: Codex has no per-project config. `--scope project --client codex` warns and degrades to user scope.
162
- - **Auto-capture hooks**: when Codex hooks are available, the installer copies the Meko hook scripts to `~/.codex/meko-hooks/hooks-handlers/`, writes Meko lifecycle hooks to `~/.codex/hooks.json` pointing at that copy, and prunes stale Meko hook entries from deleted install paths. The scripts live under `$CODEX_HOME` (not the shipped package) on purpose: under `npx @yugabytedb/meko-mcp` the package unpacks into an ephemeral `~/.npm/_npx/<hash>/` cache dir that npx later garbage-collects, so a `hooks.json` pinned there would become a dangling `bash '<missing>'` and Codex would report `SessionStart hook (failed) — hook exited with code 127`. Copying under `$CODEX_HOME` gives the hook a home-dir-stable target; re-running the installer refreshes the copy and auto-heals any stale npx-pinned entry. `memory_search` still handles recall; capture is driven by the hooks (and, where hooks are unavailable, by AGENTS.md-prompted per-turn `conversation_add_message` posting that the server extracts memories from). Explicit `memory_add` is reserved for the narrow cases — the user says "remember this", a fact lives only in the assistant's output or a tool result, or a negated/corrected fact needs overwriting.
194
+ - **Auto-capture hooks**: the installer copies the Meko hook scripts to `~/.codex/meko-hooks/hooks-handlers/`, writes Meko lifecycle hooks to `~/.codex/hooks.json` pointing at that copy, and prunes stale Meko hook entries from deleted install paths. The scripts live under `$CODEX_HOME` (not the shipped package) on purpose: under `npx @yugabytedb/meko-mcp` the package unpacks into an ephemeral `~/.npm/_npx/<hash>/` cache dir that npx later garbage-collects, so a `hooks.json` pinned there would become a dangling `bash '<missing>'` and Codex would report `SessionStart hook (failed) — hook exited with code 127`. Copying under `$CODEX_HOME` gives the hook a home-dir-stable target; re-running the installer refreshes the copy and auto-heals any stale npx-pinned entry. `memory_search` still handles recall; capture is driven by the hooks (and, where hooks are unavailable, by AGENTS.md-prompted per-turn `conversation_add_message` posting that the server extracts memories from). Explicit `memory_add` is reserved for the narrow cases — the user says "remember this", a fact lives only in the assistant's output or a tool result, or a negated/corrected fact needs overwriting.
163
195
  - **API key storage** (security note): Codex's hooks.json schema rejects a top-level `env` block and provides no per-command env field. The installer writes `MEKO_MCP_URL` and (when supplied) `MEKO_API_KEY` to `~/.codex/meko-env.sh` at mode 0600 (user read/write only); each hook's `command` field sources that file before invoking bash, e.g. `. '/Users/you/.codex/meko-env.sh' && bash '/Users/you/.codex/meko-hooks/hooks-handlers/session-start.sh'`. The key never appears in process arguments, so `ps -ef` from another user can't lift it while a hook is running. Trust boundary is the same as `~/.aws/credentials` or `~/.netrc` — file-system permissions on the user's home directory. If your threat model precludes even that, install for the local Meko URL (no API key prompt), or skip hook installation by deleting the Meko entries from `~/.codex/hooks.json` and removing `~/.codex/meko-env.sh` and `~/.codex/meko-hooks/` after install.
164
196
  - **Restart Codex.app** after install so it picks up the new MCP server registration.
197
+ - **Kiro (IDE + `kiro-cli`)** — one install configures both surfaces (they share the same config tree). `$KIRO_HOME` overrides the global root, matching how Kiro resolves every global surface.
198
+ - MCP config: `~/.kiro/settings/mcp.json` or `.kiro/settings/mcp.json`. Registered as a native remote server (`url` + `headers`), never via the `mcp-remote` stdio bridge. Because the file contains the bearer token, the adapter writes and reasserts mode 0600 — see [Kiro notes](#kiro-notes).
199
+ - Skills path: `~/.kiro/skills` or `.kiro/skills`. Kiro implements the same [Agent Skills](https://agentskills.io) standard our skills ship in, so they install by copy with no conversion. Packaged installs copy rather than symlink back into the ephemeral npm cache.
200
+ - Standalone hooks: `~/.kiro/hooks/meko-{session-start,user-prompt-submit,stop}.json` or the project equivalents, covering CLI 3.x and trusted IDE workspaces.
201
+ - Managed CLI 2.x agent: `~/.kiro/agents/meko-default.json` or `.kiro/agents/meko-default.json`, with inline `agentSpawn`, `userPromptSubmit`, and `stop` hooks. The installer preserves the previous `chat.defaultAgent` before selecting it.
202
+ - Stable capture runtime: `~/.kiro/hooks/meko-hooks/hooks-handlers/` or the project equivalent. `kiro-hook-env.json` stores the endpoint and optional API key at mode 0600.
203
+ - Steering file: `~/.kiro/steering/meko.md` or `.kiro/steering/meko.md`. It follows hook capture when the session-start marker is present and falls back to agent-driven persistence only when hooks are disabled, untrusted, or a different CLI 2.x agent is selected. See [Kiro notes](#kiro-notes).
204
+ - **OpenCode CLI/TUI 1.18.x**
205
+ - User plugin: `$OPENCODE_CONFIG_DIR/plugins/meko.js` when set, otherwise `~/.config/opencode/plugins/meko.js`; project plugin: `<project>/.opencode/plugins/meko.js`.
206
+ - Stable runtime and private environment: `$OPENCODE_CONFIG_DIR/meko/` when set, otherwise `~/.config/opencode/meko/`, or `<project>/.opencode/meko/`. `opencode-hook-env.json` is mode 0600 and contains the MCP URL, API key, server name, User-Agent, hook client, stable Node executable, and watermark dir. Automatic capture uses the server-default datapack in this release. `--datapack` applies only to `--migrate`. OpenCode does not read `pin-<slug>.json` (no SessionStart, and `meko-select-datapack` is not installed).
207
+ - Skill: `$OPENCODE_CONFIG_DIR/skills/meko-mcp-tools/` when set, otherwise `~/.config/opencode/skills/meko-mcp-tools/`, or `<project>/.opencode/skills/meko-mcp-tools/`.
208
+ - The plugin mutates OpenCode's resolved configuration at runtime with a native remote MCP entry (`type: remote`, `enabled: true`, `oauth: false`). It does **not** edit `opencode.json` or `opencode.jsonc`; validation runs `opencode debug config` and verifies the resolved entry.
209
+ - Restart OpenCode after install. Reinstall upgrades installer-owned assets while preserving durable capture state and user-owned collisions.
210
+
211
+ ### OpenCode CLI / TUI 1.18.x
212
+
213
+ OpenCode **1.18.x only** is supported in this release; 1.18.22 is the initial canary baseline. The installer rejects older releases and future minor/major versions until they are explicitly certified because OpenCode's plugin and MCP configuration APIs are evolving.
214
+
215
+ The installer-owned plugin listens to OpenCode's public `event({event})` hook. On `session.idle` and `session.error`, it fetches `session.get({path:{id}})` and `session.messages({path:{id}})`, strips the snapshot to visible text plus stable IDs/timestamps, and hands it to Capture v2. Duplicate completed turns are skipped by OpenCode message IDs. A `session.error` snapshot does not rewrite a completed turn and does not append a synthetic `u1:interrupted` user id. A failed handoff leaves a staged snapshot under the durable capture root for startup replay; structurally invalid snapshots are quarantined.
216
+
217
+ **Capture boundary:** one visible user message plus the first visible assistant message before the next user message becomes one Meko turn. Later assistant messages in the same turn are dropped. Multiple visible text parts on that first assistant retain order. Tool calls/results, reasoning, files, attachments, steps, snapshots, patches, text explicitly marked ignored or synthetic, and binary content are excluded. OpenCode compaction-summary exclusion is not certified because the 1.18.22 export shape has not been frozen in a real fixture. Active incomplete turns remain pending until a later snapshot completes them, a later user turn interrupts them, or the 30-minute settle deadline expires (every Capture v2 timer that shares the capture root uses that default; `MEKO_OPENCODE_SETTLE_MS` is a same-process test override). `session.error` holds a partial assistant as a tentative observation until OpenCode rewrites that assistant in place on idle, a later user turn interrupts, or that deadline expires. A later distinct user turn interrupts the prior pending user. Ownership is `opencode:<repo>`, derived at capture from the session directory (the plugin does not inject `agent_id`). Health is visible through `create-meko-setup --capture-status`.
218
+
219
+ **Migration:** `create-meko-setup --migrate` invokes only supported public CLI commands (`opencode session list --format json` and `opencode export <sessionID>`); it never reads OpenCode's SQLite database or internal storage. User scope is a cwd-backed superset that includes the active project. OpenCode 1.18.x lists root sessions only, so child sessions are not migrated. An unsupported OpenCode version is skipped without blocking Claude Code, Cursor, or Codex migration. The current durable root-session view is migrated, not a historical event log.
220
+
221
+ **Not supported yet:** OpenCode Desktop certification, V2 preview APIs/configuration, pre-1.18 or post-1.18 versions, GitHub agent/headless server/remote workspace/shared-session migration, parent-child tree reconstruction, tool/reasoning/file/attachment capture, and incremental streaming capture.
165
222
 
166
223
  ## CLI Reference
167
224
 
@@ -172,7 +229,7 @@ Usage:
172
229
 
173
230
  | Flag | Description | Default |
174
231
  |------|-------------|---------|
175
- | `--client <list>` | Target client(s): one or more of `claude-code`, `claude-desktop`, `cursor`, `codex`. Comma-separated, or `all`. `codex` covers both the OpenAI Codex CLI and the Codex Desktop App (they share `~/.codex/config.toml`). | `claude-code` |
232
+ | `--client <list>` | Target client(s): one or more of `claude-code`, `claude-desktop`, `cursor`, `codex`, `kiro`, `opencode`. Comma-separated, or `all`. `codex` covers both the OpenAI Codex CLI and the Codex Desktop App (they share `~/.codex/config.toml`); `kiro` covers both the Kiro IDE and `kiro-cli` (they share `~/.kiro/settings/mcp.json`). OpenCode is version-gated to CLI/TUI 1.18.x. Aliases: `claude` → `claude-code`, `kiro-cli` / `kiro-ide` → `kiro`. | `claude-code` |
176
233
  | `--url <url>` | MCP server URL (from cloud.mekodata.ai > Datapacks > Connect) | Shared existing endpoint, otherwise production; conflicts prompt or require `--url` under `--yes` |
177
234
  | `--api-key <key>` | API key for Cloud Meko authentication. If a Cloud `--url` is supplied without this flag, the installer falls back to `$MEKO_API_KEY`, then to an interactive prompt on a TTY. Only fails hard when running under `--yes` or with no TTY attached. | _(interactive prompt or `$MEKO_API_KEY` env)_ |
178
235
  | `--local` | Use local Meko at `http://localhost:8000/mcp` (no API key needed) | `false` |
@@ -189,11 +246,23 @@ Usage:
189
246
  | `-h`, `--help` | Show help message | |
190
247
  | `-v`, `--version` | Show version | |
191
248
 
249
+ Capture health flags (durable outbox / recovery — Capture V2):
250
+
251
+ | Flag | Description | Default |
252
+ |------|-------------|---------|
253
+ | `--capture-status` | Report aggregate conversation-capture health from `~/.claude/meko-capture` (or `MEKO_WATERMARK_DIR`). Exit `0` = healthy\|catching_up, `2` = degraded_retrying, `3` = blocked_action_required. | `false` |
254
+ | `--json` | With `--capture-status`, print machine-readable JSON matching the capture-health cache shape. Also accepted by `--capture-cleanup`. | `false` |
255
+ | `--capture-cleanup <action>` | Inspect and clean up stuck capture on this machine. Actions: `status`, `stop`, `resume`, `drain`, `discard`, `repair`. See [Clean up stuck capture](../OPERATIONS.md#clean-up-stuck-capture). Exit `0` = done, `1` = error, `2` = partly done. | |
256
+ | `--session <id>` | With `--capture-cleanup`, act on one session. Repeatable; a unique prefix of the ID is enough. A bare ID after the action works too: `--capture-cleanup drain c0acdeb2`. | |
257
+ | `--all` | With `--capture-cleanup`, act on every session. `drain` and `discard` need `--session` or `--all`. | `false` |
258
+ | `--max-exchanges <n>` | With `--capture-cleanup drain`, stop after sending this many exchanges in total. | `200` |
259
+ | `--pace-ms <n>` | With `--capture-cleanup drain`, wait this long between batches. | `1000` |
260
+
192
261
  Migrate subcommand flags (see [Migrate Existing Data](#migrate-existing-data) below for details):
193
262
 
194
263
  | Flag | Description | Default |
195
264
  |------|-------------|---------|
196
- | `--migrate` | Start a detached migration of existing Claude Code + Cursor conversations and `CLAUDE.md` memories into Meko. | `false` |
265
+ | `--migrate` | Start a detached migration of existing Claude Code, Cursor, Codex, and OpenCode sessions plus `CLAUDE.md` memories into Meko. | `false` |
197
266
  | `--with-migrate` | With `--yes`, kick off `--migrate` at the end of a successful install without prompting. CI-friendly. | `false` |
198
267
  | `--migrate-status` | Print the status of the latest (or `--run-id`-specified) migration run. | `false` |
199
268
  | `--migrate-stop` | Stop the running migration (SIGTERM, then SIGKILL after 2s). | `false` |
@@ -210,8 +279,9 @@ After installing Meko, `--migrate` seeds your new datapack with the conversation
210
279
 
211
280
  | Source | Location | Becomes |
212
281
  |---|---|---|
213
- | Claude Code transcripts | `~/.claude/projects/<slug>/<uuid>.jsonl` | One Meko conversation (one Langfuse trace) per session; one paired user↔assistant turn per message pair. |
282
+ | Claude Code transcripts | `~/.claude/projects/<slug>/<uuid>.jsonl` | One Meko conversation (one Langfuse trace) per session; one paired user↔assistant turn per message pair. Skill bodies and task notifications join the `reasoning` of the turn they belong to, and a typed `/skill args` becomes that turn's input, as in live capture. |
214
283
  | Cursor transcripts | `~/.cursor/projects/<name>/agent-transcripts/<uuid>/*.jsonl` | Same — one conversation per session, one paired turn per user↔assistant round. |
284
+ | OpenCode sessions | Public CLI: `opencode session list --format json`, then `opencode export <sessionID>` | One Meko conversation per root session with directory metadata; visible text only. |
215
285
  | User-scope CLAUDE.md | `~/.claude/CLAUDE.md` | One memory per top-level `# ` section (`agent_id = "meko_agent"` — the cross-project common bucket — with `metadata.scope = "user"`). |
216
286
  | Project-scope CLAUDE.md | `<projectCwd>/.claude/CLAUDE.md` | One memory per top-level `# ` section (`agent_id = "claude_code:<repo-basename>"`, `metadata.scope = "project"`, `metadata.cwd = <project path>`). |
217
287
  | Indexed project memories | `~/.claude/projects/<slug>/memory/*.md` | One memory per file (`agent_id = "claude_code:<repo-basename>"`); YAML frontmatter (`name`, `description`, `type`) is preserved in metadata. |
@@ -220,12 +290,14 @@ Project `cwd` is derived by reading the `cwd` field from a real session file in
220
290
 
221
291
  ### What the migrator writes (MCP calls)
222
292
 
223
- Per migrated Claude Code / Cursor session:
293
+ Per migrated Claude Code / Cursor / Codex session:
224
294
 
225
- 1. **`conversation_create`** — once per session. Captures `cwd`, `gitBranch`, `startedAt`, and source agent (`claude-code` or `cursor`) in metadata. Returns the `conversation_id` used for subsequent calls.
295
+ 1. **`conversation_create`** — once per session. Captures `cwd`, `startedAt`, and source agent (`claude-code`, `cursor`, or `codex`) in metadata. Returns the `conversation_id` used for subsequent calls. `gitBranch` is set only for Claude Code (its refs have a slug). Cursor and Codex send null.
226
296
  2. **`conversation_add_message`** — once per paired user↔assistant turn. Carries `input`, `output`, and `reasoning` (thinking blocks + tool-use/tool-result summaries), plus a deterministic `seed` for server-side dedup. Fire-and-forget; Meko batches to Langfuse.
227
297
  3. **`memory_add(messages=[{role,content},...])`** — once per paired turn. This invokes Meko's server-side Mem0 extractor (the same pipeline the demo chatbot uses at `chatbot.py:177-178`) so the migrator seeds structured memories, not just raw transcripts. Skipped entirely under `--no-extract`.
228
298
 
299
+ Per migrated OpenCode session: the same three calls, with two differences. `gitBranch` is always null because OpenCode refs have no slug. The adapter sets `reasoning` to `""`, and the orchestrator omits the field (`reasoning || undefined`), so the MCP call has no `reasoning` key.
300
+
229
301
  Per CLAUDE.md / memory directory discovered:
230
302
 
231
303
  4. **`conversation_create`** — one umbrella "memory-migration" conversation per agent (reused for all MD items under that agent, to satisfy Meko's `memory_add` requirement that memories be associated with a conversation).
@@ -237,6 +309,8 @@ So a real run exercises **both** the Langfuse observability surface (1 + 2) and
237
309
  Dry run:
238
310
  Claude Code sessions: 94
239
311
  Cursor sessions: 10
312
+ Codex sessions: 3
313
+ OpenCode sessions: 4
240
314
  Paired user↔asst turns: 887
241
315
  Memory items (MD): 14
242
316
  Planned MCP calls:
@@ -255,7 +329,7 @@ The Meko MCP server does **not** currently auto-extract memories on `conversatio
255
329
 
256
330
  ### Scope
257
331
 
258
- - `--scope user` (default) — every Claude Code and Cursor project on the machine, plus `~/.claude/CLAUDE.md`, plus every per-project `.claude/CLAUDE.md` and indexed `memory/*.md` directory.
332
+ - `--scope user` (default) — every supported local session found by the registered migration adapters, plus `~/.claude/CLAUDE.md`, plus every per-project `.claude/CLAUDE.md` and indexed `memory/*.md` directory.
259
333
  - `--scope project` — only sessions whose `cwd` equals `process.cwd()`, plus that one project's `.claude/CLAUDE.md` and memory directory.
260
334
 
261
335
  ### Running
@@ -286,32 +360,36 @@ create-meko-setup --migrate --scope user --no-extract
286
360
 
287
361
  ```
288
362
  run-id: 20260429-000912-74dbe6
289
- status: running (pending → running → done | failed | stopped)
363
+ status: running (running → succeeded | failed | cancelled; crashed is inferred)
290
364
  scope: user
291
365
  started: 2026-04-29T00:09:12.673Z
292
366
  finished: (in progress)
293
- current: agent / a2f6a1d6-... (agent_id / source-session-uuid)
367
+ heartbeat: 2026-04-29T00:14:02.118Z (refreshed while running; staleness implies crashed)
368
+ current: agent / a2f6a1d6-... / 7c1e0b44-... (<agent-id> / <source-session> / <message-id>)
294
369
  sessions: 76 / 111 (processed / discovered)
295
370
  messages: 674
296
371
  memory pairs: 674 (memory_add from Mem0 extractor)
297
372
  memory (MD): 0 (memory_add from CLAUDE.md / memory files)
298
373
  warnings: 0 (non-fatal; run continues)
299
374
  memory errs: 0 (failed memory_add calls; re-run with --force to retry)
375
+ auth skipped: 0 (calls dropped on a mid-run 401/403; re-run to retry them)
300
376
  ```
301
377
 
302
378
  | Field | Meaning |
303
379
  |---|---|
304
380
  | `run-id` | Unique ID; also the directory name under `~/.meko/migrate/<run-id>/`. |
305
- | `status` | Terminal states are `done`, `failed`, `stopped`. A `running` row that hasn't advanced in >30 s may indicate the daemon died — check `~/.meko/migrate/<run-id>/stdout.log`. |
381
+ | `status` | `running` while the daemon is alive; `succeeded`, `failed`, or `cancelled` once it ends. A fifth value, `crashed`, is not stored: the status command reports it for a run whose daemon is gone (dead PID, or a heartbeat older than 30 s), which is what a `kill -9`, an OOM kill, or a reboot looks like. On `failed`, an `error:` line carries the reason. |
306
382
  | `scope` | Matches the `--scope` flag used at start (`user` or `project`). |
307
- | `started` / `finished` | UTC timestamps. `finished` shows `(in progress)` until a terminal state is reached. |
308
- | `current` | Which source item is being replayed — `<adapter>:<source-session>` (adapter = `claude-code` / `cursor` / `memory-md`). Becomes stale on `done`/`failed`/`stopped`. |
383
+ | `started` / `finished` | UTC timestamps. `finished` shows `(in progress)` until a terminal state is reached, or `(never, daemon died)` for a crashed run. |
384
+ | `heartbeat` | Last time the daemon touched `progress.json`. Refreshed every 10 s while running. |
385
+ | `current` | Which source item is being replayed: `<agent-id> / <source-session-uuid> / <source-message-uuid>`, where `<agent-id>` is the owner such as `opencode:<repo>` or `claude_code:<repo>`. Becomes `-` once the run reaches a terminal state. |
309
386
  | `sessions` | Conversation sessions processed / discovered across all adapters. Climbs during the run. |
310
387
  | `messages` | Total messages replayed so far via `conversation_add_message`. |
311
388
  | `memory pairs` | `memory_add` calls made via the Mem0 extractor path (messages-style input). Usually close to `messages` when the extractor is enabled. |
312
389
  | `memory (MD)` | `memory_add` calls from `CLAUDE.md` / memory-file sources (plain-text input). |
313
390
  | `warnings` | Non-fatal issues (skipped item, partial parse). Run keeps going. |
314
391
  | `memory errs` | `memory_add` calls that failed. Non-zero means the memory extractor couldn't reach the server or rejected the payload — check the per-run log. Re-run with `--force` to retry only the failed items. |
392
+ | `auth skipped` | Calls dropped because the server returned 401/403 partway through the run, typically an expired token. The session that hit the 401/403 stops at that turn and is marked failed, and the run moves on to the next session. Each dropped call is recorded in `log.ndjson` as `message_skipped_auth`. Refresh the credential and re-run without `--force`: the run resumes each failed session from its last delivered turn. |
315
393
 
316
394
  ### Behavior around install
317
395
 
@@ -321,39 +399,82 @@ memory errs: 0 (failed memory_add calls; re-run with --force to
321
399
 
322
400
  ### Idempotency, resume, and state
323
401
 
324
- - Per-run logs: `~/.meko/migrate/<run-id>/log.ndjson` (one JSON object per line). `tail -f | jq .` is a reasonable debug loop.
402
+ - Per-run logs: `~/.meko/migrate/<run-id>/log.ndjson` (one JSON object per line). `tail -f | jq .` is a reasonable debug loop. This is the file the start banner points at, and the only one with run diagnostics in it.
403
+ - `~/.meko/migrate/<run-id>/stdout.log` is the daemon's redirected stdout/stderr. It is normally empty, because every diagnostic goes through the NDJSON logger instead. Non-empty means output escaped the logger: a stack trace from before it opened, or a native crash.
325
404
  - Per-run progress snapshot: `~/.meko/migrate/<run-id>/progress.json` (what `--migrate-status` reads).
326
405
  - Durable state: `~/.meko/migrate/state/<datapack-key>.json` tracks per-session checkpoints (`conversationId`, `messagesSent`, `lastUuid`, `status`) and per-memory content hashes, **scoped per destination datapack**. A re-run skips sessions marked `done` and memories whose body hash is unchanged. A partial session resumes from `lastUuid + 1`.
327
406
  - **Per-datapack scoping**: the datapack key is the UUID segment of the MCP URL (`https://<uuid>.mcp.mekodev.com/mcp` → `<uuid>`), or a stable 16-char sha256 prefix for non-UUID URLs (e.g. `http://localhost:8000/mcp`). Switching datapacks produces completely independent state — migrating into a fresh datapack never falsely treats sessions as already-done just because an earlier run populated a different one. `--migrate-status` prints the `datapack:` key of the latest run so you can tell at a glance which state it belongs to.
328
407
  - Back-compat for the legacy layout: migrations created before this change wrote to a single `~/.meko/migrate/state.json`. On the first run after upgrading, if `state/` is empty and `state.json` exists, the installer asks (or, under `--yes` / CI, silently accepts) "treat the legacy state as belonging to the current datapack" and copies `state.json` → `state/<current-key>.json`. The legacy file is kept on disk in case you roll back the installer.
329
408
  - Server-side dedup: `conversation_add_message` gets a deterministic `seed = sha256(agentId + ":" + sessionUuid + ":" + messageUuid)` so partial-run overlap doesn't create duplicate messages.
330
409
  - `--force` wipes the **current datapack's** resume state and re-issues every MCP call for it. Other datapacks' state files are untouched. If you only want to re-migrate into a new datapack, just point `--url` at the new one — no `--force` needed.
410
+ - `--force` doesn't remove messages that an older installer stored and this version no longer produces. Installers before 2.3.0 stored each Claude Code task notification as its own message, with the notification as input. 2.3.0 folds the notification and its reply into the turn it belongs to. After `--force`, that turn is sent again with the reply, but the old notification message stays, so the reply can appear twice. To get a clean copy, migrate into a new datapack instead.
331
411
 
332
412
  ### What does NOT get migrated (v1)
333
413
 
334
414
  - Slash-command skills, hooks, and `.claude/settings.json` — those remain in Claude Code's own config; Meko has no equivalent tool.
335
415
  - Knowledge-base documents — KB ingestion is UI-only (Datapack → Actions → Add Knowledge); not exposed via MCP.
336
- - Transcripts from other coding agents (Aider, Continue.dev, Windsurf, Codex). Claude Code + Cursor only.
416
+ - Transcripts from unregistered coding agents (for example Aider, Continue.dev, or Windsurf).
337
417
  - `user_id` is left null on every call (the server fills it from the API key). Multi-user routing is a follow-up.
338
418
 
339
419
  ## Client Compatibility Matrix
340
420
 
341
- | Capability | Claude Code | Claude Desktop | Cursor | Codex (CLI + App) |
342
- |---|---|---|---|---|
343
- | One-command install | Yes | Yes | Yes | Yes |
344
- | MCP server registration | via `claude mcp add` | via `claude_desktop_config.json` merge using `mcp-remote` stdio bridge | via `.cursor/mcp.json` merge | via `codex mcp add` (CLI) or direct TOML write (App-only) |
345
- | Backup before config write | `.bak` on `~/.claude/settings.json` | `.bak` on `claude_desktop_config.json` | `.bak` on Cursor config | `.bak` on `~/.codex/config.toml` and `~/.codex/AGENTS.md` |
346
- | Automatic conversation capture | Yes (hooks) | **No** — see [issue #8](https://github.com/amiramm/meko-mcp-server/issues/8) | Yes (native hooks; first SessionStart may pause briefly while creating the Meko conversation) | **No** — Codex has no user-installable hooks; AGENTS.md prompts proactive memory tool calls instead |
347
- | Automatic memory persistence | Yes (hooks + skill) | Skill only (proactive `memory_add`) | Yes (hooks + skill) | Skill + AGENTS.md (proactive `memory_add`) |
348
- | Skill install | Plugin marketplace (automatic) | `.skill` bundle auto-built, user drags into app | Copy into `.cursor/skills` | Copy into `~/.agents/skills/meko-mcp-tools/` |
349
- | Uninstall | Full automatic | MCP entry removed; skill must be removed manually from app | MCP + hooks + skills cleaned up | MCP entry, skill dir, and AGENTS.md block all cleaned up |
421
+ | Capability | Claude Code | Claude Desktop | Cursor | Codex (CLI + App) | Kiro (IDE + `kiro-cli`) | OpenCode CLI/TUI 1.18.x |
422
+ |---|---|---|---|---|---|---|
423
+ | One-command install | Yes | Yes | Yes | Yes | Yes | Yes |
424
+ | MCP server registration | via `claude mcp add` | via `claude_desktop_config.json` merge using `mcp-remote` stdio bridge | via `.cursor/mcp.json` merge | via `codex mcp add` (CLI) or direct TOML write (App-only) | via `~/.kiro/settings/mcp.json` merge (native remote MCP) | Installer-owned plugin overlay; source config untouched |
425
+ | Backup before config write | `.bak` on `~/.claude/settings.json` | `.bak` on `claude_desktop_config.json` | No `.bak` — additive JSON writes preserve unrelated MCP servers, hooks, and settings | `.bak` on `~/.codex/config.toml` and `~/.codex/AGENTS.md` | No `.bak` — the write is an additive merge that preserves other `mcpServers` entries | No source-config write; plugin overlay is copied then renamed into place; runtime tree uses installStableTree |
426
+ | Automatic conversation capture | Yes (hooks) | **No** — see [issue #8](https://github.com/amiramm/meko-mcp-server/issues/8) | Yes (native hooks; first SessionStart may pause briefly while creating the Meko conversation) | Yes (hooks) — `SessionStart` / `PreCompact` / `SessionEnd` written to `~/.codex/hooks.json`; the AGENTS.md block provides fallback instructions when hooks are unavailable | Yes — inline CLI 2.x plus standalone CLI 3.x/IDE hooks share Capture v2 | Yes (`session.idle` / `session.error`, Capture v2; error is an observation, not an immediate interrupt) |
427
+ | Automatic memory persistence | Yes (hooks + skill) | Skill only (proactive `memory_add`) | Yes (hooks + skill) | Hooks + skill; AGENTS.md fallback when hooks are unavailable | Hooks + skill; conditional steering fallback when the hook marker is absent | Yes (capture + skill) |
428
+ | Skill install | Plugin marketplace (automatic) | `.skill` bundle auto-built, user drags into app | Copy into `.cursor/skills` | Copy into `~/.agents/skills/meko-mcp-tools/` | Copy into `~/.kiro/skills/` (native Agent Skills format) | Copy into scoped OpenCode `skills/` |
429
+ | Uninstall | Full automatic | MCP entry removed; skill must be removed manually from app | MCP + hooks + skills cleaned up | MCP entry, skill dir, and AGENTS.md block all cleaned up | MCP entry plus installer-owned hooks, managed agent, private env, skills, and steering; pending outbox data is preserved | Owned plugin/runtime/skill removed; env stays watermark-only; snapshots, journals, outboxes, and capture-root quarantine stay; #307-era invalid files stay in meko/snapshots/quarantine |
430
+ | `agent_id` used by skill | `claude_code:<repo>` per project (via SessionStart hook); cross-project facts go to `meko_agent` | `claude_desktop` | `cursor:<repo>` per project (via Cursor hook); cross-project facts go to `meko_agent` | `codex:<repo>` per project (via SessionStart hook); cross-project facts go to `meko_agent` | `kiro:<repo>` per project via hook capture; cross-project facts go to `meko_agent` | `opencode:<repo>` per project, derived at capture from the session directory (the plugin does not inject `agent_id`); cross-project facts go to `meko_agent` |
431
+ | Transport | HTTP only | HTTP (or stdio via hand-edited config) | HTTP only | HTTP (Codex's `bearer_token_env_var` model) | HTTP only | Native remote HTTP MCP |
432
+ | Auth model | inline `Authorization: Bearer ...` header | inline header via `mcp-remote` | inline header | MCP server: env-var name reference (`MEKO_API_KEY` exported in shell or via `launchctl setenv`). Hooks: key written to `~/.codex/meko-env.sh` at mode 0600; each hook command sources the file before bash (no env block in Codex's hooks schema; see [Security](#security)). | inline `Authorization: Bearer ...` header (never `mcp-remote` — see note below) | Authorization header supplied by the plugin from a mode-0600 private env file |
433
+ | Project scope | Yes (`--scope project`) | No | Yes (`--scope project`) | No (warns and degrades to user scope) | Yes (`--scope project`) | Yes (`--scope project`) |
434
+ | Deferred to a follow-up | — | `.mcpb` Desktop Extension packaging — see [issue #7](https://github.com/amiramm/meko-mcp-server/issues/7) | — | — | Tool-call/hidden-reasoning capture (not exposed by Kiro hook events) | Desktop/V2/headless certification; richer capture surfaces |
350
435
 
351
436
  Cursor previously had a hybrid hook mode that also wrote Claude-compatible hooks into `~/.claude/settings.json`. If you used that mode and now see duplicate `claude_code:*` Meko instructions in Cursor, remove the old Meko hook entries from `~/.claude/settings.json` unless you still use them for Claude Code.
352
- | `agent_id` used by skill | `claude_code:<repo>` per project (via SessionStart hook); cross-project facts go to `meko_agent` | `claude_desktop` | `cursor:<repo>` per project (via Cursor hook); cross-project facts go to `meko_agent` | Whatever the agent derives by following the meko-mcp-tools skill (no hook injects an agent_id; the AGENTS.md block defers to the skill) |
353
- | Transport | HTTP only | HTTP (or stdio via hand-edited config) | HTTP only | HTTP (Codex's `bearer_token_env_var` model) |
354
- | Auth model | inline `Authorization: Bearer ...` header | inline header via `mcp-remote` | inline header | MCP server: env-var name reference (`MEKO_API_KEY` exported in shell or via `launchctl setenv`). Hooks: key written to `~/.codex/meko-env.sh` at mode 0600; each hook command sources the file before bash (no env block in Codex's hooks schema; see [Security](#security)). |
355
- | Project scope | Yes (`--scope project`) | No | Yes (`--scope project`) | No (warns and degrades to user scope) |
356
- | Deferred to a follow-up | — | `.mcpb` Desktop Extension packaging — see [issue #7](https://github.com/amiramm/meko-mcp-server/issues/7) | — | Native `codex:<repo>` agent_id classification in capture.js |
437
+
438
+ ### Kiro notes
439
+
440
+ **One adapter, both surfaces.** The Kiro IDE and `kiro-cli` read the same `mcpServers` config and the same skills directories, so `--client kiro` configures both. `kiro-cli` and `kiro-ide` are accepted as aliases. `$KIRO_HOME` is honored for the global config tree, matching how Kiro resolves every global surface.
441
+
442
+ **Never use the `mcp-remote` bridge with Kiro.** Kiro speaks remote MCP natively, so the installer registers `{ url, headers }` directly.
443
+
444
+ What was measured against `https://mcp.mekodata.ai` before 2026-09-24. Production
445
+ now accepts loopback redirects on `/register`, `/authorize`, and `/token`; DEV
446
+ (`mcp.mekodev.com`) still refuses them. The `mcp-remote` findings below still
447
+ apply, because an API key behind the bridge is never sent.
448
+
449
+ - `mcp-remote@0.1.38` reads no auth env var (its only `process.env` reference is `MCP_REMOTE_CONFIG_DIR`), so `MEKO_API_KEY` is never sent; headers come only from `--header`. It therefore connects unauthenticated and begins OAuth.
450
+ - Run directly, it fails at `registerClient` — the DCR POST — with `HTTP 403: Invalid OAuth error response … Raw body: <html>403 Forbidden</html>`. Its source builds `redirect_uri` as `http://${host}:${callbackPort}${callbackPath}`, defaulting to `localhost` and `/oauth/callback`.
451
+ - Sending mcp-remote's exact registration body to `POST /register` reproduces it, and changing **only** the redirect host fixes it: loopback → `403 text/html` from `server: awselb/2.0`; `https://example.com/...` → `201 application/json` from `server: uvicorn`. So the block is at the AWS load balancer, before the app, and keys on the redirect host — not the scheme (`http://example.com` passes) and not the `client_id` (an unregistered one returns a helpful `400 JSON`).
452
+ - Refused redirect hosts: `localhost`, `127.0.0.1`, `0.0.0.0`, RFC1918, `169.254.169.254`. Accepted: public hosts and private-use schemes. (`[::1]` and `127.1` also pass — the rule is not exhaustive.)
453
+ - **This hits the Kiro IDE too, not just `kiro-cli`.** Both start a `http://localhost:<port>/oauth/callback` server, so OAuth fails identically on both surfaces. Verified against the installed IDE: with the installer's headers its MCP log shows `[meko] Connected`; remove the headers and change nothing else and it logs `HTTP 403 ... Raw body: <html>403 Forbidden</html>`, then closes the connection. Don't assume a GUI client is safe — what matters is the callback it registers, not whether it has a window.
454
+
455
+ Confirmed on `kiro-cli` 2.18.1 itself. Its DCR registration is:
456
+
457
+ ```json
458
+ {"client_name":"kiro","redirect_uris":["http://127.0.0.1:56459"],"application_type":"native", ...}
459
+ ```
460
+
461
+ so against Cloud Meko it fails with `Dynamic registration failed: Registration failed: HTTP 403 Forbidden`. Its first `POST /mcp` also goes out with **no `User-Agent`**, which trips a second HTML-403 rule independently. With Mode 1 headers and a valid `mko_tkn_…` key it connects and lists all 23 tools — no OAuth, no 403.
462
+
463
+ Both surfaces are verified against Cloud Meko with a real key. On the **Kiro IDE** (1.0.337), the installer's config produces `[meko] Connected` in its `Kiro - MCP Logs` output. On **`kiro-cli`** (2.18.1) the agent enumerates all 23 tools. The steering file is confirmed to reach the model, not just disk: asked with tools disabled what its Meko steering requires, the agent answered that it must call `memory_add` when the user states a durable fact and that this "explicitly overrides the meko-mcp-tools skill", which assumes capture hooks are running.
464
+
465
+ One naming correction worth knowing, since it has caused confusion: **`Q DEV CLI` is not an OAuth client id.** It is the MCP `initialize` handshake's `clientInfo.name` (`"clientInfo":{"name":"Q DEV CLI","version":"1.0.0"}`); the string appears nowhere in the `kiro-cli` binary's OAuth path, and its actual DCR `client_name` is `kiro`. There is nothing named `Q DEV CLI` to allowlist. See [#277](https://github.com/yugabyte/meko-mcp-server/issues/277).
466
+
467
+ Header auth avoids the OAuth path entirely. `installer/test/clients-kiro.test.mjs` guards this.
468
+
469
+ **Two hook generations coexist.** Kiro 2.18.1 CLI 2.x reads lowercase inline hooks from the selected custom agent, while CLI 3.x and IDE 1.x read [standalone PascalCase hooks](https://kiro.dev/docs/hooks/) from `hooks/`. The installer writes both formats and routes them to one handler. It creates a default-like `meko-default` agent for CLI 2.x, records and selects it, and restores the previous `chat.defaultAgent` on uninstall only when `meko-default` is still selected. Existing files at installer-owned names are treated as user-owned collisions and left untouched.
470
+
471
+ The handler captures the exact user prompt exposed by `UserPromptSubmit`/`userPromptSubmit` and the exact final assistant response exposed by `Stop`/`stop`. Kiro does not expose tool calls or hidden reasoning through these events, so those are outside the capture boundary. Inline and standalone hooks may both deliver the same event; prompt hashes and stable turn IDs deduplicate them, including the initial prompt repeated after `agentSpawn`. If another prompt arrives before Stop, the prior turn receives the standard interrupted-response marker.
472
+
473
+ Events are fsynced to a mode-0600 local JSONL journal before delivery. The handler initializes the shared Capture v2 session on spawn/start, launches a non-blocking drain on each Stop, and starts the normal recovery timer. Create/add failures remain queued; acknowledgements advance only through the existing validated Capture v2 protocol. Kiro's `session_id` is preferred; absent that, host PID plus working directory provides correlation. Project ownership is stable as `kiro:<repo>`, and these sessions appear in the normal `--capture-status` health view.
474
+
475
+ IDE workspace hooks run only in trusted workspaces. The installer does not alter Kiro's trust decision. The steering file is therefore conditional: `MEKO_CAPTURE_SESSION_STARTED` means hooks are active and normal capture rules apply; without it, hooks may be disabled or untrusted, or CLI 2.x may be using another agent, so steering asks the agent to persist turns proactively. The file carries a `Managed by meko installer` marker and is removed only when owned by the installer.
476
+
477
+ Uninstall removes only files carrying installer ownership markers: the three standalone hook files, managed agent, private hook environment/runtime, skills, and steering. It preserves user config and filename collisions, and deliberately leaves the Capture v2 event journal and pending outbox state so recovery can finish after a reinstall.
357
478
 
358
479
  ## Cloud vs Local
359
480
 
@@ -384,6 +505,11 @@ The installer auto-detects cloud vs local based on the URL: any URL containing `
384
505
  - **OpenAI Codex (CLI + Desktop App)**
385
506
  - MCP-server bearer reference — `~/.codex/config.toml` stores `bearer_token_env_var = "MEKO_API_KEY"`, **not** the key itself. The runtime env (shell rc or `launchctl setenv`) supplies the value.
386
507
  - Hook commands — Codex's `~/.codex/hooks.json` schema rejects a top-level `env` block and provides no per-command env field, so the installer writes `MEKO_API_KEY` to `~/.codex/meko-env.sh` at **mode 0600** (user read/write only). Each Meko hook's `command` field sources that file before invoking bash, so the key never appears in process arguments — `ps -ef` from another user can't lift it while the hook runs. Same trust boundary as `~/.aws/credentials` or `~/.netrc` (file-system permissions on the user's home directory). To opt out: install for the local Meko URL (no key prompt), or skip Codex hook installation by deleting the Meko entries from `~/.codex/hooks.json` and removing `~/.codex/meko-env.sh` and `~/.codex/meko-hooks/` after install. The hook scripts themselves are copied to `~/.codex/meko-hooks/hooks-handlers/` (a home-dir-stable location) rather than referenced from the shipped package, so `npx`'s cache garbage-collection can't leave the hook pointing at a deleted script (which would fail with `code 127`).
508
+ - **Kiro (IDE + `kiro-cli`)**
509
+ - API key header — stored in `~/.kiro/settings/mcp.json` (or `.kiro/settings/mcp.json` for project scope) under `mcpServers.<name>.headers.Authorization`. The adapter forces the file to **mode 0600** on registration and removal rewrites.
510
+ - Hook runtime — the endpoint and optional API key are also stored in `kiro-hook-env.json` at **mode 0600** so inline and standalone hooks can authenticate without putting the key in their command strings.
511
+ - **OpenCode**
512
+ - The plugin reads the API key from `$OPENCODE_CONFIG_DIR/meko/opencode-hook-env.json` when set, otherwise `~/.config/opencode/meko/opencode-hook-env.json` (user scope), or `.opencode/meko/opencode-hook-env.json` (project scope). The installer writes this file at **mode 0600** and never places the key in process arguments. Sanitized staged snapshots and Capture v2 journal/outbox files are also private; successfully handed-off snapshots are deleted, transient failures remain retryable, and malformed snapshots move to quarantine.
387
513
 
388
514
  **How credentials are protected:**
389
515
 
@@ -396,7 +522,7 @@ The installer auto-detects cloud vs local based on the URL: any URL containing `
396
522
 
397
523
  **Backups:**
398
524
 
399
- Before modifying any config file the installer touches, it creates a `.bak` copy alongside the original (e.g. `~/.claude/settings.json.bak`, `~/.codex/config.toml.bak`, `~/.codex/AGENTS.md.bak`). The merge is additive: existing keys are preserved unless explicitly overridden.
525
+ Adapters that use backup-aware merge helpers create a `.bak` copy alongside the original (for example `~/.claude/settings.json.bak`, `~/.codex/config.toml.bak`, or `~/.codex/AGENTS.md.bak`). The support matrix above lists backup behavior per client. Cursor and Kiro use additive JSON writes but do not create `.bak` files; they preserve unrelated settings and user-owned collisions instead.
400
526
 
401
527
  ## Upgrading
402
528
 
@@ -428,6 +554,7 @@ Re-running the installer is idempotent but not inert. A re-run:
428
554
  - Rewrites the Cursor skill directory and hooks config if `--client cursor` is targeted.
429
555
  - Auto-updates the skills plugin if it's already installed (via `claude plugin update meko-agent-skills@meko-agent-skills`), otherwise installs it. See "Updating the skills plugin" below.
430
556
  - Rebuilds the Claude Desktop `.skill` bundle if `--client claude-desktop` is targeted. You must re-drag the rebuilt bundle into Claude Desktop to pick up the new skill content.
557
+ - Replaces installer-owned OpenCode plugin/runtime/skill assets if `--client opencode` is targeted; restart OpenCode to load them. User-owned collisions are refused rather than overwritten.
431
558
 
432
559
  ### Updating the skills plugin (Claude Code)
433
560
 
@@ -481,13 +608,13 @@ create-meko-setup --version
481
608
 
482
609
  ### Rollback
483
610
 
484
- If an upgrade breaks something, restore the `.bak` backups the installer wrote before its changes:
611
+ For Claude Code, Claude Desktop, or Codex, restore the relevant `.bak` file written before the change. For example, to restore Claude Code settings:
485
612
 
486
613
  ```bash
487
614
  cp ~/.claude/settings.json.bak ~/.claude/settings.json
488
615
  ```
489
616
 
490
- See the Security section above for the full list of backup locations across Claude Code, Claude Desktop, and Cursor.
617
+ See the Security section above for the exact backup locations. Cursor and Kiro do not write `.bak` files; rerun or uninstall the relevant adapter to remove installer-owned entries while preserving unrelated settings and user-owned files.
491
618
 
492
619
  ## Troubleshooting
493
620
 
@@ -530,6 +657,21 @@ Logs worth collecting before filing a bug:
530
657
  - **Claude Code MCP log** (macOS) — `ls -lt ~/Library/Caches/claude-cli-nodejs/*/mcp-logs-meko/ 2>/dev/null` — most recent `*.txt` shows the registration attempt and any stderr from the spawned process.
531
658
  - **Current config snippet** — `grep -A 20 '"meko"' ~/.claude.json` — confirms what state Claude Code was in when the install was attempted.
532
659
 
660
+ ### OpenCode is unsupported or does not load Meko
661
+
662
+ OpenCode integration is gated to **1.18.x**. Check the exact binary that the installer sees:
663
+
664
+ ```bash
665
+ which opencode
666
+ opencode --version
667
+ ```
668
+
669
+ If the version is outside 1.18.x, upgrade or install a certified 1.18.x release before rerunning the installer. If the version is supported but installer validation does not report the remote `mcp.<name>` overlay (`mcp.meko` for the default name), confirm the selected scope and `--name`, check for a user-owned collision at the plugin path, rerun the installer, and restart OpenCode. User scope uses `$OPENCODE_CONFIG_DIR/plugins/meko.js` when the variable is set and `~/.config/opencode/plugins/meko.js` otherwise; project scope uses `.opencode/plugins/meko.js`. Do not print or share `opencode debug config`; its resolved headers may contain the API key.
670
+
671
+ For capture failures, run `create-meko-setup --capture-status --json`. The plugin captures only on `session.idle` or `session.error`, so an actively streaming turn is expected to remain pending. Retained staged snapshots indicate a retryable handoff failure and are replayed at startup; files moved to quarantine were invalid and require inspection. Uninstall leaves staged snapshots in the durable capture root. Do not delete Capture v2 journal/outbox data while diagnosing.
672
+
673
+ For migration failures, run `opencode session list --format json` and `opencode export <sessionID>` directly. One malformed export is isolated to that session. A malformed list records an OpenCode source warning while the other migration sources continue; dry-run output prints the warning.
674
+
533
675
  ### Connection refused
534
676
 
535
677
  ```
@@ -558,10 +700,47 @@ The MCP server is enforcing strict Streamable HTTP compliance: clients must adve
558
700
 
559
701
  ### Authentication failure (401 / 403)
560
702
 
561
- ```
703
+ Check the response shape first — it tells you which layer rejected you, and the
704
+ two causes have nothing to do with each other.
705
+
706
+ **A JSON 401 means the server saw your request and refused the credential.**
707
+
708
+ ```output
562
709
  Server returned 401. Verify your API key is correct and has not expired.
563
710
  ```
564
711
 
712
+ Work through the API-key checks below.
713
+
714
+ **An HTML 403 means a load balancer rejected the request before it reached the
715
+ server.** You'll see an unparseable error, often wrapped in a JSON parse failure
716
+ because the client expected JSON:
717
+
718
+ ```output
719
+ HTTP 403: Invalid OAuth error response: SyntaxError: Unexpected token '<',
720
+ "<html>..." is not valid JSON. Raw body: <html>403 Forbidden</html>
721
+ ```
722
+
723
+ Three known causes:
724
+
725
+ - **No `User-Agent` header.** The WAF refuses any request without one. Add
726
+ `User-Agent: meko-mcp-installer/1.0`. The installer always does; hand-written
727
+ configs and some native MCP clients don't.
728
+ - **An OAuth callback on a loopback address (DEV only).** The DEV WAF refuses
729
+ any `redirect_uri` pointing at an internal host, so dynamic client
730
+ registration fails there for any client that registers such a callback,
731
+ including IDEs. Production accepts these callbacks as of 2026-09-24. Tracked in
732
+ [#277](https://github.com/yugabyte/meko-mcp-server/issues/277).
733
+ - **Message text that looks like a path.** Capture writes containing `../` or
734
+ absolute paths can trip a traversal rule. Tracked in
735
+ [#245](https://github.com/yugabyte/meko-mcp-server/issues/245).
736
+
737
+ **A stale key can surface as the HTML 403, not the JSON 401.** If your key is
738
+ expired, the server returns 401, the client quietly falls back to OAuth, and the
739
+ registration attempt returns the HTML 403. So verify your key before concluding
740
+ that OAuth is broken.
741
+
742
+ API-key checks:
743
+
565
744
  - Confirm the API key at cloud.mekodata.ai > Datapacks > Connect.
566
745
  - If using `$MEKO_API_KEY` env var, ensure it is exported in the current shell.
567
746
  - Confirm the key environment matches the URL: PATs minted on `app.mekodev.com` only validate against `mcp.mekodev.com`; PATs minted on `cloud.mekodata.ai` only validate against `mcp.mekodata.ai`.
@@ -621,7 +800,7 @@ After the installer writes `claude_desktop_config.json`, **quit and relaunch** C
621
800
  ```json
622
801
  "meko": {
623
802
  "command": "npx",
624
- "args": ["-y", "mcp-remote", "<url>", "--transport", "http-only", "--header", "User-Agent:meko-mcp-installer/1.0"]
803
+ "args": ["-y", "mcp-remote@0.1.38", "<url>", "--transport", "http-only", "--header", "User-Agent:meko-mcp-installer/1.0"]
625
804
  }
626
805
  ```
627
806
  If you see a `"url"` field at the top level instead, re-run the installer to rewrite it.
@@ -658,6 +837,19 @@ This reverses installer-managed changes for the selected client:
658
837
 
659
838
  Each step is safe if the component was already removed -- the uninstaller logs a warning and continues.
660
839
 
840
+ For OpenCode, uninstall removes installer-owned plugin, runtime, and skill assets. The private env stays with only `MEKO_WATERMARK_DIR` (API key stripped) so reinstall resumes the same capture root. Staged snapshots, journals, outboxes, and watermarks stay in the durable capture root. Quarantine of snapshots staged there lives next to those files. Invalid files from a leftover `#307` `meko/snapshots` tree stay in `meko/snapshots/quarantine`. Uninstall does not edit `opencode.json` / `opencode.jsonc` or remove user-owned collisions. There is no installer purge command. Do not delete the capture root: it is shared with Claude Code, Cursor, Codex, and Kiro, and also stores datapack pins. Do not delete OpenCode subdirectories on their own: each session also has a `<sessionId>.watermark.json` in that shared root, and it still points at the journal.
841
+
842
+ ### Claude Desktop: which artifact, and why the UI can't remove it
843
+
844
+ Claude Desktop can be connected to Meko in two different ways, and they are removed differently:
845
+
846
+ - **Config-file server** — the `mcpServers.<name>` entry this installer writes into `claude_desktop_config.json`. Claude Desktop shows it with a **"Local dev"** badge; that badge is just Desktop's label for *any* config-file server, **not** a Meko setting. It is **not** removable from the Desktop UI — remove it with the uninstall flow:
847
+ ```bash
848
+ npx @yugabytedb/meko-mcp --client claude-desktop --uninstall
849
+ ```
850
+ This removes the `mcpServers.<name>` entry (via `removeKeys(..., ['mcpServers.<name>'])`). If you installed under a non-default server name, pass `--name <name>`. Note: running bare `npx @yugabytedb/meko-mcp` re-runs the **install** flow and re-adds the entry — always include `--uninstall`. The uninstall strips only the MCP server entry; the imported Desktop **skill** must still be removed manually via **Settings > Extensions**.
851
+ - **OAuth Connector** — added by URL under **Settings > Connectors**. This is UI-managed and **is** removable directly from the Desktop UI; no CLI needed.
852
+
661
853
  Use `--verbose` for detailed output during uninstall:
662
854
 
663
855
  ```bash
@@ -681,12 +873,13 @@ node installer/bin/create-meko-setup.mjs --uninstall --verbose
681
873
  | M11 | Invoke from repo root | `cd <repo> && node installer/bin/create-meko-setup.mjs --client cursor --local --scope user --dry-run --yes` | Dry-run succeeds; no path-resolution errors |
682
874
  | M12 | Invoke from `installer/` | `cd <repo>/installer && node bin/create-meko-setup.mjs --client cursor --local --scope user --dry-run --yes` | Dry-run succeeds (note: `--scope project` gives different `.cursor/...` paths because project scope uses cwd — pick a fixed cwd or `--scope user` to compare byte-for-byte) |
683
875
  | M13 | Clone-free via npm pack | `cd installer && npm pack` → `npm install -g ./yugabytedb-meko-mcp-*.tgz` → from a directory with **no repo clone**, run `create-meko-setup --client cursor --local --scope project --dry-run` | Succeeds; asset paths resolve under the globally-installed `node_modules/@yugabytedb/meko-mcp/assets/...` |
876
+ | M14 | OpenCode Tier-2 canary | Follow the OpenCode checklist in [`RELEASING.md`](../RELEASING.md#opencode-tier-2-canary) on certified 1.18.x | Packaged user/project install, tools visibility, automatic capture/recovery, migration, and uninstall all pass |
684
877
 
685
878
  ## Development
686
879
 
687
880
  ### Prerequisites
688
881
 
689
- - Node.js >= 18.0.0 (required for `util.parseArgs` and `node:test`)
882
+ - Node.js >= 20.18.1 (the Claude Desktop `mcp-remote` bridge's `undici@^7` dependency requires it; `util.parseArgs` and `node:test` are also used)
690
883
  - No `npm install` needed -- zero external dependencies
691
884
 
692
885
  ### Project Structure
@@ -765,7 +958,7 @@ branch.
765
958
  ### Design Principles
766
959
 
767
960
  - **Zero external dependencies** -- Node.js built-ins only (`fs`, `path`, `readline`, `child_process`, `url`, `http`, `https`, `os`, `node:test`, `node:assert`), matching the pattern of `capture.js`.
768
- - **Non-destructive** -- Creates `.bak` backups before modifying settings.
961
+ - **Non-destructive** -- Preserves unrelated settings and user-owned files, and creates `.bak` backups where the client adapter supports them (see the support matrix).
769
962
  - **Additive merges** -- New env vars are added without overwriting existing unrelated keys.
770
963
  - **Graceful failures** -- Non-critical errors (plugin already exists) warn and continue; only missing prerequisites cause hard failures.
771
964
  - **API key safety** -- Never logged, always redacted in output.