@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.
- package/.dockerignore +6 -0
- package/.github/dependabot.yml +25 -0
- package/.github/workflows/ci.yml +50 -0
- package/.hadolint.yaml +6 -0
- package/.prettierignore +4 -0
- package/.prettierrc +7 -0
- package/AGENTS.md +118 -0
- package/DECISIONS.md +99 -0
- package/Dockerfile +39 -0
- package/README.md +278 -0
- package/aube-lock.yaml +4146 -0
- package/dist/code-mode/code-mode-integration.test.d.ts +2 -0
- package/dist/code-mode/code-mode-integration.test.d.ts.map +1 -0
- package/dist/code-mode/code-mode-integration.test.js +116 -0
- package/dist/code-mode/code-mode-integration.test.js.map +1 -0
- package/dist/code-mode/eligibility.d.ts +10 -0
- package/dist/code-mode/eligibility.d.ts.map +1 -0
- package/dist/code-mode/eligibility.js +14 -0
- package/dist/code-mode/eligibility.js.map +1 -0
- package/dist/code-mode/eligibility.test.d.ts +2 -0
- package/dist/code-mode/eligibility.test.d.ts.map +1 -0
- package/dist/code-mode/eligibility.test.js +53 -0
- package/dist/code-mode/eligibility.test.js.map +1 -0
- package/dist/code-mode/executor.d.ts +35 -0
- package/dist/code-mode/executor.d.ts.map +1 -0
- package/dist/code-mode/executor.js +120 -0
- package/dist/code-mode/executor.js.map +1 -0
- package/dist/code-mode/executor.test.d.ts +2 -0
- package/dist/code-mode/executor.test.d.ts.map +1 -0
- package/dist/code-mode/executor.test.js +130 -0
- package/dist/code-mode/executor.test.js.map +1 -0
- package/dist/code-mode/index.d.ts +65 -0
- package/dist/code-mode/index.d.ts.map +1 -0
- package/dist/code-mode/index.js +175 -0
- package/dist/code-mode/index.js.map +1 -0
- package/dist/code-mode/tools.d.ts +45 -0
- package/dist/code-mode/tools.d.ts.map +1 -0
- package/dist/code-mode/tools.js +70 -0
- package/dist/code-mode/tools.js.map +1 -0
- package/dist/code-mode/type-hints.d.ts +20 -0
- package/dist/code-mode/type-hints.d.ts.map +1 -0
- package/dist/code-mode/type-hints.js +189 -0
- package/dist/code-mode/type-hints.js.map +1 -0
- package/dist/code-mode/type-hints.test.d.ts +2 -0
- package/dist/code-mode/type-hints.test.d.ts.map +1 -0
- package/dist/code-mode/type-hints.test.js +157 -0
- package/dist/code-mode/type-hints.test.js.map +1 -0
- package/dist/docker-e2e.d.ts +55 -0
- package/dist/docker-e2e.d.ts.map +1 -0
- package/dist/docker-e2e.js +235 -0
- package/dist/docker-e2e.js.map +1 -0
- package/dist/docker-e2e.test.d.ts +2 -0
- package/dist/docker-e2e.test.d.ts.map +1 -0
- package/dist/docker-e2e.test.js +176 -0
- package/dist/docker-e2e.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +8 -0
- package/dist/index.test.js.map +1 -0
- package/dist/mcp/client-manager.d.ts +46 -0
- package/dist/mcp/client-manager.d.ts.map +1 -0
- package/dist/mcp/client-manager.js +148 -0
- package/dist/mcp/client-manager.js.map +1 -0
- package/dist/mcp/client-manager.test.d.ts +2 -0
- package/dist/mcp/client-manager.test.d.ts.map +1 -0
- package/dist/mcp/client-manager.test.js +204 -0
- package/dist/mcp/client-manager.test.js.map +1 -0
- package/dist/mcp/config-loader.d.ts +8 -0
- package/dist/mcp/config-loader.d.ts.map +1 -0
- package/dist/mcp/config-loader.js +43 -0
- package/dist/mcp/config-loader.js.map +1 -0
- package/dist/mcp/config-loader.test.d.ts +2 -0
- package/dist/mcp/config-loader.test.d.ts.map +1 -0
- package/dist/mcp/config-loader.test.js +93 -0
- package/dist/mcp/config-loader.test.js.map +1 -0
- package/dist/mcp/config.d.ts +46 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +28 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +4 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/skills/discover.d.ts +10 -0
- package/dist/skills/discover.d.ts.map +1 -0
- package/dist/skills/discover.js +60 -0
- package/dist/skills/discover.js.map +1 -0
- package/dist/skills/format.d.ts +10 -0
- package/dist/skills/format.d.ts.map +1 -0
- package/dist/skills/format.js +38 -0
- package/dist/skills/format.js.map +1 -0
- package/dist/skills/format.test.d.ts +2 -0
- package/dist/skills/format.test.d.ts.map +1 -0
- package/dist/skills/format.test.js +63 -0
- package/dist/skills/format.test.js.map +1 -0
- package/dist/skills/index.d.ts +6 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +6 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/load-skill-tool.d.ts +40 -0
- package/dist/skills/load-skill-tool.d.ts.map +1 -0
- package/dist/skills/load-skill-tool.js +104 -0
- package/dist/skills/load-skill-tool.js.map +1 -0
- package/dist/skills/mcp-tool-proxy.d.ts +11 -0
- package/dist/skills/mcp-tool-proxy.d.ts.map +1 -0
- package/dist/skills/mcp-tool-proxy.js +116 -0
- package/dist/skills/mcp-tool-proxy.js.map +1 -0
- package/dist/skills/skill-integration.test.d.ts +2 -0
- package/dist/skills/skill-integration.test.d.ts.map +1 -0
- package/dist/skills/skill-integration.test.js +119 -0
- package/dist/skills/skill-integration.test.js.map +1 -0
- package/dist/skills/skill-registry.d.ts +39 -0
- package/dist/skills/skill-registry.d.ts.map +1 -0
- package/dist/skills/skill-registry.js +48 -0
- package/dist/skills/skill-registry.js.map +1 -0
- package/dist/skills/skill-registry.test.d.ts +2 -0
- package/dist/skills/skill-registry.test.d.ts.map +1 -0
- package/dist/skills/skill-registry.test.js +67 -0
- package/dist/skills/skill-registry.test.js.map +1 -0
- package/dist/test-servers/weather-server.d.ts +12 -0
- package/dist/test-servers/weather-server.d.ts.map +1 -0
- package/dist/test-servers/weather-server.js +139 -0
- package/dist/test-servers/weather-server.js.map +1 -0
- package/dist/test-servers/weather-stdio.d.ts +2 -0
- package/dist/test-servers/weather-stdio.d.ts.map +1 -0
- package/dist/test-servers/weather-stdio.js +11 -0
- package/dist/test-servers/weather-stdio.js.map +1 -0
- package/dist/tool-cli/cli.d.ts +3 -0
- package/dist/tool-cli/cli.d.ts.map +1 -0
- package/dist/tool-cli/cli.js +179 -0
- package/dist/tool-cli/cli.js.map +1 -0
- package/dist/tool-cli/constants.d.ts +7 -0
- package/dist/tool-cli/constants.d.ts.map +1 -0
- package/dist/tool-cli/constants.js +15 -0
- package/dist/tool-cli/constants.js.map +1 -0
- package/dist/tool-cli/format.d.ts +8 -0
- package/dist/tool-cli/format.d.ts.map +1 -0
- package/dist/tool-cli/format.js +57 -0
- package/dist/tool-cli/format.js.map +1 -0
- package/dist/tool-cli/format.test.d.ts +2 -0
- package/dist/tool-cli/format.test.d.ts.map +1 -0
- package/dist/tool-cli/format.test.js +30 -0
- package/dist/tool-cli/format.test.js.map +1 -0
- package/dist/tool-cli/index.d.ts +4 -0
- package/dist/tool-cli/index.d.ts.map +1 -0
- package/dist/tool-cli/index.js +4 -0
- package/dist/tool-cli/index.js.map +1 -0
- package/dist/tool-cli/rpc-client.d.ts +6 -0
- package/dist/tool-cli/rpc-client.d.ts.map +1 -0
- package/dist/tool-cli/rpc-client.js +25 -0
- package/dist/tool-cli/rpc-client.js.map +1 -0
- package/dist/tool-cli/rpc-server.d.ts +48 -0
- package/dist/tool-cli/rpc-server.d.ts.map +1 -0
- package/dist/tool-cli/rpc-server.js +220 -0
- package/dist/tool-cli/rpc-server.js.map +1 -0
- package/dist/tool-cli/rpc-server.test.d.ts +2 -0
- package/dist/tool-cli/rpc-server.test.d.ts.map +1 -0
- package/dist/tool-cli/rpc-server.test.js +154 -0
- package/dist/tool-cli/rpc-server.test.js.map +1 -0
- package/eslint.config.js +27 -0
- package/images/banner.webp +0 -0
- package/images/code-c-maude.webp +0 -0
- package/images/nuclear-mcp-football.webp +0 -0
- package/images/the-skill-dealer.webp +0 -0
- package/mise.toml +65 -0
- package/package.json +46 -0
- package/scripts/docker-entrypoint.sh +29 -0
- package/scripts/test-echo-server.mjs +28 -0
- package/scripts/test-mcp-integration.mjs +59 -0
- package/scripts/test-tool-cli-smoke.mjs +62 -0
- package/src/code-mode/code-mode-integration.test.ts +135 -0
- package/src/code-mode/eligibility.test.ts +60 -0
- package/src/code-mode/eligibility.ts +16 -0
- package/src/code-mode/executor.test.ts +168 -0
- package/src/code-mode/executor.ts +163 -0
- package/src/code-mode/index.ts +208 -0
- package/src/code-mode/tools.ts +110 -0
- package/src/code-mode/type-hints.test.ts +187 -0
- package/src/code-mode/type-hints.ts +249 -0
- package/src/docker-e2e.test.ts +195 -0
- package/src/docker-e2e.ts +277 -0
- package/src/index.test.ts +8 -0
- package/src/index.ts +158 -0
- package/src/mcp/client-manager.test.ts +250 -0
- package/src/mcp/client-manager.ts +199 -0
- package/src/mcp/config-loader.test.ts +115 -0
- package/src/mcp/config-loader.ts +46 -0
- package/src/mcp/config.ts +40 -0
- package/src/mcp/index.ts +4 -0
- package/src/skills/discover.ts +81 -0
- package/src/skills/format.test.ts +70 -0
- package/src/skills/format.ts +42 -0
- package/src/skills/index.ts +9 -0
- package/src/skills/load-skill-tool.ts +136 -0
- package/src/skills/mcp-tool-proxy.ts +143 -0
- package/src/skills/skill-integration.test.ts +139 -0
- package/src/skills/skill-registry.test.ts +83 -0
- package/src/skills/skill-registry.ts +69 -0
- package/src/test-servers/weather-server.ts +171 -0
- package/src/test-servers/weather-stdio.ts +11 -0
- package/src/tool-cli/cli.ts +232 -0
- package/src/tool-cli/constants.ts +15 -0
- package/src/tool-cli/format.test.ts +33 -0
- package/src/tool-cli/format.ts +56 -0
- package/src/tool-cli/index.ts +3 -0
- package/src/tool-cli/rpc-client.ts +39 -0
- package/src/tool-cli/rpc-server.test.ts +193 -0
- package/src/tool-cli/rpc-server.ts +287 -0
- package/tsconfig.json +21 -0
package/.dockerignore
ADDED
|
@@ -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
package/.prettierignore
ADDED
package/.prettierrc
ADDED
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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 <server> <tool> '{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
|
+

|
|
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.
|