@sammorrowdrums/mcpi-ext 0.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 (213) hide show
  1. package/.dockerignore +6 -0
  2. package/.github/dependabot.yml +25 -0
  3. package/.github/workflows/ci.yml +50 -0
  4. package/.hadolint.yaml +6 -0
  5. package/.prettierignore +4 -0
  6. package/.prettierrc +7 -0
  7. package/AGENTS.md +118 -0
  8. package/DECISIONS.md +99 -0
  9. package/Dockerfile +39 -0
  10. package/README.md +278 -0
  11. package/aube-lock.yaml +4146 -0
  12. package/dist/code-mode/code-mode-integration.test.d.ts +2 -0
  13. package/dist/code-mode/code-mode-integration.test.d.ts.map +1 -0
  14. package/dist/code-mode/code-mode-integration.test.js +116 -0
  15. package/dist/code-mode/code-mode-integration.test.js.map +1 -0
  16. package/dist/code-mode/eligibility.d.ts +10 -0
  17. package/dist/code-mode/eligibility.d.ts.map +1 -0
  18. package/dist/code-mode/eligibility.js +14 -0
  19. package/dist/code-mode/eligibility.js.map +1 -0
  20. package/dist/code-mode/eligibility.test.d.ts +2 -0
  21. package/dist/code-mode/eligibility.test.d.ts.map +1 -0
  22. package/dist/code-mode/eligibility.test.js +53 -0
  23. package/dist/code-mode/eligibility.test.js.map +1 -0
  24. package/dist/code-mode/executor.d.ts +35 -0
  25. package/dist/code-mode/executor.d.ts.map +1 -0
  26. package/dist/code-mode/executor.js +120 -0
  27. package/dist/code-mode/executor.js.map +1 -0
  28. package/dist/code-mode/executor.test.d.ts +2 -0
  29. package/dist/code-mode/executor.test.d.ts.map +1 -0
  30. package/dist/code-mode/executor.test.js +130 -0
  31. package/dist/code-mode/executor.test.js.map +1 -0
  32. package/dist/code-mode/index.d.ts +65 -0
  33. package/dist/code-mode/index.d.ts.map +1 -0
  34. package/dist/code-mode/index.js +175 -0
  35. package/dist/code-mode/index.js.map +1 -0
  36. package/dist/code-mode/tools.d.ts +45 -0
  37. package/dist/code-mode/tools.d.ts.map +1 -0
  38. package/dist/code-mode/tools.js +70 -0
  39. package/dist/code-mode/tools.js.map +1 -0
  40. package/dist/code-mode/type-hints.d.ts +20 -0
  41. package/dist/code-mode/type-hints.d.ts.map +1 -0
  42. package/dist/code-mode/type-hints.js +189 -0
  43. package/dist/code-mode/type-hints.js.map +1 -0
  44. package/dist/code-mode/type-hints.test.d.ts +2 -0
  45. package/dist/code-mode/type-hints.test.d.ts.map +1 -0
  46. package/dist/code-mode/type-hints.test.js +157 -0
  47. package/dist/code-mode/type-hints.test.js.map +1 -0
  48. package/dist/docker-e2e.d.ts +55 -0
  49. package/dist/docker-e2e.d.ts.map +1 -0
  50. package/dist/docker-e2e.js +235 -0
  51. package/dist/docker-e2e.js.map +1 -0
  52. package/dist/docker-e2e.test.d.ts +2 -0
  53. package/dist/docker-e2e.test.d.ts.map +1 -0
  54. package/dist/docker-e2e.test.js +176 -0
  55. package/dist/docker-e2e.test.js.map +1 -0
  56. package/dist/index.d.ts +3 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +128 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/index.test.d.ts +2 -0
  61. package/dist/index.test.d.ts.map +1 -0
  62. package/dist/index.test.js +8 -0
  63. package/dist/index.test.js.map +1 -0
  64. package/dist/mcp/client-manager.d.ts +46 -0
  65. package/dist/mcp/client-manager.d.ts.map +1 -0
  66. package/dist/mcp/client-manager.js +148 -0
  67. package/dist/mcp/client-manager.js.map +1 -0
  68. package/dist/mcp/client-manager.test.d.ts +2 -0
  69. package/dist/mcp/client-manager.test.d.ts.map +1 -0
  70. package/dist/mcp/client-manager.test.js +204 -0
  71. package/dist/mcp/client-manager.test.js.map +1 -0
  72. package/dist/mcp/config-loader.d.ts +8 -0
  73. package/dist/mcp/config-loader.d.ts.map +1 -0
  74. package/dist/mcp/config-loader.js +43 -0
  75. package/dist/mcp/config-loader.js.map +1 -0
  76. package/dist/mcp/config-loader.test.d.ts +2 -0
  77. package/dist/mcp/config-loader.test.d.ts.map +1 -0
  78. package/dist/mcp/config-loader.test.js +93 -0
  79. package/dist/mcp/config-loader.test.js.map +1 -0
  80. package/dist/mcp/config.d.ts +46 -0
  81. package/dist/mcp/config.d.ts.map +1 -0
  82. package/dist/mcp/config.js +28 -0
  83. package/dist/mcp/config.js.map +1 -0
  84. package/dist/mcp/index.d.ts +5 -0
  85. package/dist/mcp/index.d.ts.map +1 -0
  86. package/dist/mcp/index.js +4 -0
  87. package/dist/mcp/index.js.map +1 -0
  88. package/dist/skills/discover.d.ts +10 -0
  89. package/dist/skills/discover.d.ts.map +1 -0
  90. package/dist/skills/discover.js +60 -0
  91. package/dist/skills/discover.js.map +1 -0
  92. package/dist/skills/format.d.ts +10 -0
  93. package/dist/skills/format.d.ts.map +1 -0
  94. package/dist/skills/format.js +38 -0
  95. package/dist/skills/format.js.map +1 -0
  96. package/dist/skills/format.test.d.ts +2 -0
  97. package/dist/skills/format.test.d.ts.map +1 -0
  98. package/dist/skills/format.test.js +63 -0
  99. package/dist/skills/format.test.js.map +1 -0
  100. package/dist/skills/index.d.ts +6 -0
  101. package/dist/skills/index.d.ts.map +1 -0
  102. package/dist/skills/index.js +6 -0
  103. package/dist/skills/index.js.map +1 -0
  104. package/dist/skills/load-skill-tool.d.ts +40 -0
  105. package/dist/skills/load-skill-tool.d.ts.map +1 -0
  106. package/dist/skills/load-skill-tool.js +104 -0
  107. package/dist/skills/load-skill-tool.js.map +1 -0
  108. package/dist/skills/mcp-tool-proxy.d.ts +11 -0
  109. package/dist/skills/mcp-tool-proxy.d.ts.map +1 -0
  110. package/dist/skills/mcp-tool-proxy.js +116 -0
  111. package/dist/skills/mcp-tool-proxy.js.map +1 -0
  112. package/dist/skills/skill-integration.test.d.ts +2 -0
  113. package/dist/skills/skill-integration.test.d.ts.map +1 -0
  114. package/dist/skills/skill-integration.test.js +119 -0
  115. package/dist/skills/skill-integration.test.js.map +1 -0
  116. package/dist/skills/skill-registry.d.ts +39 -0
  117. package/dist/skills/skill-registry.d.ts.map +1 -0
  118. package/dist/skills/skill-registry.js +48 -0
  119. package/dist/skills/skill-registry.js.map +1 -0
  120. package/dist/skills/skill-registry.test.d.ts +2 -0
  121. package/dist/skills/skill-registry.test.d.ts.map +1 -0
  122. package/dist/skills/skill-registry.test.js +67 -0
  123. package/dist/skills/skill-registry.test.js.map +1 -0
  124. package/dist/test-servers/weather-server.d.ts +12 -0
  125. package/dist/test-servers/weather-server.d.ts.map +1 -0
  126. package/dist/test-servers/weather-server.js +139 -0
  127. package/dist/test-servers/weather-server.js.map +1 -0
  128. package/dist/test-servers/weather-stdio.d.ts +2 -0
  129. package/dist/test-servers/weather-stdio.d.ts.map +1 -0
  130. package/dist/test-servers/weather-stdio.js +11 -0
  131. package/dist/test-servers/weather-stdio.js.map +1 -0
  132. package/dist/tool-cli/cli.d.ts +3 -0
  133. package/dist/tool-cli/cli.d.ts.map +1 -0
  134. package/dist/tool-cli/cli.js +179 -0
  135. package/dist/tool-cli/cli.js.map +1 -0
  136. package/dist/tool-cli/constants.d.ts +7 -0
  137. package/dist/tool-cli/constants.d.ts.map +1 -0
  138. package/dist/tool-cli/constants.js +15 -0
  139. package/dist/tool-cli/constants.js.map +1 -0
  140. package/dist/tool-cli/format.d.ts +8 -0
  141. package/dist/tool-cli/format.d.ts.map +1 -0
  142. package/dist/tool-cli/format.js +57 -0
  143. package/dist/tool-cli/format.js.map +1 -0
  144. package/dist/tool-cli/format.test.d.ts +2 -0
  145. package/dist/tool-cli/format.test.d.ts.map +1 -0
  146. package/dist/tool-cli/format.test.js +30 -0
  147. package/dist/tool-cli/format.test.js.map +1 -0
  148. package/dist/tool-cli/index.d.ts +4 -0
  149. package/dist/tool-cli/index.d.ts.map +1 -0
  150. package/dist/tool-cli/index.js +4 -0
  151. package/dist/tool-cli/index.js.map +1 -0
  152. package/dist/tool-cli/rpc-client.d.ts +6 -0
  153. package/dist/tool-cli/rpc-client.d.ts.map +1 -0
  154. package/dist/tool-cli/rpc-client.js +25 -0
  155. package/dist/tool-cli/rpc-client.js.map +1 -0
  156. package/dist/tool-cli/rpc-server.d.ts +48 -0
  157. package/dist/tool-cli/rpc-server.d.ts.map +1 -0
  158. package/dist/tool-cli/rpc-server.js +220 -0
  159. package/dist/tool-cli/rpc-server.js.map +1 -0
  160. package/dist/tool-cli/rpc-server.test.d.ts +2 -0
  161. package/dist/tool-cli/rpc-server.test.d.ts.map +1 -0
  162. package/dist/tool-cli/rpc-server.test.js +154 -0
  163. package/dist/tool-cli/rpc-server.test.js.map +1 -0
  164. package/eslint.config.js +27 -0
  165. package/images/banner.webp +0 -0
  166. package/images/code-c-maude.webp +0 -0
  167. package/images/nuclear-mcp-football.webp +0 -0
  168. package/images/the-skill-dealer.webp +0 -0
  169. package/mise.toml +65 -0
  170. package/package.json +46 -0
  171. package/scripts/docker-entrypoint.sh +29 -0
  172. package/scripts/test-echo-server.mjs +28 -0
  173. package/scripts/test-mcp-integration.mjs +59 -0
  174. package/scripts/test-tool-cli-smoke.mjs +62 -0
  175. package/src/code-mode/code-mode-integration.test.ts +135 -0
  176. package/src/code-mode/eligibility.test.ts +60 -0
  177. package/src/code-mode/eligibility.ts +16 -0
  178. package/src/code-mode/executor.test.ts +168 -0
  179. package/src/code-mode/executor.ts +163 -0
  180. package/src/code-mode/index.ts +208 -0
  181. package/src/code-mode/tools.ts +110 -0
  182. package/src/code-mode/type-hints.test.ts +187 -0
  183. package/src/code-mode/type-hints.ts +249 -0
  184. package/src/docker-e2e.test.ts +195 -0
  185. package/src/docker-e2e.ts +277 -0
  186. package/src/index.test.ts +8 -0
  187. package/src/index.ts +158 -0
  188. package/src/mcp/client-manager.test.ts +250 -0
  189. package/src/mcp/client-manager.ts +199 -0
  190. package/src/mcp/config-loader.test.ts +115 -0
  191. package/src/mcp/config-loader.ts +46 -0
  192. package/src/mcp/config.ts +40 -0
  193. package/src/mcp/index.ts +4 -0
  194. package/src/skills/discover.ts +81 -0
  195. package/src/skills/format.test.ts +70 -0
  196. package/src/skills/format.ts +42 -0
  197. package/src/skills/index.ts +9 -0
  198. package/src/skills/load-skill-tool.ts +136 -0
  199. package/src/skills/mcp-tool-proxy.ts +143 -0
  200. package/src/skills/skill-integration.test.ts +139 -0
  201. package/src/skills/skill-registry.test.ts +83 -0
  202. package/src/skills/skill-registry.ts +69 -0
  203. package/src/test-servers/weather-server.ts +171 -0
  204. package/src/test-servers/weather-stdio.ts +11 -0
  205. package/src/tool-cli/cli.ts +232 -0
  206. package/src/tool-cli/constants.ts +15 -0
  207. package/src/tool-cli/format.test.ts +33 -0
  208. package/src/tool-cli/format.ts +56 -0
  209. package/src/tool-cli/index.ts +3 -0
  210. package/src/tool-cli/rpc-client.ts +39 -0
  211. package/src/tool-cli/rpc-server.test.ts +193 -0
  212. package/src/tool-cli/rpc-server.ts +287 -0
  213. package/tsconfig.json +21 -0
