@njuptlzf/mcp-adapter 2.29.0-0.0.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 (131) hide show
  1. package/CHANGELOG.md +1205 -0
  2. package/LICENSE +21 -0
  3. package/MAPPING.md +73 -0
  4. package/README.md +564 -0
  5. package/abort.ts +34 -0
  6. package/adapters/pi-renderer.ts +7 -0
  7. package/adapters/protocol-elicitation-forwarder.ts +164 -0
  8. package/adapters/protocol-sampling-forwarder.ts +108 -0
  9. package/adapters/universal-host.ts +213 -0
  10. package/agent-dir.ts +68 -0
  11. package/agent-plugin-loader.ts +377 -0
  12. package/app-bridge.bundle.js +67 -0
  13. package/banner.png +0 -0
  14. package/bin/mcp-server.ts +282 -0
  15. package/cli.js +392 -0
  16. package/commands.ts +735 -0
  17. package/config.ts +1284 -0
  18. package/consent-manager.ts +64 -0
  19. package/direct-tools.ts +619 -0
  20. package/dist/agent-dir.d.ts +10 -0
  21. package/dist/agent-dir.js +63 -0
  22. package/dist/agent-dir.js.map +1 -0
  23. package/dist/agent-plugin-loader.d.ts +8 -0
  24. package/dist/agent-plugin-loader.js +356 -0
  25. package/dist/agent-plugin-loader.js.map +1 -0
  26. package/dist/config.d.ts +123 -0
  27. package/dist/config.js +1087 -0
  28. package/dist/config.js.map +1 -0
  29. package/dist/metadata-cache.d.ts +19 -0
  30. package/dist/metadata-cache.js +319 -0
  31. package/dist/metadata-cache.js.map +1 -0
  32. package/dist/package-mcp-loader.d.ts +2 -0
  33. package/dist/package-mcp-loader.js +156 -0
  34. package/dist/package-mcp-loader.js.map +1 -0
  35. package/dist/resource-tools.d.ts +1 -0
  36. package/dist/resource-tools.js +15 -0
  37. package/dist/resource-tools.js.map +1 -0
  38. package/dist/types.d.ts +564 -0
  39. package/dist/types.js +287 -0
  40. package/dist/types.js.map +1 -0
  41. package/dist/ui-app-bridge-helpers.d.ts +6 -0
  42. package/dist/ui-app-bridge-helpers.js +37 -0
  43. package/dist/ui-app-bridge-helpers.js.map +1 -0
  44. package/dist/ui-stream-types.d.ts +104 -0
  45. package/dist/ui-stream-types.js +59 -0
  46. package/dist/ui-stream-types.js.map +1 -0
  47. package/dist/ui-tool-visibility.d.ts +4 -0
  48. package/dist/ui-tool-visibility.js +27 -0
  49. package/dist/ui-tool-visibility.js.map +1 -0
  50. package/dist/utils.d.ts +32 -0
  51. package/dist/utils.js +338 -0
  52. package/dist/utils.js.map +1 -0
  53. package/elicitation-handler.ts +348 -0
  54. package/error-signal.ts +21 -0
  55. package/errors.ts +223 -0
  56. package/failure-backoff.ts +23 -0
  57. package/glimpse-ui.ts +80 -0
  58. package/host-html-template.ts +461 -0
  59. package/index.ts +1291 -0
  60. package/init.ts +692 -0
  61. package/interfaces/agent-paths.ts +67 -0
  62. package/interfaces/host-types.ts +102 -0
  63. package/interfaces/sampling.ts +57 -0
  64. package/json-schema-validator.ts +69 -0
  65. package/lifecycle.ts +446 -0
  66. package/logger.ts +169 -0
  67. package/mcp-auth-flow.ts +1026 -0
  68. package/mcp-auth.ts +1000 -0
  69. package/mcp-bearer-store.ts +387 -0
  70. package/mcp-callback-server.ts +534 -0
  71. package/mcp-code.ts +360 -0
  72. package/mcp-keyring-helper.cjs +89 -0
  73. package/mcp-oauth-provider.ts +666 -0
  74. package/mcp-output-guard.ts +496 -0
  75. package/mcp-panel.ts +1044 -0
  76. package/mcp-probe.ts +201 -0
  77. package/mcp-references.ts +368 -0
  78. package/mcp-script-worker.mjs +130 -0
  79. package/mcp-server.mjs +2133 -0
  80. package/mcp-setup-panel.ts +667 -0
  81. package/mcp-status.ts +98 -0
  82. package/mcp-trace.ts +306 -0
  83. package/metadata-cache.ts +382 -0
  84. package/namespace-tools.ts +276 -0
  85. package/npx-resolver.ts +543 -0
  86. package/oauth-handler.ts +30 -0
  87. package/oauth.ts +48 -0
  88. package/onboarding-state.ts +76 -0
  89. package/package-mcp-loader.ts +168 -0
  90. package/package.json +225 -0
  91. package/panel-keys.ts +53 -0
  92. package/prompts.ts +353 -0
  93. package/proxy-modes.ts +1418 -0
  94. package/request-headers-command.ts +330 -0
  95. package/resource-tools.ts +17 -0
  96. package/runtime-owner.ts +91 -0
  97. package/sampling-handler.ts +284 -0
  98. package/search-ranking.ts +278 -0
  99. package/server-manager.ts +1318 -0
  100. package/session-recovery.ts +158 -0
  101. package/skills/README.md +64 -0
  102. package/skills/mcp-adapter/SKILL.md +332 -0
  103. package/skills/mcp-adapter/references/deploy-examples.md +259 -0
  104. package/skills/mcp-adapter/references/deploy.md +89 -0
  105. package/skills/mcp-adapter/references/generate.md +144 -0
  106. package/skills/mcp-adapter/references/resolver.md +105 -0
  107. package/skills/mcp-adapter/references/verify.md +157 -0
  108. package/skills/mcp-scripting/SKILL.md +38 -0
  109. package/skills/resolve-conflicts/SKILL.md +482 -0
  110. package/skills/resolve-conflicts/references/patterns.md +432 -0
  111. package/skills/resolve-conflicts/references/sample-plan.md +96 -0
  112. package/skills/resolve-conflicts/scripts/handle-deleted-modified.sh +183 -0
  113. package/skills/resolve-conflicts/scripts/validate-conflicts.sh +120 -0
  114. package/skills/upstream-merge/SKILL.md +486 -0
  115. package/skills/upstream-merge/references/pi-coupling-markers.md +129 -0
  116. package/skills/upstream-merge/references/special-cases.md +39 -0
  117. package/state.ts +60 -0
  118. package/tool-approval.ts +195 -0
  119. package/tool-metadata.ts +296 -0
  120. package/tool-registrar.ts +253 -0
  121. package/tool-result-renderer.ts +477 -0
  122. package/ts-shape.ts +149 -0
  123. package/types.ts +991 -0
  124. package/ui-app-bridge-helpers.ts +40 -0
  125. package/ui-resource-handler.ts +250 -0
  126. package/ui-server.ts +852 -0
  127. package/ui-session.ts +571 -0
  128. package/ui-stream-types.ts +89 -0
  129. package/ui-tool-visibility.ts +25 -0
  130. package/unix-socket-transport.ts +85 -0
  131. package/utils.ts +382 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nico Bailon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/MAPPING.md ADDED
