@sammorrowdrums/mcpi-ext 0.2.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 (208) hide show
  1. package/dist/code-mode/code-mode-integration.test.d.ts +2 -0
  2. package/dist/code-mode/code-mode-integration.test.d.ts.map +1 -0
  3. package/dist/code-mode/code-mode-integration.test.js +116 -0
  4. package/dist/code-mode/code-mode-integration.test.js.map +1 -0
  5. package/dist/code-mode/eligibility.d.ts +10 -0
  6. package/dist/code-mode/eligibility.d.ts.map +1 -0
  7. package/dist/code-mode/eligibility.js +14 -0
  8. package/dist/code-mode/eligibility.js.map +1 -0
  9. package/dist/code-mode/eligibility.test.d.ts +2 -0
  10. package/dist/code-mode/eligibility.test.d.ts.map +1 -0
  11. package/dist/code-mode/eligibility.test.js +53 -0
  12. package/dist/code-mode/eligibility.test.js.map +1 -0
  13. package/dist/code-mode/executor.d.ts +35 -0
  14. package/dist/code-mode/executor.d.ts.map +1 -0
  15. package/dist/code-mode/executor.js +120 -0
  16. package/dist/code-mode/executor.js.map +1 -0
  17. package/dist/code-mode/executor.test.d.ts +2 -0
  18. package/dist/code-mode/executor.test.d.ts.map +1 -0
  19. package/dist/code-mode/executor.test.js +130 -0
  20. package/dist/code-mode/executor.test.js.map +1 -0
  21. package/dist/code-mode/index.d.ts +65 -0
  22. package/dist/code-mode/index.d.ts.map +1 -0
  23. package/dist/code-mode/index.js +175 -0
  24. package/dist/code-mode/index.js.map +1 -0
  25. package/dist/code-mode/tools.d.ts +45 -0
  26. package/dist/code-mode/tools.d.ts.map +1 -0
  27. package/dist/code-mode/tools.js +70 -0
  28. package/dist/code-mode/tools.js.map +1 -0
  29. package/dist/code-mode/type-hints.d.ts +20 -0
  30. package/dist/code-mode/type-hints.d.ts.map +1 -0
  31. package/dist/code-mode/type-hints.js +189 -0
  32. package/dist/code-mode/type-hints.js.map +1 -0
  33. package/dist/code-mode/type-hints.test.d.ts +2 -0
  34. package/dist/code-mode/type-hints.test.d.ts.map +1 -0
  35. package/dist/code-mode/type-hints.test.js +157 -0
  36. package/dist/code-mode/type-hints.test.js.map +1 -0
  37. package/dist/docker-e2e.d.ts +55 -0
  38. package/dist/docker-e2e.d.ts.map +1 -0
  39. package/dist/docker-e2e.js +235 -0
  40. package/dist/docker-e2e.js.map +1 -0
  41. package/dist/docker-e2e.test.d.ts +2 -0
  42. package/dist/docker-e2e.test.d.ts.map +1 -0
  43. package/dist/docker-e2e.test.js +176 -0
  44. package/dist/docker-e2e.test.js.map +1 -0
  45. package/dist/index.d.ts +3 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +148 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/index.test.d.ts +2 -0
  50. package/dist/index.test.d.ts.map +1 -0
  51. package/dist/index.test.js +8 -0
  52. package/dist/index.test.js.map +1 -0
  53. package/dist/mcp/client-manager.d.ts +46 -0
  54. package/dist/mcp/client-manager.d.ts.map +1 -0
  55. package/dist/mcp/client-manager.js +148 -0
  56. package/dist/mcp/client-manager.js.map +1 -0
  57. package/dist/mcp/client-manager.test.d.ts +2 -0
  58. package/dist/mcp/client-manager.test.d.ts.map +1 -0
  59. package/dist/mcp/client-manager.test.js +204 -0
  60. package/dist/mcp/client-manager.test.js.map +1 -0
  61. package/dist/mcp/config-loader.d.ts +8 -0
  62. package/dist/mcp/config-loader.d.ts.map +1 -0
  63. package/dist/mcp/config-loader.js +43 -0
  64. package/dist/mcp/config-loader.js.map +1 -0
  65. package/dist/mcp/config-loader.test.d.ts +2 -0
  66. package/dist/mcp/config-loader.test.d.ts.map +1 -0
  67. package/dist/mcp/config-loader.test.js +93 -0
  68. package/dist/mcp/config-loader.test.js.map +1 -0
  69. package/dist/mcp/config.d.ts +46 -0
  70. package/dist/mcp/config.d.ts.map +1 -0
  71. package/dist/mcp/config.js +28 -0
  72. package/dist/mcp/config.js.map +1 -0
  73. package/{src/mcp/index.ts → dist/mcp/index.d.ts} +1 -0
  74. package/dist/mcp/index.d.ts.map +1 -0
  75. package/dist/mcp/index.js +4 -0
  76. package/dist/mcp/index.js.map +1 -0
  77. package/dist/skills/discover.d.ts +10 -0
  78. package/dist/skills/discover.d.ts.map +1 -0
  79. package/dist/skills/discover.js +60 -0
  80. package/dist/skills/discover.js.map +1 -0
  81. package/dist/skills/format.d.ts +10 -0
  82. package/dist/skills/format.d.ts.map +1 -0
  83. package/dist/skills/format.js +38 -0
  84. package/dist/skills/format.js.map +1 -0
  85. package/dist/skills/format.test.d.ts +2 -0
  86. package/dist/skills/format.test.d.ts.map +1 -0
  87. package/dist/skills/format.test.js +63 -0
  88. package/dist/skills/format.test.js.map +1 -0
  89. package/{src/skills/index.ts → dist/skills/index.d.ts} +2 -5
  90. package/dist/skills/index.d.ts.map +1 -0
  91. package/dist/skills/index.js +6 -0
  92. package/dist/skills/index.js.map +1 -0
  93. package/dist/skills/load-skill-tool.d.ts +40 -0
  94. package/dist/skills/load-skill-tool.d.ts.map +1 -0
  95. package/dist/skills/load-skill-tool.js +104 -0
  96. package/dist/skills/load-skill-tool.js.map +1 -0
  97. package/dist/skills/mcp-tool-proxy.d.ts +11 -0
  98. package/dist/skills/mcp-tool-proxy.d.ts.map +1 -0
  99. package/dist/skills/mcp-tool-proxy.js +116 -0
  100. package/dist/skills/mcp-tool-proxy.js.map +1 -0
  101. package/dist/skills/skill-integration.test.d.ts +2 -0
  102. package/dist/skills/skill-integration.test.d.ts.map +1 -0
  103. package/dist/skills/skill-integration.test.js +119 -0
  104. package/dist/skills/skill-integration.test.js.map +1 -0
  105. package/dist/skills/skill-registry.d.ts +39 -0
  106. package/dist/skills/skill-registry.d.ts.map +1 -0
  107. package/dist/skills/skill-registry.js +48 -0
  108. package/dist/skills/skill-registry.js.map +1 -0
  109. package/dist/skills/skill-registry.test.d.ts +2 -0
  110. package/dist/skills/skill-registry.test.d.ts.map +1 -0
  111. package/dist/skills/skill-registry.test.js +67 -0
  112. package/dist/skills/skill-registry.test.js.map +1 -0
  113. package/dist/test-servers/weather-server.d.ts +12 -0
  114. package/dist/test-servers/weather-server.d.ts.map +1 -0
  115. package/dist/test-servers/weather-server.js +139 -0
  116. package/dist/test-servers/weather-server.js.map +1 -0
  117. package/dist/test-servers/weather-stdio.d.ts +2 -0
  118. package/dist/test-servers/weather-stdio.d.ts.map +1 -0
  119. package/{src/test-servers/weather-stdio.ts → dist/test-servers/weather-stdio.js} +1 -1
  120. package/dist/test-servers/weather-stdio.js.map +1 -0
  121. package/dist/tool-cli/cli.d.ts +3 -0
  122. package/dist/tool-cli/cli.d.ts.map +1 -0
  123. package/dist/tool-cli/cli.js +179 -0
  124. package/dist/tool-cli/cli.js.map +1 -0
  125. package/dist/tool-cli/constants.d.ts +7 -0
  126. package/dist/tool-cli/constants.d.ts.map +1 -0
  127. package/dist/tool-cli/constants.js +15 -0
  128. package/dist/tool-cli/constants.js.map +1 -0
  129. package/dist/tool-cli/format.d.ts +8 -0
  130. package/dist/tool-cli/format.d.ts.map +1 -0
  131. package/{src/tool-cli/format.ts → dist/tool-cli/format.js} +5 -4
  132. package/dist/tool-cli/format.js.map +1 -0
  133. package/dist/tool-cli/format.test.d.ts +2 -0
  134. package/dist/tool-cli/format.test.d.ts.map +1 -0
  135. package/dist/tool-cli/format.test.js +30 -0
  136. package/dist/tool-cli/format.test.js.map +1 -0
  137. package/{src/tool-cli/index.ts → dist/tool-cli/index.d.ts} +1 -0
  138. package/dist/tool-cli/index.d.ts.map +1 -0
  139. package/dist/tool-cli/index.js +3 -0
  140. package/dist/tool-cli/index.js.map +1 -0
  141. package/dist/tool-cli/rpc-client.d.ts +6 -0
  142. package/dist/tool-cli/rpc-client.d.ts.map +1 -0
  143. package/dist/tool-cli/rpc-client.js +25 -0
  144. package/dist/tool-cli/rpc-client.js.map +1 -0
  145. package/dist/tool-cli/rpc-server.d.ts +48 -0
  146. package/dist/tool-cli/rpc-server.d.ts.map +1 -0
  147. package/dist/tool-cli/rpc-server.js +220 -0
  148. package/dist/tool-cli/rpc-server.js.map +1 -0
  149. package/dist/tool-cli/rpc-server.test.d.ts +2 -0
  150. package/dist/tool-cli/rpc-server.test.d.ts.map +1 -0
  151. package/dist/tool-cli/rpc-server.test.js +154 -0
  152. package/dist/tool-cli/rpc-server.test.js.map +1 -0
  153. package/package.json +5 -1
  154. package/.dockerignore +0 -6
  155. package/.github/dependabot.yml +0 -25
  156. package/.github/workflows/ci.yml +0 -50
  157. package/.hadolint.yaml +0 -6
  158. package/.prettierignore +0 -4
  159. package/.prettierrc +0 -7
  160. package/AGENTS.md +0 -118
  161. package/DECISIONS.md +0 -99
  162. package/Dockerfile +0 -39
  163. package/aube-lock.yaml +0 -4146
  164. package/docs/code-mode.md +0 -38
  165. package/docs/skills.md +0 -57
  166. package/docs/tool-cli.md +0 -54
  167. package/eslint.config.js +0 -27
  168. package/images/banner.webp +0 -0
  169. package/images/code-c-maude.webp +0 -0
  170. package/images/code-mode-histogram.png +0 -0
  171. package/images/nuclear-mcp-football.webp +0 -0
  172. package/images/skills-enabling-mcp-tools.png +0 -0
  173. package/images/the-skill-dealer.webp +0 -0
  174. package/images/tool-cli-grep.png +0 -0
  175. package/mise.toml +0 -53
  176. package/scripts/docker-entrypoint.sh +0 -29
  177. package/scripts/test-echo-server.mjs +0 -28
  178. package/scripts/test-mcp-integration.mjs +0 -59
  179. package/scripts/test-tool-cli-smoke.mjs +0 -93
  180. package/src/code-mode/code-mode-integration.test.ts +0 -135
  181. package/src/code-mode/eligibility.test.ts +0 -60
  182. package/src/code-mode/eligibility.ts +0 -16
  183. package/src/code-mode/executor.test.ts +0 -168
  184. package/src/code-mode/executor.ts +0 -163
  185. package/src/code-mode/index.ts +0 -208
  186. package/src/code-mode/tools.ts +0 -110
  187. package/src/code-mode/type-hints.test.ts +0 -187
  188. package/src/code-mode/type-hints.ts +0 -249
  189. package/src/docker-e2e.test.ts +0 -195
  190. package/src/docker-e2e.ts +0 -277
  191. package/src/index.test.ts +0 -8
  192. package/src/index.ts +0 -179
  193. package/src/mcp/client-manager.test.ts +0 -250
  194. package/src/mcp/client-manager.ts +0 -199
  195. package/src/mcp/config-loader.test.ts +0 -115
  196. package/src/mcp/config-loader.ts +0 -46
  197. package/src/mcp/config.ts +0 -40
  198. package/src/skills/discover.ts +0 -81
  199. package/src/skills/format.test.ts +0 -70
  200. package/src/skills/format.ts +0 -42
  201. package/src/skills/load-skill-tool.ts +0 -136
  202. package/src/skills/mcp-tool-proxy.ts +0 -143
  203. package/src/skills/skill-integration.test.ts +0 -139
  204. package/src/skills/skill-registry.test.ts +0 -83
  205. package/src/skills/skill-registry.ts +0 -69
  206. package/src/test-servers/weather-server.ts +0 -171
  207. package/src/tool-cli/format.test.ts +0 -33
  208. package/tsconfig.json +0 -21
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"]