package/.dockerignore ADDED
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ dist
3
+ .git
4
+ .gitignore
5
+ *.md
6
+ .pi
@@ -0,0 +1,25 @@
1
+ version: 2
2
+
3
+ updates:
4
+ - package-ecosystem: npm
5
+ directory: /
6
+ schedule:
7
+ interval: weekly
8
+ open-pull-requests-limit: 10
9
+ groups:
10
+ dev-dependencies:
11
+ dependency-type: development
12
+ update-types:
13
+ - minor
14
+ - patch
15
+ production-dependencies:
16
+ dependency-type: production
17
+ update-types:
18
+ - minor
19
+ - patch
20
+
21
+ - package-ecosystem: github-actions
22
+ directory: /
23
+ schedule:
24
+ interval: weekly
25
+ open-pull-requests-limit: 5
@@ -0,0 +1,50 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ci-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ permissions:
14
+ contents: read
15
+ models: read
16
+
17
+ jobs:
18
+ ci:
19
+ name: Lint, Build & Test
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v6
23
+
24
+ - uses: jdx/mise-action@v4
25
+
26
+ - name: Install dependencies
27
+ run: npm install
28
+
29
+ - name: Lint
30
+ run: mise run lint
31
+
32
+ - name: Lint Dockerfile
33
+ uses: hadolint/hadolint-action@v3.1.0
34
+ with:
35
+ dockerfile: Dockerfile
36
+
37
+ - name: Check formatting
38
+ run: mise run format:check
39
+
40
+ - name: Type-check
41
+ run: mise run check
42
+
43
+ - name: Build
44
+ run: mise run build
45
+
46
+ - name: Test
47
+ run: mise run test
48
+
49
+ - name: MCP integration smoke test
50
+ run: mise run test:integration
package/.hadolint.yaml ADDED
@@ -0,0 +1,6 @@
1
+ # hadolint configuration
2
+ # DL3008: Pin versions in apt-get — not practical for a dev/e2e image
3
+ # SC1091: Can't follow sourced file (/etc/os-release) — false positive
4
+ ignored:
5
+ - DL3008
6
+ - SC1091
@@ -0,0 +1,4 @@
1
+ dist/
2
+ node_modules/
3
+ aube-lock.yaml
4
+ *.tsbuildinfo
package/.prettierrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": false,
4
+ "trailingComma": "all",
5
+ "printWidth": 100,
6
+ "tabWidth": 2
7
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,118 @@
1
+ # AGENTS.md
2
+
3
+ ## Tooling
4
+
5
+ This project uses **mise** for tool version management and **npm** for package management. If you are an AI agent or new contributor, read this first.
6
+
7
+ ### mise (tool version manager)
8
+
9
+ - **What:** [mise](https://mise.jdx.dev/) manages tool versions (node) and project tasks. Think asdf + direnv + make in one tool.
10
+ - **Why:** Reproducible dev environments. `mise.toml` pins exact tool versions so every contributor and CI run uses the same stack.
11
+ - **Install:** `curl https://mise.run | sh` then activate: `eval "$(~/.local/bin/mise activate bash)"`
12
+ - **Usage:**
13
+ - `mise install` — install all tools from `mise.toml`
14
+ - `mise run <task>` — run a project task (build, test, dev, check, start)
15
+ - `mise use <tool>@<version>` — add/update a tool version
16
+
17
+ ### mcpi (coding agent)
18
+
19
+ - **What:** [mcpi](https://github.com/SamMorrowDrums/mcpi) is an extensible terminal coding agent (fork of pi). This project builds extensions for it.
20
+ - **Extension pattern:** Export a default function receiving `ExtensionAPI`, register tools/commands/hooks.
21
+ - **Load extension:** `mcpi --extension ./dist/index.js`
22
+
23
+ ## Dev Loop
24
+
25
+ ```sh
26
+ mise install # one-time: install node
27
+ npm install # install npm dependencies
28
+ mise run build # compile TypeScript
29
+ mise run test # run tests
30
+ mise run dev # watch mode for development
31
+ mise run check # type-check only
32
+ mise run lint # lint TypeScript files
33
+ mise run format # auto-format code with Prettier
34
+ mise run format:check # check formatting (CI mode)
35
+ ```
36
+
37
+ ## Project Structure
38
+
39
+ ```
40
+ src/
41
+ index.ts Extension entry point (lifecycle hooks, wiring)
42
+ mcp/ MCP client management (connections, tool discovery)
43
+ skills/ Skill registry, discovery, gating, tool proxies
44
+ tool-cli/ tool-cli RPC server, client, CLI binary, prompt
45
+ test-servers/ Test MCP servers (weather, echo)
46
+ dist/ Compiled output (gitignored)
47
+ scripts/ Integration and smoke test scripts
48
+ mise.toml Tool versions and tasks
49
+ package.json Dependencies and npm scripts
50
+ tsconfig.json TypeScript configuration
51
+ ```
52
+
53
+ ## Architecture
54
+
55
+ ### Tiered MCP Tool Access
56
+
57
+ The extension provides three tiers for exposing MCP tools to the agent:
58
+
59
+ | Tier | Mechanism | When Used |
60
+ | ------------- | ------------------------------------------------------------------ | ------------------------------------------------ |
61
+ | 1 — Skills | `deferred: true` + `tool_call` gate → tools unlocked by load_skill | MCP server ships skills |
62
+ | 2 — tool-cli | CLI progressive discovery via shell | Ad-hoc exploration, no skills |
63
+ | 3 — Code Mode | search+execute, no HITL | Read-only tools with structured output (planned) |
64
+
65
+ ### tool-cli Architecture
66
+
67
+ tool-cli is a thin CLI binary that communicates with the extension via JSON-RPC 2.0 over HTTP. The agent uses it as a standard shell command, composable with pipes, grep, jq, loops, etc.
68
+
69
+ ```
70
+ Agent (mcpi)
71
+
72
+ │ shell exec
73
+
74
+ tool-cli <server> <tool> '{"args"}'
75
+
76
+ │ HTTP JSON-RPC (localhost:7179)
77
+
78
+ ToolCliRpcServer (in extension process)
79
+
80
+ │ MCP protocol (stdio/HTTP)
81
+
82
+ MCP Server(s)
83
+ ```
84
+
85
+ **Key design points:**
86
+
87
+ - **No auth (temporary)** — the RPC server binds to `127.0.0.1` only, limiting access to the local machine. This is acceptable for development but not a finished security posture — any local process can call the server and execute MCP tools. Future work should add a shared secret or token (e.g. passed via environment variable to the CLI) so only the intended agent process can make calls.
88
+ - **Interception point for HITL** — the RPC server's `callTool` method is the single choke point for all tool execution. Future work can check tool annotations (`readOnlyHint`, `destructiveHint`) here and gate non-read-only calls through user confirmation before forwarding to the MCP server.
89
+ - **Progressive discovery** — the agent discovers servers → tools → schemas incrementally, paying only the tokens it needs.
90
+ - **Shell-native** — plain text output composes with grep, jq, xargs, pipes, loops. The agent can chain tool calls using standard bash idioms.
91
+
92
+ ## Code Quality
93
+
94
+ - **ESLint** — flat config with `typescript-eslint` (strict + stylistic) and Prettier compat
95
+ - **Prettier** — auto-formatting (double quotes, semicolons, trailing commas, 100 char width)
96
+ - **CI** — GitHub Actions runs lint → format:check → type-check → build → test on every PR
97
+ - **Dependabot** — automated dependency updates (npm + GitHub Actions)
98
+
99
+ Run `mise run lint` and `mise run format` before committing. CI will reject PRs that fail any check.
100
+
101
+ **Before pushing**, always run:
102
+
103
+ ```sh
104
+ mise run lint # lint must pass
105
+ mise run test # tests must pass
106
+ mise run format:check # formatting must pass
107
+ ```
108
+
109
+ ## Decision Log
110
+
111
+ We maintain a [DECISIONS.md](DECISIONS.md) file recording key architectural and design decisions. Keep it up to date when making significant choices — add a new numbered entry with date, context, decision, and rationale.
112
+
113
+ ## Conventions
114
+
115
+ - ESM (`"type": "module"`)
116
+ - Target: ES2022, module: Node16
117
+ - Strict TypeScript
118
+ - Tests with vitest (co-located `*.test.ts` files)
package/DECISIONS.md ADDED
@@ -0,0 +1,99 @@
1
+ # Decision Log
2
+
3
+ Record of key architectural and design decisions. Keep this up to date as decisions are made.
4
+
5
+ ## 001 — Extension, not fork
6
+
7
+ **Date:** 2026-04-23
8
+ **Context:** Should we fork pi to build the MCP agent harness, or use the extension API?
9
+ **Decision:** Build as a pi extension. The extension API provides everything we need: `registerTool()`, `setActiveTools()`, `on("tool_result", ...)`, `exec()`, lifecycle hooks.
10
+ **Rationale:** Everything in scope (skill-gated tool visibility, football CLI, code mode, output offloading) is policy and orchestration — deciding _when_ and _how_ to expose MCP tools to the model. That's extension territory. We'd only need to fork if we needed to change pi's tool dispatch, context assembly, or model loop.
11
+
12
+ ## 002 — Agent harness controls tool visibility, not MCP servers
13
+
14
+ **Date:** 2026-04-23
15
+ **Context:** How do MCP tools become visible to the model?
16
+ **Decision:** The harness (pi extension) decides what tools the model sees. MCP servers just expose their tools and optionally their skills. The harness holds all discovered tools internally and only sends them to the model when a skill names them.
17
+ **Rationale:** Maximal agent capability, minimal tokens. The model's context window isn't stuffed with every tool from every connected MCP server. Tools appear only when a skill provides the context for using them.
18
+
19
+ ## 003 — Tiered access model
20
+
21
+ **Date:** 2026-04-23
22
+ **Context:** How should the model access MCP tools?
23
+ **Decision:** Three tiers of access, all complementary:
24
+
25
+ | Tier | Mechanism | When Used |
26
+ | ------------------ | ------------------------------------------------------------ | -------------------------------------- |
27
+ | 1 — Skills (#1) | Skill loaded → `allowed-tools` exact-matched → tools visible | MCP server ships skills |
28
+ | 2 — Football (#2) | CLI progressive discovery → HITL for writes | Ad-hoc exploration, no skills |
29
+ | 3 — Code Mode (#4) | search+execute → no HITL | Read-only tools with structured output |
30
+
31
+ **Rationale:** Different situations call for different access patterns. Skills give direct access with workflow knowledge. Football gives interactive access with safety. Code mode gives autonomous access to safe operations at scale.
32
+
33
+ ## 004 — Large tool output offloading
34
+
35
+ **Date:** 2026-04-23
36
+ **Context:** Tool responses can be thousands of tokens, wasting context window.
37
+ **Decision:** Intercept tool results via `pi.on("tool_result", ...)`. If output exceeds ~500 tokens, write to a file and return a pointer to the model.
38
+ **Rationale:** Controls output token cost the same way skills/football/code-mode control input token cost. The model can read the file if it needs the content.
39
+
40
+ ## 005 — MCP SDK and JSON config for server connections
41
+
42
+ **Date:** 2026-04-23
43
+ **Context:** How should the extension connect to MCP servers?
44
+ **Decision:** Use `@modelcontextprotocol/sdk` (TypeScript MCP SDK) with a JSON config file at `~/.config/mcpi-ext/mcp.json` (overridable via `--mcp-config` flag). The config supports two transport types: `stdio` (spawns a child process) and `remote` (Streamable HTTP). A `McpClientManager` class connects to all configured servers on `session_start`, discovers tools via `tools/list`, handles `notifications/tools/list_changed`, and disconnects on `session_shutdown`.
45
+ **Rationale:** The official MCP SDK is the canonical way to implement MCP clients. JSON config aligns with VS Code and Claude Code conventions for MCP server configuration. Supporting both stdio and remote covers local dev servers and cloud-hosted MCP endpoints. Tools are discovered and stored internally but NOT registered with pi — the access tiers (Skills #1, Football #2, Code Mode #4) decide when to expose tools to the model.
46
+
47
+ ## 006 — CI model access via GITHUB_TOKEN
48
+
49
+ **Date:** 2026-04-23
50
+ **Context:** Can Pi's `github-copilot` provider use the Actions `GITHUB_TOKEN` for model inference in CI?
51
+ **Decision:** Yes. The GitHub Models API (GA since April 2025) grants model inference to the Actions `GITHUB_TOKEN` when the workflow declares `permissions: models: read`. Pi's `--provider github-copilot` uses this same API. The CI workflow declares this permission so future e2e tests can run Pi with model access without a PAT.
52
+ **Rationale:** Using the built-in `GITHUB_TOKEN` avoids storing secrets for CI model access. The `models: read` scope is the minimum required — no write access needed. This enables full trajectory e2e tests in CI (connect to MCP servers, run Pi agent, verify results).
53
+
54
+ ## 007 — Custom skill registry for MCP skills, not Pi's native skill system
55
+
56
+ **Date:** 2026-04-23
57
+ **Context:** Should MCP-discovered skills use Pi's built-in `loadSkills`/`formatSkillsForPrompt` pipeline (writing SKILL.md files to disk) or a custom in-extension registry?
58
+ **Decision:** Custom `SkillRegistry` + `load_skill` tool + `formatMcpSkillsForPrompt`, styled after Pi's native skill system but fully self-contained in the extension. Skills are discovered from MCP `skill://` resources and injected into the system prompt via the `before_agent_start` hook.
59
+ **Rationale:** MCP skills live on remote servers, not on disk. Writing them to temp files would be fragile and unnecessary. The custom approach keeps MCP skills self-contained, gives us full control over the activation → tool gating flow, and avoids coupling to Pi's internal skill loader. The XML format matches Pi's `<available_skills>` pattern so models already know how to interact with it.
60
+
61
+ ## 008 — Cache-safe progressive tool disclosure via `deferred` flag
62
+
63
+ **Date:** 2026-05-11
64
+ **Context:** When `load_skill` called `setActiveTools()` to reveal new tools, the tools array sent to the model changed, invalidating prompt cache. Decision 008 previously accepted this trade-off.
65
+ **Decision:** Use `deferred: true` on MCP tool proxies with provider-native support and extension-level gating:
66
+
67
+ 1. **Anthropic:** pi-mono maps `deferred: true` to `defer_loading: true` in the API payload. Deferred tools stay in the tools array but are hidden from the model's view. Optional `tool_reference` content blocks can explicitly enable them on demand.
68
+ 2. **OpenAI Responses:** pi-mono maps `deferred: true` to `defer_loading: true` and auto-injects `{"type": "tool_search"}` into the tools array. The model discovers deferred tools automatically via hosted server-side search — no explicit activation needed. (OpenAI's client-executed `tool_search_output` is the equivalent of Anthropic's `tool_reference`, but hosted search is sufficient for our use case.)
69
+ 3. **All providers (fallback):** The extension's `tool_call` hook blocks premature calls to gated tools and returns an error message naming the relevant skill. After `load_skill` fires, tools are marked as enabled and calls go through.
70
+
71
+ **Rationale:** Both Anthropic and OpenAI natively support `defer_loading` (tested with Claude Opus 4.7 and GPT-5.4). The tools array and system prompt stay constant throughout the conversation — prompt cache is fully preserved. The `tool_call` hook provides a provider-agnostic enforcement layer for providers without native `defer_loading` support.
72
+
73
+ ## 009 — tool-cli uses JSON-RPC 2.0 over HTTP on a predefined port
74
+
75
+ **Date:** 2026-04-24
76
+ **Context:** The Football CLI (issue #2) needs a communication protocol between the thin CLI binary and the extension that manages MCP connections.
77
+ **Decision:** JSON-RPC 2.0 over HTTP on `localhost:7179` (overridable via `TOOL_CLI_PORT` env var). The RPC server lives in the extension, started on `session_start` and stopped on `session_shutdown`. The CLI binary (`tool-cli`) is a thin client that uses `fetch` to call the server. No new dependencies — uses Node's `http` module for the server and global `fetch` for the client.
78
+ **Rationale:** JSON-RPC 2.0 is a standard, simple protocol that maps cleanly to the four operations needed (listServers, listTools, describeTool, callTool). HTTP is the simplest transport for request/response. A predefined port avoids coordination complexity for now. The architecture supports future HITL confirmation for non-read-only tools — the RPC server's `callTool` method is the single choke point where tool annotations can be checked and destructive calls gated through user approval.
79
+
80
+ ## 010 — tool-cli has no auth (known limitation)
81
+
82
+ **Date:** 2026-04-24
83
+ **Context:** The tool-cli RPC server listens on `127.0.0.1:7179` with no authentication. Any local process can discover and execute MCP tools through it.
84
+ **Decision:** Accept this for now as a development convenience. The server is localhost-only, which limits the blast radius to local processes, but this is not a finished security posture. Before production use, add a shared secret (e.g. a one-time token passed via environment variable from the extension to the CLI) so only the intended agent process can make calls.
85
+ **Rationale:** Adding auth now would complicate the initial implementation without changing the threat model much — the agent already has shell access and could call MCP tools through other means. But as the tool matures and especially once HITL gating is added for destructive tools, unauthenticated access would let other local processes bypass those safety checks. Auth is a prerequisite for trustworthy HITL.
86
+
87
+ ## 011 — Code mode uses `isolated-vm` for sandbox execution
88
+
89
+ **Date:** 2026-04-24
90
+ **Context:** Code mode (#4) lets the model write JavaScript to chain read-only MCP tool calls. The generated code runs in a sandbox. Options evaluated: Node `vm` module, `isolated-vm`, Deno subprocess, Cloudflare workerd, Pydantic/Python subprocess, WASM.
91
+ **Decision:** Use `isolated-vm` (V8 isolates in Node.js). Provides memory limits (128MB default), CPU timeouts (30s default), and V8-level isolation. Tool dispatch via `Reference` async callbacks — actual MCP calls execute on the host, never in the sandbox.
92
+ **Rationale:** Code mode has no HITL (human-in-the-loop) since all tools are read-only, making sandbox security important. Node's `vm` module is documented as "not a security mechanism" and is escapable via prototype pollution. `isolated-vm` provides genuine V8-level isolation with ~15ms overhead — negligible vs MCP network I/O. Deno subprocess (400ms/call) and workerd are too slow or complex for interactive use.
93
+
94
+ ## 012 — Code mode uses `ctx.eval` instead of `compileModule` for execution
95
+
96
+ **Date:** 2026-04-24
97
+ **Context:** Initial implementation used `isolate.compileModule()` + `module.evaluate()` for running sandboxed code with top-level await. Discovery: `module.evaluate()` resolves prematurely when multiple sequential `Reference.apply()` calls use `{ result: { promise: true } }` — the module evaluation promise resolves after the first async reference call, not after all code completes.
98
+ **Decision:** Use `ctx.eval()` with `{ promise: true, copy: true }` instead. Wrap user code in an async IIFE that returns the final result.
99
+ **Rationale:** `ctx.eval` with `promise: true` correctly awaits the full async IIFE, including all sequential tool dispatch calls. This is critical for code mode's chaining use case where the model writes for-loops calling multiple tools sequentially.
package/Dockerfile ADDED
@@ -0,0 +1,39 @@
1
+ FROM node:22-slim
2
+
3
+ SHELL ["/bin/bash", "-o", "pipefail", "-c"]
4
+
5
+ RUN apt-get update && apt-get install -y --no-install-recommends \
6
+ git \
7
+ curl \
8
+ ripgrep \
9
+ ca-certificates \
10
+ gnupg \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ # Docker CLI (for Docker-in-Docker via mounted socket)
14
+ RUN install -m 0755 -d /etc/apt/keyrings \
15
+ && curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
16
+ && chmod a+r /etc/apt/keyrings/docker.gpg \
17
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
18
+ https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
19
+ > /etc/apt/sources.list.d/docker.list \
20
+ && apt-get update \
21
+ && apt-get install -y --no-install-recommends docker-ce-cli \
22
+ && rm -rf /var/lib/apt/lists/*
23
+
24
+ # uv (Python package manager — for running Python-based MCP servers)
25
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
26
+
27
+ WORKDIR /app
28
+
29
+ COPY package.json ./
30
+ RUN npm install --ignore-scripts
31
+
32
+ COPY tsconfig.json ./
33
+ COPY src ./src
34
+ RUN npx tsc
35
+
36
+ COPY scripts/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
37
+ RUN chmod +x /usr/local/bin/docker-entrypoint.sh
38
+
39
+ ENTRYPOINT ["docker-entrypoint.sh"]
package/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # mcpi-ext
2
+
3
+ > **Experimental.** This extension implements progressive MCP tool discovery via skills for [mcpi](https://github.com/SamMorrowDrums/mcpi) (an experimental pi fork). Please only use this to try out the experiment on skills over MCP. See [progressive tool discovery docs](https://github.com/SamMorrowDrums/mcpi/blob/main/docs/progressive-tool-discovery.md) for details.
4
+
5
+ ![Three figures in a dark, Sandman-esque realm — The Skill Dealer, The Nuclear Football, and Codey C. Maude — standing before swirling constellations of MCP tool connections](images/banner.webp)
6
+
7
+ > _They will tell you that MCP has a context problem. That the protocol gives too many tools, that the model drowns in schemas it doesn't need, that the cost of knowing everything is losing the ability to do anything well._
8
+ >
9
+ > _They are wrong._
10
+ >
11
+ > _MCP doesn't have a context problem. It has an imagination problem. The protocol already contains everything you need — `skill://` resources, tool annotations, `outputSchema`, progressive discovery. The pieces are all there, lying in the open like runes on a hillside. You just have to read them._
12
+ >
13
+ > _What follows is the story of three who did._
14
+
15
+ ---
16
+
17
+ Building custom [MCP](https://modelcontextprotocol.io/) support as [mcpi](https://github.com/SamMorrowDrums/mcpi) extensions. This project implements **tiered progressive discovery** — three complementary strategies for exposing MCP tools to an AI agent, each paying only the context tokens it needs.
18
+
19
+ | Tier | Aspect | Mechanism |
20
+ | ------------- | ------------------------ | ----------------------------------------------------- |
21
+ | 1 — Skills | **The Skill Dealer** | `skill://` resources gate tools via `allowed-tools` |
22
+ | 2 — tool-cli | **The Nuclear Football** | CLI progressive discovery via shell |
23
+ | 3 — Code Mode | **Codey C. Maude** | Sandboxed JS over read-only tools with `outputSchema` |
24
+
25
+ ---
26
+
27
+ ## I. The Skill Dealer
28
+
29
+ ![A shadowy figure behind a table of glowing cards, each card inscribed with the name of an MCP tool](images/the-skill-dealer.webp)
30
+
31
+ > _The Skill Dealer does not give you what you ask for. The Skill Dealer gives you what you need — and nothing more._
32
+
33
+ MCP servers can ship `skill://` resources: SKILL.md files with frontmatter declaring which tools a skill gates. On connection, the extension discovers all skills and registers their tools with `deferred: true` — present in pi's tool registry but excluded from both the tools array sent to the model and the system prompt.
34
+
35
+ This approach is **cache-preserving**: the tools array and system prompt stay constant throughout the conversation, so prompt cache is never invalidated by skill activation.
36
+
37
+ ### How deferred tool gating works
38
+
39
+ Three mechanisms work together:
40
+
41
+ 1. **`deferred: true`** — MCP tool proxies are registered with this flag. Pi's runtime keeps them in the internal registry for execution dispatch (via `resolveTool`) but excludes them from the tools array and system prompt sent to the model.
42
+
43
+ 2. **Provider-native `defer_loading`** — Pi's providers map `deferred: true` to the native API parameter. Both Anthropic and OpenAI support this (tested with Claude Opus 4.7 and GPT-5.4). On Anthropic, `defer_loading` keeps the tool in the grammar but hidden from the model's view — the skill body naming the tools is sufficient for the model to call them. On OpenAI Responses, pi-mono auto-injects `{"type": "tool_search"}` and the model searches/loads deferred tools server-side.
44
+
45
+ 3. **`tool_call` hook gating** — The extension registers a `tool_call` event handler that blocks premature calls to skill-gated tools. If the model tries to call a gated tool before loading its skill, the handler returns an error: _"Tool X requires loading a skill first. Call load_skill with: Y"_. This creates a natural feedback loop and serves as the provider-agnostic enforcement layer.
46
+
47
+ When the model invokes `load_skill`:
48
+
49
+ 1. The skill's SKILL.md is read from the MCP server and returned as workflow instructions
50
+ 2. The skill's `allowedTools` are added to the `enabledTools` set, unblocking the `tool_call` gate
51
+ 3. The model can now call the tools — it discovers them from the skill body (which names them) and the provider's grammar
52
+
53
+ ```mermaid
54
+ sequenceDiagram
55
+ participant Model
56
+ participant load_skill
57
+ participant SkillRegistry
58
+ participant MCP Server
59
+ participant tool_call gate
60
+
61
+ Model->>load_skill: load_skill("github-pr")
62
+ load_skill->>SkillRegistry: Look up skill
63
+ SkillRegistry-->>load_skill: skill metadata + allowed-tools
64
+ load_skill->>MCP Server: Read skill://github-pr
65
+ MCP Server-->>load_skill: SKILL.md body
66
+ load_skill->>tool_call gate: Enable allowed-tools
67
+ load_skill-->>Model: Return workflow instructions
68
+ Note over Model: Skill body names the tools.<br/>Model calls them via tool_use.
69
+ Model->>tool_call gate: create_pull_request(...)
70
+ tool_call gate-->>Model: ✓ Allowed (skill loaded)
71
+ ```
72
+
73
+ This is self-referential enablement: **the MCP server itself declares how its tools should be discovered**. The harness holds all the tools as deferred. The skill decides which ones the model can access. The model gets instructions in one atomic operation, paying only the tokens for the skills it actually loads — and the prompt cache stays intact.
74
+
75
+ The context window stays clean. The tools appear exactly when the model has the context to use them well. And prompt cache is preserved because neither the tools array nor the system prompt changes.
76
+
77
+ Anthropic's [tool search](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) solves a similar problem from the model side — deferring tool loading to avoid cache invalidation from large tool lists. Our approach uses Anthropic's native `defer_loading` parameter (when available) combined with extension-level `tool_call` gating for provider-agnostic safety. Where tool search has the model _pull_ tools on demand, skill invocation _pushes_ them: when `load_skill` fires, the skill's tools are unblocked and the model gets workflow instructions. The model doesn't search for tools — the right tools arrive because the skill declared them.
78
+
79
+ > _"What you do not need to know," said the Skill Dealer, shuffling the deck, "you will not be burdened with knowing."_
80
+
81
+ ---
82
+
83
+ ## II. The Nuclear Football
84
+
85
+ ![A glowing briefcase marked 'tool-cli' being passed between hands in a dark corridor, trailing sparks of shell commands](images/nuclear-mcp-football.webp)
86
+
87
+ > _The Football is not a weapon. The Football is the authority to use weapons. Whoever holds it can reach any server, call any tool, chain any result — but they must do so deliberately, one command at a time._
88
+
89
+ `tool-cli` is a thin CLI binary that speaks JSON-RPC 2.0 to the extension over HTTP. The agent uses it like any shell command — composable with pipes, grep, jq, loops, and all the bash idioms it already knows.
90
+
91
+ ```mermaid
92
+ flowchart TD
93
+ A["Agent (mcpi)"] -->|shell exec| B["tool-cli &lt;server&gt; &lt;tool&gt; '{args}'"]
94
+ B -->|"HTTP JSON-RPC (localhost:7179)"| C["ToolCliRpcServer (in extension)"]
95
+ C -->|"MCP protocol (stdio/HTTP)"| D["MCP Server(s)"]
96
+ ```
97
+
98
+ Discovery is **progressive** — the agent pays only the tokens it needs:
99
+
100
+ ```sh
101
+ tool-cli --help # What servers exist?
102
+ tool-cli github # What tools does this server have?
103
+ tool-cli github search_code # What's the schema for this tool?
104
+ tool-cli github search_code '{"query":"auth"}' # Call it
105
+ ```
106
+
107
+ And because it's shell-native, the agent gets bash superpowers for free:
108
+
109
+ ```sh
110
+ # Chain tool calls
111
+ tool-cli myserver list_items '{}' | jq -r '.[0].id' | \
112
+ xargs -I{} tool-cli myserver get_item '{"id":"{}"}'
113
+
114
+ # Process collections
115
+ for city in London Tokyo Paris; do
116
+ echo "=== $city ==="
117
+ tool-cli weather check_weather '{"city":"'"$city"'"}'
118
+ done
119
+
120
+ # Combine with the Unix toolbox
121
+ tool-cli myserver export_csv '{"table":"users"}' | sort -t, -k2 | head -20
122
+ ```
123
+
124
+ The RPC server is the single choke point for all tool execution — the natural interception point for human-in-the-loop confirmation on destructive operations.
125
+
126
+ > _They pass the Football from hand to hand. It is heavy with potential. Every tool on every server is one command away — but you must type the command yourself._
127
+
128
+ ---
129
+
130
+ ## III. Codey C. Maude
131
+
132
+ ![A luminous figure composed of flowing code, sitting cross-legged in a V8 isolate bubble, reading structured data from floating JSON schemas](images/code-c-maude.webp)
133
+
134
+ > _Codey does not ask permission. Codey does not need to. Everything Codey touches is read-only, every result is typed, and the sandbox cannot be escaped. Codey is safe by construction._
135
+
136
+ Code Mode is for the tools that are **read-only** (`annotations.readOnlyHint === true`) and return **structured output** (`outputSchema` defined). These two properties together make a tool safe for autonomous use — it can't modify anything, and its results are machine-parseable.
137
+
138
+ The model writes JavaScript that chains these tools:
139
+
140
+ ```javascript
141
+ // Executed in a V8 isolate via isolated-vm
142
+ const issues = await codemode.list_issues({ repo: "owner/repo", state: "open" });
143
+ const critical = issues.filter((i) => i.labels.includes("critical"));
144
+ const details = await Promise.all(critical.map((i) => codemode.get_issue({ number: i.number })));
145
+ return details.map((d) => ({ title: d.title, assignee: d.assignee }));
146
+ ```
147
+
148
+ The sandbox runs in `isolated-vm` — genuine V8-level isolation:
149
+
150
+ - **128MB memory limit**, 30-second timeout
151
+ - **No access** to filesystem, network, or Node.js APIs
152
+ - Tool calls dispatch to the host via `Reference` callbacks — MCP execution happens outside the sandbox
153
+ - ~15ms overhead, negligible vs network I/O
154
+
155
+ Two tools expose this to the model:
156
+
157
+ | Tool | Purpose |
158
+ | -------------- | ---------------------------------------------------------------------------------- |
159
+ | `code_search` | Discover available tools — `codemode.listTools()`, `codemode.describeTools(names)` |
160
+ | `code_execute` | Chain tool calls — write JS that calls `codemode.toolName(args)` |
161
+
162
+ > _"I can see everything," Codey said, eyes reflecting infinite JSON. "I just can't touch it. That's the point. That's why they trust me."_
163
+
164
+ ---
165
+
166
+ ## The Architecture
167
+
168
+ The three tiers are complementary. Skills give curated access with workflow knowledge. The Football gives interactive access with safety. Code Mode gives autonomous access to safe operations at scale.
169
+
170
+ ```mermaid
171
+ flowchart TD
172
+ subgraph mcpi["mcpi (agent)"]
173
+ T1["load_skill\n(Tier 1 — Skills)"]
174
+ T2["tool-cli\n(Tier 2 — Football)"]
175
+ T3["code_search / code_execute\n(Tier 3 — Code Mode)"]
176
+ MCM["McpClientManager\n(MCP SDK — stdio & Streamable HTTP)"]
177
+ T1 --> MCM
178
+ T2 --> MCM
179
+ T3 --> MCM
180
+ end
181
+ MCM --> S1["MCP Server"]
182
+ MCM --> S2["MCP Server"]
183
+ MCM --> S3["MCP Server"]
184
+ ```
185
+
186
+ The harness controls what the model sees. MCP servers just expose their tools and skills. The extension decides _when_ and _how_ to reveal them.
187
+
188
+ > _MCP doesn't have a context problem. It never did. It was just waiting for someone to imagine the right way to read the runes._
189
+
190
+ ---
191
+
192
+ ## Quick Start
193
+
194
+ ### Install from npm
195
+
196
+ ```sh
197
+ npm install @sammorrowdrums/mcpi @sammorrowdrums/mcpi-ext
198
+ ```
199
+
200
+ Run mcpi with the extension:
201
+
202
+ ```sh
203
+ npx mcpi --extension node_modules/@sammorrowdrums/mcpi-ext/dist/index.js \
204
+ --mcp-config ~/.config/mcpi-ext/mcp.json
205
+ ```
206
+
207
+ ### Local development
208
+
209
+ ```sh
210
+ curl https://mise.run | sh # install mise
211
+ eval "$(~/.local/bin/mise activate bash)" # activate
212
+ mise install # install node
213
+ npm install # install dependencies
214
+ mise run build # build
215
+ mise run test # test
216
+ ```
217
+
218
+ For local dev, override the `@sammorrowdrums/mcpi` dependency to point at your local mcpi clone:
219
+
220
+ ```json
221
+ // package.json overrides (not committed)
222
+ "devDependencies": {
223
+ "@sammorrowdrums/mcpi": "file:../path/to/mcpi/packages/coding-agent"
224
+ }
225
+ ```
226
+
227
+ Then load the extension from the built output:
228
+
229
+ ```sh
230
+ mcpi --extension ./dist/index.js --mcp-config ~/.config/mcpi-ext/mcp.json
231
+ ```
232
+
233
+ ### MCP server configuration
234
+
235
+ Create `~/.config/mcpi-ext/mcp.json` (or pass `--mcp-config /path/to/config.json`):
236
+
237
+ ```json
238
+ {
239
+ "mcpServers": {
240
+ "github": {
241
+ "type": "stdio",
242
+ "command": "npx",
243
+ "args": ["-y", "@modelcontextprotocol/server-github"],
244
+ "env": {
245
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "xxx"
246
+ }
247
+ },
248
+ "my-remote-server": {
249
+ "type": "remote",
250
+ "url": "https://my-mcp-server.example.com/mcp",
251
+ "headers": {
252
+ "Authorization": "Bearer xxx"
253
+ }
254
+ }
255
+ }
256
+ }
257
+ ```
258
+
259
+ Each server can be `stdio` (spawns a child process) or `remote` (Streamable HTTP). The extension connects to all configured servers on startup, discovers their tools, and gates them via skills.
260
+
261
+ See [AGENTS.md](AGENTS.md) for full tooling docs, dev loop, and architecture details.
262
+
263
+ ## Project Structure
264
+
265
+ ```
266
+ src/
267
+ index.ts Extension entry point (lifecycle hooks, wiring)
268
+ mcp/ MCP client management (connections, tool discovery)
269
+ skills/ Skill registry, discovery, gating, tool proxies
270
+ tool-cli/ tool-cli RPC server, client, CLI binary, prompt
271
+ code-mode/ V8 sandbox executor, eligibility, type hints
272
+ test-servers/ Test MCP servers (weather, echo)
273
+ images/ Banner and character art
274
+ ```
275
+
276
+ ## License
277
+
278
+ See repository for license details.