@@ -0,0 +1,73 @@
1
+ # Universal Host Shim — ExtensionAPI Mapping
2
+
3
+ This file documents how the fork-owned universal MCP-stdio host
4
+ (`adapters/universal-host.ts`) satisfies the upstream Pi `ExtensionAPI`
5
+ surface, so the upstream engine (`index.ts` `installMcpAdapter`) runs
6
+ unchanged against a non-Pi host.
7
+
8
+ > Direction (Phase 1): **fork the host, not the engine.** The retired
9
+ > `AgentAPI` abstraction (`interfaces/agent-api.ts` / `adapters/pi-adapter.ts`)
10
+ > is gone. The sole adapter surface is now `UniversalMcpHost`, which
11
+ > impersonates `ExtensionAPI` directly; `bin/mcp-server.ts` casts to
12
+ > `ExtensionAPI` at the boundary. The shared fork-owned types live in
13
+ > `interfaces/host-types.ts`.
14
+
15
+ This is the source of truth for verifying the host surface. When the
16
+ upstream Pi ecosystem changes its public surface, update both the host
17
+ and the corresponding row below.
18
+
19
+ ---
20
+
21
+ ## UniversalHostSurface → ExtensionAPI
22
+
23
+ | Host member (host-types.ts / universal-host.ts) | Satisfies | Upstream call sites (evidence) | Notes |
24
+ | --- | --- | --- | --- |
25
+ | `registerTool` | `pi.registerTool` | `index.ts` registerDirectTool / registerMcpScript / registerProxyTool | Renderer boundary keeps 3 `as (tool: unknown)` casts — fork `RenderTheme` ≠ pi-* `Theme`. |
26
+ | `registerCommand` | `pi.registerCommand` | `index.ts` `mcp` / `mcp-auth` / `pi-mcp` | Direct pass-through; no-op surface on MCP stdio. |
27
+ | `registerFlag` | `pi.registerFlag` | `index.ts` / `init.ts` | Direct pass-through. |
28
+ | `on` | `pi.on` | `index.ts` `session_start` / `tool_result` / `input` | Event-name generics erased. |
29
+ | `events` | `pi.events` (`McpStatusEventBus`) | `index.ts` `publishMcpStatusShutdown` | `UniversalStatusEventBus` = `EventEmitter` subset (`emit` / `on` / `removeAllListeners`). |
30
+ | `getAllTools` | `pi.getAllTools` | `index.ts` | `ToolInfo[]`. |
31
+ | `setActiveTools` | `pi.setActiveTools` | `index.ts` `syncProxyTool` / `syncNamespaceTools` | Drives MCP `ListTools` filtering. |
32
+ | `getActiveTools` | `pi.getActiveTools` | `index.ts` | — |
33
+ | `unregisterTool` | `pi.unregisterTool` | `index.ts` dynamic proxy/direct removal | Returns `boolean`. |
34
+ | `getFlag` | `pi.getFlag` | `init.ts` | `string \| undefined`. |
35
+ | `sendMessage` | `pi.sendMessage` | `init.ts` | Both `message` / `options` are `unknown`. |
36
+ | `exec` | `pi.exec` | `utils.ts` | `Promise<unknown>`. |
37
+
38
+ ## UISystem / Form → ExtensionUIContext
39
+
40
+ | Host member (host-types.ts) | Pi member | Call site | Optional? |
41
+ | --- | --- | --- | --- |
42
+ | `UISystem.notify` | `ctx.ui.notify` | `commands.ts`, `init.ts` | Required. |
43
+ | `UISystem.setStatus` | `ctx.ui.setStatus` | `init.ts` | Optional. |
44
+ | `UISystem.form` | `ctx.ui.form` → `elicitation-handler.ts` | `bin/mcp-server.ts` → `protocol-elicitation-forwarder.ts` | Optional. |
45
+ | `UISystem.custom` | `ctx.ui.custom` | (reserved, no current use) | Optional. |
46
+ | `UISystem.theme.fg` | `ctx.ui.theme.fg` | `init.ts` | Optional. |
47
+ | `FormConfig` / `FormResult` | `ExtensionUIFormRequest` / `ExtensionUIFormResult` | `elicitation-handler.ts` | — |
48
+
49
+ ## Type Mappings
50
+
51
+ | Fork-owned type (`interfaces/host-types.ts`) | Pi type | Source |
52
+ | --- | --- | --- |
53
+ | `UniversalHostSurface` | `ExtensionAPI` | boundary cast in `bin/mcp-server.ts` |
54
+ | `ToolInfo` | `getAllTools` element | `index.ts` |
55
+ | `ToolRegistration` | `ToolDefinition` | `index.ts` registerTool |
56
+ | `CommandConfig` | `RegisteredCommand` | `index.ts` registerCommand |
57
+ | `FlagConfig` | `ExtensionFlag` | `index.ts` registerFlag |
58
+ | `AgentChannel` | (host → owning stdio process) | `bin/mcp-server.ts` (host `sendMessage` → stderr) |
59
+ | `FormConfig` / `FormResult` | `ExtensionUIFormRequest` / `ExtensionUIFormResult` | `elicitation-handler.ts` |
60
+
61
+ ---
62
+
63
+ ## Upstream Update Checklist
64
+
65
+ When upstream Pi changes land, walk this list:
66
+
67
+ - [ ] `UniversalHostSurface` members vs `index.ts` / `init.ts` / `utils.ts` `pi.*` call sites
68
+ - [ ] the 3 `as (tool: unknown)` renderer-boundary casts in `index.ts`
69
+ - [ ] `events` bus shape (`McpStatusEventBus`)
70
+ - [ ] upstream v2.28.0 `MCP_RUNTIME_REGISTER_EVENT` runtime registration (deferred merge)
71
+
72
+ For any new `pi.*` / `ctx.ui.*` call site, add a row to the tables above and a
73
+ corresponding member on `UniversalMcpHost` (or in `host-types.ts`).
package/README.md ADDED
@@ -0,0 +1,564 @@
1
+ <p>
2
+ <img src="banner.png" alt="pi-mcp-adapter" width="1100">
3
+ </p>
4
+
5
+ # Universal MCP Adapter
6
+
7
+ A universal MCP (Model Context Protocol) adapter with **Pi as a first-class host** (native extension) — and every other MCP-compatible coding agent via the universal `mcp-server` stdio entry point.
8
+
9
+ Use MCP servers without burning your context window, from Pi today and from any MCP-compatible coding agent via the [Supported Agents](#supported-agents) matrix below.
10
+
11
+ ## Supported Agents
12
+
13
+ Pi is a first-class host (Branch A, native extension). **Any other MCP-compatible agent** works via Branch C — the universal `mcp-server` stdio entry point. No per-agent adapter code is needed; the server discovers client capabilities at runtime via the MCP protocol.
14
+
15
+ A single upstream engine (`index.ts` `installMcpAdapter`) drives both paths: on Pi it runs as a native extension; on other agents a small host shim (`adapters/universal-host.ts`) impersonates Pi's `ExtensionAPI` so the same engine runs unchanged behind the `mcp-server` stdio layer. See [MAPPING.md](MAPPING.md) for the host-surface contract.
16
+
17
+ | Branch | Agent | Config path | Integration | Sampling | Elicitation |
18
+ |--------|-------|-------------|-------------|----------|-------------|
19
+ | Branch A | Pi | `~/.pi/agent/mcp.json` | Native Pi extension (`pi install`) | ✅ In-process | ✅ Pi UI forms |
20
+ | Branch C | Any MCP-compatible agent | `~/.config/mcp/mcp.json` or `.mcp.json` | Universal MCP stdio server (`mcp-server`) | Runtime-discovered | Runtime-discovered |
21
+
22
+ > **Branch C is a complete implementation** (D-08): the `mcp` proxy tool is always available.
23
+ > Sampling is forwarded via MCP `sampling/createMessage` reverse call when the agent
24
+ > declares `sampling` capability. Elicitation is forwarded via `elicitation/create` when
25
+ > the agent declares `elicitation.form` capability. Capabilities are discovered at runtime
26
+ > — no static matrix needed.
27
+
28
+ See [Verification](#verification) below for the latest matrix results.
29
+
30
+ ## Agent Skills
31
+
32
+ `mcp-adapter` ships three project-level Agent Skills to automate common maintainer and integration workflows. Copy a skill into your agent's global skills directory, then trigger it with the prompt.
33
+
34
+ | Skill | Purpose | Trigger | Flow |
35
+ |-------|---------|---------|------|
36
+ | `mcp-adapter` | Universal agent integration — config generation, adapter deployment, verification | `integrate agent` · `deploy mcp-adapter` · `configure MCP servers` · `verify mcp-adapter` | Identify agent + capability-gate → Generate `mcp.json` → Deploy adapter → Verify |
37
+ | `upstream-merge` | Sync upstream [`nicobailon/pi-mcp-adapter`](https://github.com/nicobailon/pi-mcp-adapter) into this fork with conflict resolution (e.g. reverse-merging the source repo) | `merge upstream` · `sync fork` · `upstream conflict` · `/upstream-merge` | Run divergence check → Apply 12-category resolution matrix → Delegate conflicts to `/resolve-conflicts` → Validate with §5 checklist |
38
+ | `resolve-conflicts` | Professional Git conflict resolution — 7 conflict type patterns, plan-first, user-approved | `merge conflict` · `/resolve-conflicts` | Assess conflicts → Create resolution plan → Get user approval → Execute resolution → Validate with tests |
39
+
40
+ > **`upstream-merge` is the recommended way to reverse-merge the source repo.** When upstream `pi-mcp-adapter` cuts a new release, run `/upstream-merge` from your agent — it creates a `upstream-merge/<version>` branch (no direct commits to `main`, per `AGENTS.md` branch policy), runs `npm run upstream:check` to surface live divergence, and walks the per-file decision tree before delegating to `/resolve-conflicts`. Both `upstream-merge` and `resolve-conflicts` must be installed together.
41
+
42
+ Install & use:
43
+
44
+ ```bash
45
+ # 1. Copy the skills you need into your agent's global skills directory
46
+ cp -r skills/mcp-adapter ~/.qoder/skills/ # or your agent's equivalent path
47
+ cp -r skills/upstream-merge ~/.qoder/skills/ # also copy resolve-conflicts
48
+ cp -r skills/resolve-conflicts ~/.qoder/skills/
49
+
50
+ # 2. Trigger the skill from your agent
51
+ # /mcp-adapter — integrate an agent (Phase 0 → 1 → 2 → 3)
52
+ # /upstream-merge — sync this fork with upstream pi-mcp-adapter
53
+ # /resolve-conflicts — resolve Git merge conflicts professionally
54
+ ```
55
+
56
+ See [`skills/README.md`](skills/README.md) for the full skill directory, dependency graph, and `Skill Usage Order` diagram.
57
+
58
+ https://github.com/user-attachments/assets/4b7c66ff-e27e-4639-b195-22c3db406a5a
59
+
60
+ ## Why This Exists
61
+
62
+ Mario wrote about [why you might not need MCP](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/). The problem: tool definitions are verbose. A single MCP server can burn 10k+ tokens, and you're paying that cost whether you use those tools or not. Connect a few servers and you've burned half your context window before the conversation starts.
63
+
64
+ His take: skip MCP entirely, write simple CLI tools instead.
65
+
66
+ But the MCP ecosystem has useful stuff - databases, browsers, APIs. This adapter gives you access without the bloat. One proxy tool (~200 tokens) instead of hundreds. The agent discovers what it needs on-demand. Servers only start when you actually use them. The same engine drives Pi today; other agents plug in through the universal `mcp-server` stdio entry without touching MCP server code.
67
+
68
+ ## Install
69
+
70
+ ### For Pi users (recommended)
71
+
72
+ ```bash
73
+ pi install npm:@njuptlzf/mcp-adapter
74
+ ```
75
+
76
+ Restart Pi after installation. The rest of this README focuses on the Pi integration; for other agents see the next section.
77
+
78
+ ### For other agents (Branch C — universal MCP stdio server)
79
+
80
+ Any MCP-compatible agent can use the universal `mcp-server` bin entry. Register it in the agent's MCP config:
81
+
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "mcp-adapter": {
86
+ "command": "mcp-server"
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ `mcp-server` auto-discovers its config (`.mcp.json` in the CWD, or `~/.config/mcp/mcp.json`). To point it at an explicit config file, pass `--config`:
93
+
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "mcp-adapter": {
98
+ "command": "mcp-server",
99
+ "args": ["--config", "/path/to/config.json"]
100
+ }
101
+ }
102
+ }
103
+ ```
104
+
105
+ For a standalone deployment that needs no `node_modules` (e.g. side-loading a single file onto another machine), build `bin/mcp-server.ts` into one self-contained `.mjs` bundle:
106
+
107
+ ```bash
108
+ npm run build:mcp-server
109
+ ```
110
+
111
+ This bundles `bin/mcp-server.ts` into a single self-contained `./mcp-server.mjs` via the `scripts/build-mcp-server.mjs` esbuild pipeline.
112
+
113
+ The bundle inlines all dependencies (MCP SDK, Zod, adapters), so the single `.mjs` runs on any machine with just Node.js.
114
+
115
+ Then register it with `node` and the `--config` flag pointing at the file that lists the servers to proxy:
116
+
117
+ ```json
118
+ {
119
+ "mcpServers": {
120
+ "mcp-adapter": {
121
+ "command": "node",
122
+ "args": [
123
+ "/home/.local/lib/mcp-server/mcp-server.mjs",
124
+ "--config",
125
+ "/home/.local/lib/mcp-server/config.json"
126
+ ]
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ Config discovery order for `--config` is: `--config` flag → `MCP_CONFIG_PATH` env var → `.mcp.json` in the CWD → `~/.config/mcp/mcp.json`.
133
+
134
+ The server is agent-agnostic — it speaks MCP protocol and discovers client capabilities at runtime. Sampling and elicitation are forwarded via MCP Server→Client reverse calls when the agent declares those capabilities.
135
+
136
+ ## What happens on first run (Pi)
137
+
138
+ The adapter reads standard MCP files automatically. No extra setup needed if you already have them.
139
+
140
+ | You already have... | What happens |
141
+ |---------------------|--------------|
142
+ | `.mcp.json` or `~/.config/mcp/mcp.json` | The shared MCP files are picked up immediately. The first time you open `/mcp`, you'll see a short heads-up explaining which file was detected and that the adapter only writes host-specific overrides to its own files (Pi: `~/.pi/agent/mcp.json`). |
143
+ | Host-specific configs (Cursor, Claude Code, Codex, etc.) but no standard MCP files | Run `/mcp setup` to adopt those host configs into the shared MCP format. The setup flow shows exactly what it found, lets you pick which ones to import, and previews the exact file changes before writing. |
144
+ | Nothing configured yet | Run `/mcp setup` to scaffold a minimal `.mcp.json`, quick-add RepoPrompt, or inspect what the adapter discovered on your machine. |
145
+
146
+ If you prefer the terminal, you can also run `pi-mcp-adapter init` after install to scan for host-specific configs and add missing compatibility imports to the host agent dir (Pi: `~/.pi/agent/mcp.json` by default, or `$PI_CODING_AGENT_DIR/mcp.json` when set).
147
+
148
+ ## Quick Start
149
+
150
+ The adapter ships one upstream engine with two entry points: the Pi-native `mcpAdapter(pi)` (backward-compatible) and the universal `mcp-server` stdio binary for any MCP-compatible agent.
151
+
152
+ ### Pi users (Pi-native entry point)
153
+
154
+ ```typescript
155
+ import { mcpAdapter } from "@njuptlzf/mcp-adapter";
156
+
157
+ export default mcpAdapter(pi); // Pi's ExtensionAPI — backward-compatible
158
+ ```
159
+
160
+ To supply config explicitly instead of relying on Pi's standard MCP files:
161
+
162
+ ```typescript
163
+ import { createMcpAdapter } from "@njuptlzf/mcp-adapter";
164
+
165
+ export default createMcpAdapter({ config })(pi); // factory, then install onto `pi`
166
+ ```
167
+
168
+ ### Universal entry point (any MCP-compatible agent)
169
+
170
+ For any other MCP-compatible agent, register the `mcp-server` bin in the agent's MCP config (see [Install](#install)) — no adapter code needed:
171
+
172
+ ```json
173
+ {
174
+ "mcpServers": {
175
+ "mcp-adapter": { "command": "mcp-server" }
176
+ }
177
+ }
178
+ ```
179
+
180
+ ---
181
+
182
+ The examples below show the Pi path's tool-call syntax. The same MCP server config and the same `mcp` proxy tool shape are reused across agents through the [Supported Agents](#supported-agents) matrix above.
183
+
184
+ Preferred project config: `.mcp.json`
185
+
186
+ ```json
187
+ {
188
+ "mcpServers": {
189
+ "chrome-devtools": {
190
+ "command": "npx",
191
+ "args": ["-y", "chrome-devtools-mcp@latest"]
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ Preferred user-global shared config: `~/.config/mcp/mcp.json`
198
+
199
+ Pi also reads Pi-owned override files for settings and host-specific compatibility:
200
+
201
+ - `<Pi agent dir>/mcp.json` — Pi global override (`~/.pi/agent/mcp.json` by default)
202
+ - `.pi/mcp.json` — Pi project override
203
+
204
+ Precedence is:
205
+
206
+ 1. `~/.config/mcp/mcp.json`
207
+ 2. `<Pi agent dir>/mcp.json`
208
+ 3. `.mcp.json`
209
+ 4. `.pi/mcp.json`
210
+
211
+ Servers are **lazy by default** — they won't connect until you actually call one of their tools. The adapter caches tool metadata so search and describe work without live connections.
212
+
213
+ ```
214
+ mcp({ search: "screenshot" })
215
+ ```
216
+ ```
217
+ chrome_devtools_take_screenshot
218
+ Take a screenshot of the page or element.
219
+
220
+ Parameters:
221
+ format (enum: "png", "jpeg", "webp") [default: "png"]
222
+ fullPage (boolean) - Full page instead of viewport
223
+ ```
224
+ ```
225
+ mcp({ tool: "chrome_devtools_take_screenshot", args: '{"format": "png"}' })
226
+ ```
227
+
228
+ Note: `args` is a JSON string, not an object.
229
+
230
+ Two calls instead of 26 tools cluttering the context.
231
+
232
+ ## Verification
233
+
234
+ ```bash
235
+ npm run test:prebuild # build visualizer dist/
236
+ npx vitest run # full suite
237
+ ```
238
+
239
+ The suite covers the upstream engine (`__tests__/index-lifecycle.test.ts`, `__tests__/mcp-code.test.ts`), the universal host wiring (`__tests__/universal-host-acceptance.test.ts`), runtime registration (`__tests__/runtime-register.test.ts`), and the protocol forwarders. Token-savings numbers are baseline-bound; see `docs/mcp-adapter-token-savings.md`.
240
+
241
+ ## Config
242
+
243
+ ### File Layout
244
+
245
+ Use the shared MCP files when you want one setup to work across hosts, and host-owned files when a specific agent needs its own overrides or settings.
246
+
247
+ | File | Purpose | Owner |
248
+ |------|---------|-------|
249
+ | `~/.config/mcp/mcp.json` | User-global shared MCP config | Shared |
250
+ | `.mcp.json` | Project-local shared MCP config | Shared |
251
+ | `<Pi agent dir>/mcp.json` | Pi global override and compatibility imports (`~/.pi/agent/mcp.json` by default) | Pi |
252
+ | `.pi/mcp.json` | Pi project override | Pi |
253
+
254
+ Pi-specific files are the write targets for imported or shared global servers when Pi needs to persist adapter-only settings such as `directTools`. Other agents use the `mcp-server` stdio path (Branch C) and don't write host-specific files.
255
+
256
+ ### Server Options
257
+
258
+ ```json
259
+ {
260
+ "mcpServers": {
261
+ "my-server": {
262
+ "command": "npx",
263
+ "args": ["-y", "some-mcp-server"],
264
+ "lifecycle": "lazy",
265
+ "idleTimeout": 10
266
+ }
267
+ }
268
+ }
269
+ ```
270
+
271
+ | Field | Description |
272
+ |-------|-------------|
273
+ | `command` | Executable for stdio transport |
274
+ | `args` | Command arguments |
275
+ | `env` | Environment variables; supports `${VAR}` and `$env:VAR` interpolation |
276
+ | `cwd` | Working directory; supports `${VAR}`, `$env:VAR`, and `~` expansion |
277
+ | `url` | HTTP endpoint (StreamableHTTP with SSE fallback) |
278
+ | `headers` | HTTP headers; supports `${VAR}` and `$env:VAR` interpolation |
279
+ | `auth` | `"bearer"` or `"oauth"` |
280
+ | `oauth.grantType` | `"authorization_code"` (default) or `"client_credentials"` for non-interactive machine auth |
281
+ | `oauth.clientId` | Pre-registered OAuth client ID; dynamic registration is used when omitted |
282
+ | `oauth.clientSecret` | OAuth client secret for confidential clients |
283
+ | `oauth.scope` | Requested OAuth scopes |
284
+ | `oauth.redirectUri` | Exact localhost redirect URI for browser OAuth, including port and path, for providers that pre-register callbacks |
285
+ | `oauth.clientName` | Client display name advertised during dynamic registration |
286
+ | `oauth.clientUri` | Client homepage URI advertised during dynamic registration |
287
+ | `bearerToken` / `bearerTokenEnv` | Token or env var name; `bearerToken` supports `${VAR}` and `$env:VAR` interpolation |
288
+ | `lifecycle` | `"lazy"` (default), `"eager"`, or `"keep-alive"` |
289
+ | `idleTimeout` | Minutes before idle disconnect (overrides global) |
290
+ | `exposeResources` | Expose MCP resources as tools (default: true) |
291
+ | `directTools` | `true`, `string[]`, or `false` — register tools individually instead of through proxy |
292
+ | `excludeTools` | `string[]` of tool names to hide (matches original names like `get_screenshot` and prefixed names like `figma_get_screenshot`) |
293
+ | `debug` | Show server stderr (default: false) |
294
+
295
+ For pre-registered browser OAuth clients, set `oauth.redirectUri` to the exact callback registered with the provider, for example `"http://localhost:3118/callback"`. Dynamic clients normally omit it and use a lazy OS-assigned localhost callback port.
296
+
297
+ ### Remote/headless OAuth
298
+
299
+ If Pi is running on a remote server and cannot open a local browser, start OAuth through the proxy tool:
300
+
301
+ ```js
302
+ mcp({ action: "auth-start", server: "linear-server" })
303
+ ```
304
+
305
+ Open the returned authorization URL in your local browser. After approval, your browser redirects to a localhost URL. On a remote server that local page may fail to load; copy the full URL from the browser address bar anyway and complete the flow in the same Pi session:
306
+
307
+ ```js
308
+ mcp({
309
+ action: "auth-complete",
310
+ server: "linear-server",
311
+ args: '{"redirectUrl":"http://localhost:19876/callback?code=...&state=..."}'
312
+ })
313
+ ```
314
+
315
+ You can also pass only the `code` query parameter with `args: '{"code":"..."}'`. Treat authorization URLs and codes as sensitive; they can grant access to the MCP server until the flow expires or completes.
316
+
317
+ ### Lifecycle Modes
318
+
319
+ - **`lazy`** (default) — Don't connect at startup. Connect on first tool call. Disconnect after idle timeout. Cached metadata keeps search/list working without connections.
320
+ - **`eager`** — Connect at startup but don't auto-reconnect if the connection drops. No idle timeout by default (set `idleTimeout` explicitly to enable).
321
+ - **`keep-alive`** — Connect at startup. Auto-reconnect via health checks. No idle timeout. Use for servers you always need available.
322
+
323
+ ### Settings
324
+
325
+ ```json
326
+ {
327
+ "settings": {
328
+ "toolPrefix": "server",
329
+ "idleTimeout": 10
330
+ },
331
+ "mcpServers": { }
332
+ }
333
+ ```
334
+
335
+ | Setting | Description |
336
+ |---------|-------------|
337
+ | `toolPrefix` | `"server"` (default), `"short"` (strips `-mcp` suffix), or `"none"` |
338
+ | `idleTimeout` | Global idle timeout in minutes (default: 10, 0 to disable) |
339
+ | `directTools` | Global default for all servers (default: false). Per-server overrides this. |
340
+ | `disableProxyTool` | Hide the `mcp` proxy tool once configured direct tools are fully available from cache. |
341
+ | `autoAuth` | Auto-run OAuth on `connect`/tool calls when a server needs auth, then retry once (default: false). |
342
+ | `sampling` | Allow MCP servers to sample through Pi models, honoring `modelPreferences.hints` before current/default fallback (default: true when UI approval is available). |
343
+ | `samplingAutoApprove` | Skip sampling confirmation prompts. Required for sampling in non-UI sessions (default: false). |
344
+ | `elicitation` | Allow MCP servers to request user input through Pi dialogs (default: true when Pi UI is available). |
345
+
346
+ Per-server `idleTimeout` overrides the global setting.
347
+
348
+ ### MCP Elicitation
349
+
350
+ When the host agent exposes UI (e.g. Pi's `ctx.ui.form()`), the adapter advertises MCP elicitation support. Form elicitations are rendered through the host's UI primitive and map agent actions to MCP actions: submit → `accept`, secondary → `decline`, cancel → `cancel`. URL elicitations prompt before opening a browser unless `elicitationAutoOpenUrls` is enabled. Hosts that don't ship a UI form capability simply don't advertise the form elicitation path.
351
+
352
+ ### Direct Tools
353
+
354
+ By default, all MCP tools are accessed through the single `mcp` proxy tool. This keeps context small but means the LLM has to discover MCP tools via proxy search. If you want specific tools to show up directly in the host agent's tool list — alongside `read`, `bash`, `edit`, etc. — add `directTools` to your config.
355
+
356
+ Per-server:
357
+
358
+ ```json
359
+ {
360
+ "mcpServers": {
361
+ "chrome-devtools": {
362
+ "command": "npx",
363
+ "args": ["-y", "chrome-devtools-mcp@latest"],
364
+ "directTools": true
365
+ },
366
+ "github": {
367
+ "command": "npx",
368
+ "args": ["-y", "@modelcontextprotocol/server-github"],
369
+ "directTools": ["search_repositories", "get_file_contents"]
370
+ },
371
+ "huge-server": {
372
+ "command": "npx",
373
+ "args": ["-y", "mega-mcp@latest"]
374
+ }
375
+ }
376
+ }
377
+ ```
378
+
379
+ | Value | Behavior |
380
+ |-------|----------|
381
+ | `true` | Register all tools from this server as individual host-agent tools |
382
+ | `["tool_a", "tool_b"]` | Register only these tools (use original MCP names) |
383
+ | Omitted or `false` | Proxy only (default) |
384
+
385
+ To set a global default for all servers:
386
+
387
+ ```json
388
+ {
389
+ "settings": {
390
+ "directTools": true
391
+ },
392
+ "mcpServers": {
393
+ "huge-server": {
394
+ "directTools": false
395
+ }
396
+ }
397
+ }
398
+ ```
399
+
400
+ Per-server `directTools` overrides the global setting. The example above registers direct tools for every server except `huge-server`.
401
+
402
+ To exclude specific tools while still using `directTools: true`, add `excludeTools` on the server:
403
+
404
+ ```json
405
+ {
406
+ "mcpServers": {
407
+ "figma": {
408
+ "url": "http://localhost:3845/mcp",
409
+ "directTools": true,
410
+ "excludeTools": ["get_figjam", "figma_get_code_connect_map"]
411
+ }
412
+ }
413
+ }
414
+ ```
415
+
416
+ `excludeTools` filters direct tools, proxy search/list/describe, and the `/mcp` panel view.
417
+
418
+ Each direct tool costs ~150-300 tokens in the system prompt (name + description + schema). Good for targeted sets of 5-20 tools. For servers with 75+ tools, stick with the proxy or pick specific tools with a `string[]`.
419
+
420
+ Direct tools register from the metadata cache in the host agent dir (Pi: `~/.pi/agent/mcp-cache.json` by default, or `$PI_CODING_AGENT_DIR/mcp-cache.json` when set), so no server connections are needed at startup. On the first session after adding `directTools` to a new server, the cache won't exist yet — tools fall back to proxy-only and the cache populates in the background. To force it: `/mcp reconnect <server>` (Pi) or the equivalent reconnect action on your host agent.
421
+
422
+ When you change direct-tool toggles in `/mcp` (Pi) or write new config through `/mcp setup`, the extension triggers the host agent's normal reload flow automatically. That refreshes extensions, prompts, skills, and MCP tool registration in one shot, so newly configured direct tools can appear without a manual restart.
423
+
424
+ **Interactive configuration (Pi):** Run `/mcp` to open an interactive panel showing all servers with connection status, tools, and direct/proxy toggles. You can reconnect servers and toggle tools between direct and proxy from the same overlay. For OAuth, press Enter on a server that needs auth or `ctrl+a` on any OAuth server. Other agents expose equivalent affordances through their own UI surfaces.
425
+
426
+ **Guided first-run setup (Pi):** Run `/mcp setup` to inspect detected shared MCP files, adopt compatibility imports from other hosts, open discovered config paths, preview exact before/after file diffs for writes, scaffold a minimal project `.mcp.json`, or quick-add RepoPrompt into a standard/shared MCP file.
427
+
428
+ **Subagent integration (Pi subagent extension):** If you use the subagent extension, agents can request direct MCP tools in their frontmatter with `mcp:server-name` syntax. See the subagent README for details.
429
+
430
+ ### MCP UI Integration
431
+
432
+ MCP servers can ship interactive UIs via the [MCP UI](https://github.com/MCP-UI-Org/mcp-ui) standard. When you call a tool that has a UI resource, the adapter opens it in a native macOS window via [Glimpse](https://github.com/hazat/glimpse) if available, otherwise falls back to the browser.
433
+
434
+ **How it works:**
435
+
436
+ 1. Agent calls a tool like `launch_dashboard`
437
+ 2. The tool's metadata includes `_meta.ui.resourceUri` pointing to a UI resource
438
+ 3. mcp-adapter fetches the UI HTML and opens it in an iframe
439
+ 4. The UI can call MCP tools and send messages back to the agent
440
+
441
+ **Native rendering:** On macOS, if [Glimpse](https://github.com/hazat/glimpse) is installed (`pi install npm:glimpseui`), UIs open in a native WKWebView window instead of a browser tab. Set `MCP_UI_VIEWER=browser` to force the browser, or `MCP_UI_VIEWER=glimpse` to require native rendering.
442
+
443
+ **Bidirectional communication:** The UI talks back. When it sends a prompt or intent, the message is stored and `triggerTurn()` wakes the agent. The agent retrieves messages via `mcp({ action: "ui-messages" })` and responds, enabling conversational UIs where the app and agent collaborate in real-time.
444
+
445
+ **Session reuse:** When the agent calls the same tool again while its UI is already open, the adapter pushes the new result to the existing window instead of replacing it. This enables live updates — the agent can refine a chart, add data, or respond to user input without losing the current view. Different tools still replace the session as before.
446
+
447
+ **Message types from UI:**
448
+
449
+ | Type | Purpose |
450
+ |------|---------|
451
+ | `prompt` | User message that triggers an agent response |
452
+ | `intent` | Structured action with name + params |
453
+ | `notify` | Fire-and-forget notification |
454
+ | `message` | Generic message payload |
455
+ | (custom) | Any other type forwarded as intent |
456
+
457
+ **Retrieving UI messages:**
458
+
459
+ ```
460
+ mcp({ action: "ui-messages" })
461
+ ```
462
+
463
+ Returns accumulated messages from UI sessions. Each message includes `type`, `sessionId`, `serverName`, `toolName`, and `timestamp`. Prompt messages include `prompt`, intent messages include `intent` and `params`.
464
+
465
+ **Browser controls:**
466
+
467
+ - **Cmd/Ctrl+Enter** — Complete and close
468
+ - **Escape** — Cancel and close
469
+ - **Done/Cancel buttons** — Same as keyboard shortcuts
470
+
471
+ **Technical notes:**
472
+
473
+ - Tool consent gates whether UIs can call MCP tools (never/once-per-server/always)
474
+ - Works with both stdio and HTTP MCP servers
475
+ - Uses a local 408KB AppBridge bundle (MCP SDK + Zod) for browser↔server communication
476
+
477
+ ### Local Example: Interactive Visualizer
478
+
479
+ A minimal MCP UI example at `examples/interactive-visualizer` demonstrating charts, bidirectional messaging, and streaming. From that directory:
480
+
481
+ ```bash
482
+ npm install
483
+ npm run build
484
+ npm run install-local
485
+ ```
486
+
487
+ Restart pi, then ask the agent to show a chart — it calls `show_chart` and opens the UI in Glimpse (macOS) or the browser. Use `npm run uninstall-local` to remove the MCP entry.
488
+
489
+ ### Import Existing Configs
490
+
491
+ Shared MCP files are loaded automatically. Use `imports` only for host-specific config formats that are not already covered by `.mcp.json` or `~/.config/mcp/mcp.json`.
492
+
493
+ ```json
494
+ {
495
+ "imports": ["cursor", "claude-code", "claude-desktop"],
496
+ "mcpServers": { }
497
+ }
498
+ ```
499
+
500
+ Supported compatibility imports: `cursor`, `claude-code`, `claude-desktop`, `vscode`, `windsurf`, `codex`
501
+
502
+ `pi-mcp-adapter init` detects these host-specific configs and adds missing imports to the host agent dir config (Pi: `~/.pi/agent/mcp.json`) for you. The `opencode` import reads OpenCode V1 `mcp` entries from both `~/.config/opencode/opencode.json` and the project `opencode.json`, with project fields taking precedence. It is explicit-import only; OpenCode V2, inline content, managed configs, and remote discovery are not supported.
503
+
504
+ ### Project Config
505
+
506
+ Prefer `.mcp.json` for project-local shared MCP config. Use `.pi/mcp.json` only when you need a Pi-specific project override. Project files override both user-global shared MCP config and Pi global overrides.
507
+
508
+ ## Usage
509
+
510
+ | Mode | Example |
511
+ |------|---------|
512
+ | Status | `mcp({ })` |
513
+ | List server | `mcp({ server: "name" })` |
514
+ | Search | `mcp({ search: "screenshot navigate" })` |
515
+ | Describe | `mcp({ describe: "tool_name" })` |
516
+ | Call | `mcp({ tool: "...", args: '{"key": "value"}' })` |
517
+ | Connect | `mcp({ connect: "server-name" })` |
518
+ | UI messages | `mcp({ action: "ui-messages" })` |
519
+ | Auth start | `mcp({ action: "auth-start", server: "name" })` |
520
+ | Auth complete | `mcp({ action: "auth-complete", server: "name", args: '{"redirectUrl":"..."}' })` |
521
+
522
+ MCP proxy and direct-tool results render compactly by default: long text shows the first three lines plus a `Ctrl+O to expand` hint, while the full result remains available when expanded and is still returned unchanged to the model. Set `settings.toolResultRendering` to `"boxed"` to restore the legacy boxed Pi row, or set `settings.collapsedResultLines` to `2` or `3` when you want more collapsed text.
523
+
524
+ Search includes both MCP tools and host-agent tools (from extensions). Host-agent tools appear first with `[host tool]` prefix. Space-separated words are OR'd.
525
+
526
+ Tool names are fuzzy-matched on hyphens and underscores — `context7_resolve_library_id` finds `context7_resolve-library-id`.
527
+
528
+ ## Commands (Pi)
529
+
530
+ The `/mcp` and `/mcp-auth` slash commands are Pi-specific UI shortcuts. On other agents, call the equivalent MCP actions directly via the proxy tool or the agent's own command surface — the underlying behaviour is identical; only the trigger differs.
531
+
532
+ | Command | What it does |
533
+ |---------|--------------|
534
+ | `/mcp` | Interactive panel and first-run onboarding surface |
535
+ | `/mcp setup` | Guided setup for imports, a minimal `.mcp.json`, RepoPrompt quick-add, and config-path inspection |
536
+ | `/mcp tools` | List all tools |
537
+ | `/mcp reconnect` | Reconnect all servers |
538
+ | `/mcp reconnect <server>` | Connect or reconnect a single server |
539
+ | `/mcp logout <server>` | Clear stored OAuth credentials for a server and disconnect it |
540
+ | `/mcp-auth` | Open an OAuth server picker in interactive UI sessions |
541
+ | `/mcp-auth <server>` | OAuth setup for a specific server |
542
+
543
+ If `settings.autoAuth` is `true`, `mcp({ connect: ... })`, `mcp({ tool: ... })`, and direct tool calls automatically run OAuth when needed and retry once.
544
+
545
+ In interactive sessions, you can also authenticate from `/mcp` with `ctrl+a` or Enter on a server that needs auth. In remote/headless sessions, use the proxy tool's `auth-start` and `auth-complete` actions to copy the authorization URL locally and paste the redirect URL back into Pi. `/mcp-auth` without a server only opens a picker in the interactive UI.
546
+
547
+ ### MCP output schemas
548
+
549
+ Advertised tool `outputSchema` values support JSON Schema draft-07 and 2020-12. Unstamped schemas use the SDK's 2020-12 default. Returned `structuredContent` is validated against the advertised schema for both proxy and direct-tool calls.
550
+
551
+ Additional implementation notes:
552
+
553
+ - Idle servers disconnect after 10 minutes (configurable), reconnect automatically on next use
554
+ - npx-based servers resolve to direct binary paths, skipping the ~143 MB npm parent process
555
+ - MCP server validates arguments, not the adapter
556
+ - Remote keep-alive servers force-refresh their tool catalog during health checks, before user input, and before adapter-triggered turns, with bounded reconnect backoff
557
+ - Specific tools can be promoted from the proxy to first-class host tools via `directTools` config, so the LLM sees them directly instead of having to search
558
+
559
+ ## Limitations
560
+
561
+ - Cross-session server sharing not yet implemented (each agent session runs its own server processes)
562
+ - Compact MCP result rendering summarizes text, but inline images are still controlled by Pi's image display settings and may render below the compact text summary.
563
+ - Pi still owns one separator row before self-rendered tool output, so compact mode reduces adapter rendering height but cannot promise true zero-gap rows.
564
+ - MCP sampling support is text-only; context inclusion, tools, stop sequences, audio, and image content are rejected with explicit errors.