@sammorrowdrums/mcpi-ext 0.1.0 → 0.2.1

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 (68) hide show
  1. package/README.md +109 -139
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +23 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/tool-cli/index.d.ts +2 -2
  6. package/dist/tool-cli/index.d.ts.map +1 -1
  7. package/dist/tool-cli/index.js +1 -2
  8. package/dist/tool-cli/index.js.map +1 -1
  9. package/package.json +9 -7
  10. package/.dockerignore +0 -6
  11. package/.github/dependabot.yml +0 -25
  12. package/.github/workflows/ci.yml +0 -50
  13. package/.hadolint.yaml +0 -6
  14. package/.prettierignore +0 -4
  15. package/.prettierrc +0 -7
  16. package/AGENTS.md +0 -118
  17. package/DECISIONS.md +0 -99
  18. package/Dockerfile +0 -39
  19. package/aube-lock.yaml +0 -4146
  20. package/eslint.config.js +0 -27
  21. package/images/banner.webp +0 -0
  22. package/images/code-c-maude.webp +0 -0
  23. package/images/nuclear-mcp-football.webp +0 -0
  24. package/images/the-skill-dealer.webp +0 -0
  25. package/mise.toml +0 -65
  26. package/scripts/docker-entrypoint.sh +0 -29
  27. package/scripts/test-echo-server.mjs +0 -28
  28. package/scripts/test-mcp-integration.mjs +0 -59
  29. package/scripts/test-tool-cli-smoke.mjs +0 -62
  30. package/src/code-mode/code-mode-integration.test.ts +0 -135
  31. package/src/code-mode/eligibility.test.ts +0 -60
  32. package/src/code-mode/eligibility.ts +0 -16
  33. package/src/code-mode/executor.test.ts +0 -168
  34. package/src/code-mode/executor.ts +0 -163
  35. package/src/code-mode/index.ts +0 -208
  36. package/src/code-mode/tools.ts +0 -110
  37. package/src/code-mode/type-hints.test.ts +0 -187
  38. package/src/code-mode/type-hints.ts +0 -249
  39. package/src/docker-e2e.test.ts +0 -195
  40. package/src/docker-e2e.ts +0 -277
  41. package/src/index.test.ts +0 -8
  42. package/src/index.ts +0 -158
  43. package/src/mcp/client-manager.test.ts +0 -250
  44. package/src/mcp/client-manager.ts +0 -199
  45. package/src/mcp/config-loader.test.ts +0 -115
  46. package/src/mcp/config-loader.ts +0 -46
  47. package/src/mcp/config.ts +0 -40
  48. package/src/mcp/index.ts +0 -4
  49. package/src/skills/discover.ts +0 -81
  50. package/src/skills/format.test.ts +0 -70
  51. package/src/skills/format.ts +0 -42
  52. package/src/skills/index.ts +0 -9
  53. package/src/skills/load-skill-tool.ts +0 -136
  54. package/src/skills/mcp-tool-proxy.ts +0 -143
  55. package/src/skills/skill-integration.test.ts +0 -139
  56. package/src/skills/skill-registry.test.ts +0 -83
  57. package/src/skills/skill-registry.ts +0 -69
  58. package/src/test-servers/weather-server.ts +0 -171
  59. package/src/test-servers/weather-stdio.ts +0 -11
  60. package/src/tool-cli/cli.ts +0 -232
  61. package/src/tool-cli/constants.ts +0 -15
  62. package/src/tool-cli/format.test.ts +0 -33
  63. package/src/tool-cli/format.ts +0 -56
  64. package/src/tool-cli/index.ts +0 -3
  65. package/src/tool-cli/rpc-client.ts +0 -39
  66. package/src/tool-cli/rpc-server.test.ts +0 -193
  67. package/src/tool-cli/rpc-server.ts +0 -287
  68. package/tsconfig.json +0 -21
package/AGENTS.md DELETED
@@ -1,118 +0,0 @@
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 DELETED
@@ -1,99 +0,0 @@
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 DELETED
@@ -1,39 +0,0 @@
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"]