pi-mcp-adapter 2.1.2 → 2.2.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/CHANGELOG.md +34 -0
- package/README.md +60 -2
- package/app-bridge.bundle.js +67 -0
- package/cli.js +0 -1
- package/commands.ts +210 -0
- package/consent-manager.ts +64 -0
- package/direct-tools.ts +301 -0
- package/errors.ts +219 -0
- package/glimpse-ui.ts +80 -0
- package/host-html-template.ts +427 -0
- package/index.ts +36 -1512
- package/init.ts +319 -0
- package/lifecycle.ts +2 -2
- package/logger.ts +169 -0
- package/metadata-cache.ts +16 -0
- package/package.json +27 -4
- package/proxy-modes.ts +635 -0
- package/server-manager.ts +48 -4
- package/state.ts +41 -0
- package/tool-metadata.ts +144 -0
- package/types.ts +211 -0
- package/ui-resource-handler.ts +145 -0
- package/ui-server.ts +623 -0
- package/ui-session.ts +384 -0
- package/ui-stream-types.ts +89 -0
- package/utils.ts +75 -0
- package/ARCHITECTURE.md +0 -630
package/ARCHITECTURE.md
DELETED
|
@@ -1,630 +0,0 @@
|
|
|
1
|
-
# Pi MCP Adapter - Architecture
|
|
2
|
-
|
|
3
|
-
## High-Level Overview
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
7
|
-
│ PI CODING AGENT │
|
|
8
|
-
│ ┌───────────────────────────────────────────────────────────────────────┐ │
|
|
9
|
-
│ │ Tool Registry │ │
|
|
10
|
-
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │
|
|
11
|
-
│ │ │ read │ │ write │ │ bash │ │ mcp │ │ │
|
|
12
|
-
│ │ │ (builtin) │ │ (builtin) │ │ (builtin) │ │ (MCP proxy) │ │ │
|
|
13
|
-
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────────┘ │ │
|
|
14
|
-
│ │ │ │
|
|
15
|
-
│ │ Only ONE tool registered for all MCP servers! │ │
|
|
16
|
-
│ │ ~200 tokens vs ~15,000 tokens for 75 individual tools │ │
|
|
17
|
-
│ │ │ │
|
|
18
|
-
│ └───────────────────────────────────────────────────────────────────────┘ │
|
|
19
|
-
│ ▲ │
|
|
20
|
-
│ │ pi.registerTool("mcp", ...) │
|
|
21
|
-
│ ┌──────────────────────────────────┴────────────────────────────────────┐ │
|
|
22
|
-
│ │ PI MCP ADAPTER EXTENSION │ │
|
|
23
|
-
│ │ ┌────────────┐ ┌─────────────────┐ ┌───────────────────────────┐ │ │
|
|
24
|
-
│ │ │ Config │ │ Server Manager │ │ Tool Metadata │ │ │
|
|
25
|
-
│ │ │ Loader │──│ (connections) │──│ (for search/lookup) │ │ │
|
|
26
|
-
│ │ └────────────┘ └─────────────────┘ └───────────────────────────┘ │ │
|
|
27
|
-
│ └───────────────────────────────────────────────────────────────────────┘ │
|
|
28
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
29
|
-
│
|
|
30
|
-
┌─────────────────┼─────────────────┐
|
|
31
|
-
│ │ │
|
|
32
|
-
▼ ▼ ▼
|
|
33
|
-
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
34
|
-
│ MCP Server │ │ MCP Server │ │ MCP Server │
|
|
35
|
-
│ (stdio) │ │ (HTTP) │ │ (stdio) │
|
|
36
|
-
│ │ │ │ │ │
|
|
37
|
-
│ xcodebuild │ │ remote-api │ │ github │
|
|
38
|
-
└─────────────┘ └─────────────┘ └─────────────┘
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Token Efficiency Design
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
45
|
-
│ OLD APPROACH (rejected) │
|
|
46
|
-
│ │
|
|
47
|
-
│ Register each MCP tool individually with Pi: │
|
|
48
|
-
│ │
|
|
49
|
-
│ - xcodebuild_list_sims (~200 tokens) │
|
|
50
|
-
│ - xcodebuild_build_sim (~200 tokens) │
|
|
51
|
-
│ - xcodebuild_tap (~200 tokens) │
|
|
52
|
-
│ - ... 72 more tools ... │
|
|
53
|
-
│ │
|
|
54
|
-
│ Total: ~15,000 tokens just for tool definitions! │
|
|
55
|
-
│ Problem: Burns context window, slow, expensive │
|
|
56
|
-
│ │
|
|
57
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
58
|
-
|
|
59
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
60
|
-
│ NEW APPROACH (implemented) │
|
|
61
|
-
│ │
|
|
62
|
-
│ Single unified `mcp` proxy tool: │
|
|
63
|
-
│ │
|
|
64
|
-
│ - mcp({ }) → Show server status │
|
|
65
|
-
│ - mcp({ server: "name" }) → List tools from server │
|
|
66
|
-
│ - mcp({ search: "search" }) → Search for tools │
|
|
67
|
-
│ - mcp({ tool: "name", args }) → Call a tool │
|
|
68
|
-
│ │
|
|
69
|
-
│ Total: ~200 tokens for the proxy tool! │
|
|
70
|
-
│ LLM discovers tools on-demand via search/list │
|
|
71
|
-
│ │
|
|
72
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Config Loading Flow
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
┌─────────────────────────┐
|
|
79
|
-
│ loadMcpConfig() │
|
|
80
|
-
└───────────┬─────────────┘
|
|
81
|
-
│
|
|
82
|
-
┌─────────────────────┼─────────────────────┐
|
|
83
|
-
│ │ │
|
|
84
|
-
▼ ▼ ▼
|
|
85
|
-
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
|
|
86
|
-
│ Global Config │ │ Import Sources │ │ Project Config │
|
|
87
|
-
│ │ │ │ │ │
|
|
88
|
-
│ ~/.pi/agent/ │ │ ~/.cursor/ │ │ .pi/mcp.json │
|
|
89
|
-
│ mcp.json │ │ mcp.json │ │ (in project) │
|
|
90
|
-
│ │ │ │ │ │
|
|
91
|
-
│ PRIORITY: 2 │ │ ~/.claude/ │ │ PRIORITY: 1 │
|
|
92
|
-
│ (base config) │ │ claude_desktop │ │ (overrides all) │
|
|
93
|
-
│ │ │ _config.json │ │ │
|
|
94
|
-
└─────────┬─────────┘ │ │ └─────────┬─────────┘
|
|
95
|
-
│ │ ~/.windsurf/ │ │
|
|
96
|
-
│ │ mcp.json │ │
|
|
97
|
-
│ │ │ │
|
|
98
|
-
│ │ .vscode/mcp.json │ │
|
|
99
|
-
│ │ │ │
|
|
100
|
-
│ │ PRIORITY: 3 │ │
|
|
101
|
-
│ │ (only if not in │ │
|
|
102
|
-
│ │ global config) │ │
|
|
103
|
-
│ └─────────┬─────────┘ │
|
|
104
|
-
│ │ │
|
|
105
|
-
└──────────┬──────────┴──────────┬──────────┘
|
|
106
|
-
│ │
|
|
107
|
-
▼ ▼
|
|
108
|
-
┌─────────────────────────────────────┐
|
|
109
|
-
│ Merged McpConfig │
|
|
110
|
-
│ │
|
|
111
|
-
│ { │
|
|
112
|
-
│ mcpServers: { │
|
|
113
|
-
│ "xcodebuild": {...}, │
|
|
114
|
-
│ "github": {...}, │
|
|
115
|
-
│ "imported-server": {...} │
|
|
116
|
-
│ }, │
|
|
117
|
-
│ settings: { │
|
|
118
|
-
│ toolPrefix: "server" │
|
|
119
|
-
│ } │
|
|
120
|
-
│ } │
|
|
121
|
-
└─────────────────────────────────────┘
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
## Connection Establishment
|
|
125
|
-
|
|
126
|
-
```
|
|
127
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
128
|
-
│ McpServerManager.connect() │
|
|
129
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
130
|
-
│
|
|
131
|
-
▼
|
|
132
|
-
┌─────────────────────────────────┐
|
|
133
|
-
│ Check: Already connecting? │
|
|
134
|
-
│ (dedupe concurrent attempts) │
|
|
135
|
-
└─────────────────┬───────────────┘
|
|
136
|
-
│ No
|
|
137
|
-
▼
|
|
138
|
-
┌─────────────────────────────────┐
|
|
139
|
-
│ Check: Existing healthy │
|
|
140
|
-
│ connection? (reuse if so) │
|
|
141
|
-
└─────────────────┬───────────────┘
|
|
142
|
-
│ No
|
|
143
|
-
▼
|
|
144
|
-
┌─────────────────────────────────┐
|
|
145
|
-
│ Has command? ──────────────── │ ─── Yes ──┐
|
|
146
|
-
└─────────────────┬───────────────┘ │
|
|
147
|
-
│ No │
|
|
148
|
-
▼ ▼
|
|
149
|
-
┌─────────────────────────────────┐ ┌────────────────────┐
|
|
150
|
-
│ Has URL? │ │ Create Stdio │
|
|
151
|
-
└─────────────────┬───────────────┘ │ Transport │
|
|
152
|
-
│ Yes │ │
|
|
153
|
-
▼ │ - spawn process │
|
|
154
|
-
┌───────────────────────────────────────┐ │ - connect stdin/ │
|
|
155
|
-
│ HTTP Transport Selection │ │ stdout │
|
|
156
|
-
│ │ └─────────┬──────────┘
|
|
157
|
-
│ ┌─────────────────────────────────┐ │ │
|
|
158
|
-
│ │ Try StreamableHTTP first │ │ │
|
|
159
|
-
│ │ (modern MCP servers) │ │ │
|
|
160
|
-
│ └───────────────┬─────────────────┘ │ │
|
|
161
|
-
│ │ │ │
|
|
162
|
-
│ Success? │ │ │
|
|
163
|
-
│ │ │ │ │
|
|
164
|
-
│ ┌─────┴──────┴──────┐ │ │
|
|
165
|
-
│ │ Yes │ No │ │
|
|
166
|
-
│ ▼ ▼ │ │
|
|
167
|
-
│ ┌────────┐ ┌──────────────┐ │ │
|
|
168
|
-
│ │ Use │ │ Fallback to │ │ │
|
|
169
|
-
│ │ Stream │ │ SSE Transport│ │ │
|
|
170
|
-
│ │ able │ │ (legacy) │ │ │
|
|
171
|
-
│ │ HTTP │ └──────┬───────┘ │ │
|
|
172
|
-
│ └───┬────┘ │ │ │
|
|
173
|
-
│ │ │ │ │
|
|
174
|
-
└──────┼────────────────┼───────────────┘ │
|
|
175
|
-
│ │ │
|
|
176
|
-
└────────┬───────┴────────────────────────────┘
|
|
177
|
-
│
|
|
178
|
-
▼
|
|
179
|
-
┌───────────────────────────────────────┐
|
|
180
|
-
│ client.connect(transport) │
|
|
181
|
-
└───────────────────┬───────────────────┘
|
|
182
|
-
│
|
|
183
|
-
┌─────────────┴─────────────┐
|
|
184
|
-
│ │
|
|
185
|
-
▼ ▼
|
|
186
|
-
┌───────────────────┐ ┌───────────────────┐
|
|
187
|
-
│ listTools() │ │ listResources() │
|
|
188
|
-
│ (with cursor │ │ (with cursor │
|
|
189
|
-
│ pagination) │ │ pagination) │
|
|
190
|
-
└─────────┬─────────┘ └─────────┬─────────┘
|
|
191
|
-
│ │
|
|
192
|
-
└─────────────┬─────────────┘
|
|
193
|
-
│
|
|
194
|
-
▼
|
|
195
|
-
┌───────────────────────────────────────┐
|
|
196
|
-
│ ServerConnection │
|
|
197
|
-
│ { │
|
|
198
|
-
│ client, │
|
|
199
|
-
│ transport, │
|
|
200
|
-
│ tools: McpTool[], │
|
|
201
|
-
│ resources: McpResource[], │
|
|
202
|
-
│ status: "connected" | "closed" │
|
|
203
|
-
│ } │
|
|
204
|
-
└───────────────────────────────────────┘
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
## Tool Metadata Collection (NOT Registration)
|
|
208
|
-
|
|
209
|
-
```
|
|
210
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
211
|
-
│ MCP Server Tool Definition │
|
|
212
|
-
│ │
|
|
213
|
-
│ { │
|
|
214
|
-
│ name: "list_sims", │
|
|
215
|
-
│ description: "Lists available iOS simulators", │
|
|
216
|
-
│ inputSchema: { ... } ◄─── NOT converted (MCP server validates) │
|
|
217
|
-
│ } │
|
|
218
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
219
|
-
│
|
|
220
|
-
▼
|
|
221
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
222
|
-
│ Tool Name Formatting │
|
|
223
|
-
│ formatToolName() │
|
|
224
|
-
│ │
|
|
225
|
-
│ Server: "xcodebuild" Tool: "list_sims" │
|
|
226
|
-
│ │
|
|
227
|
-
│ prefix: "server" ──► "xcodebuild_list_sims" │
|
|
228
|
-
│ prefix: "short" ──► "xcodebuild_list_sims" (strips -mcp suffix) │
|
|
229
|
-
│ prefix: "none" ──► "list_sims" (collision risk!) │
|
|
230
|
-
│ │
|
|
231
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
232
|
-
│
|
|
233
|
-
▼
|
|
234
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
235
|
-
│ Tool Metadata (stored in Map) │
|
|
236
|
-
│ NOT registered with Pi! │
|
|
237
|
-
│ │
|
|
238
|
-
│ toolMetadata.set("xcodebuild", [ │
|
|
239
|
-
│ { │
|
|
240
|
-
│ name: "xcodebuild_list_sims", ◄─── Prefixed name (for lookup) │
|
|
241
|
-
│ originalName: "list_sims", ◄─── Original MCP tool name │
|
|
242
|
-
│ description: "Lists available iOS simulators", │
|
|
243
|
-
│ }, │
|
|
244
|
-
│ { │
|
|
245
|
-
│ name: "xcodebuild_get_simulators", │
|
|
246
|
-
│ originalName: "get_simulators", │
|
|
247
|
-
│ description: "Read resource: xcodebuildmcp://simulators", │
|
|
248
|
-
│ resourceUri: "xcodebuildmcp://simulators", ◄─── Resource tools │
|
|
249
|
-
│ }, │
|
|
250
|
-
│ // ... more tools │
|
|
251
|
-
│ ]); │
|
|
252
|
-
│ │
|
|
253
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
## How the LLM Uses MCP Tools
|
|
257
|
-
|
|
258
|
-
```
|
|
259
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
260
|
-
│ LLM SEES (single tool in system prompt): │
|
|
261
|
-
│ │
|
|
262
|
-
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
263
|
-
│ │ Tool: mcp │ │
|
|
264
|
-
│ │ Description: MCP gateway - connect to MCP servers and call tools. │ │
|
|
265
|
-
│ │ │ │
|
|
266
|
-
│ │ Usage: │ │
|
|
267
|
-
│ │ mcp({ }) → Show server status │ │
|
|
268
|
-
│ │ mcp({ server: "name" }) → List tools from server │ │
|
|
269
|
-
│ │ mcp({ search: "query" }) → Search for tools │ │
|
|
270
|
-
│ │ mcp({ describe: "tool_name" }) → Show tool parameters │ │
|
|
271
|
-
│ │ mcp({ tool: "name", args: {...} })→ Call a tool │ │
|
|
272
|
-
│ │ │ │
|
|
273
|
-
│ │ Parameters: │ │
|
|
274
|
-
│ │ tool?: string - Tool name to call │ │
|
|
275
|
-
│ │ args?: object - Arguments for tool call │ │
|
|
276
|
-
│ │ describe?: string - Tool name to describe (shows parameters) │ │
|
|
277
|
-
│ │ search?: string - Search (space-separated words OR'd) │ │
|
|
278
|
-
│ │ server?: string - Filter to specific server │ │
|
|
279
|
-
│ │ regex?: boolean - Treat as regex instead of OR'd words │ │
|
|
280
|
-
│ │ includeSchemas?: boolean - Include schemas (default: true) │ │
|
|
281
|
-
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
282
|
-
│ │
|
|
283
|
-
│ ~200 tokens total (vs ~15,000 for 75 individual tools) │
|
|
284
|
-
│ │
|
|
285
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
286
|
-
│
|
|
287
|
-
│ LLM workflow:
|
|
288
|
-
▼
|
|
289
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
290
|
-
│ │
|
|
291
|
-
│ 1. LLM calls mcp({}) to see what servers are available │
|
|
292
|
-
│ → Returns: "MCP: 1/1 servers, 75 tools\n✓ xcodebuild (75 tools)" │
|
|
293
|
-
│ │
|
|
294
|
-
│ 2. LLM calls mcp({ search: "simulator" }) to find relevant tools │
|
|
295
|
-
│ → Returns: "Found 5 tools matching 'simulator':\n- xcodebuild_..." │
|
|
296
|
-
│ │
|
|
297
|
-
│ 3. LLM calls mcp({ describe: "xcodebuild_boot_sim" }) to see parameters │
|
|
298
|
-
│ → Returns: "Parameters:\n simulatorId (string) *required*\n ..." │
|
|
299
|
-
│ │
|
|
300
|
-
│ 4. LLM calls mcp({ tool: "xcodebuild_boot_sim", args: {...} }) to execute │
|
|
301
|
-
│ → Returns: "Simulator booted successfully" │
|
|
302
|
-
│ │
|
|
303
|
-
│ Note: Step 3 is optional - LLM can skip it and learn from error messages │
|
|
304
|
-
│ which include the expected parameter schema. │
|
|
305
|
-
│ │
|
|
306
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
## Tool Execution Flow
|
|
310
|
-
|
|
311
|
-
```
|
|
312
|
-
┌────────────────────────┐
|
|
313
|
-
│ LLM decides to call: │
|
|
314
|
-
│ mcp({ │
|
|
315
|
-
│ tool: "xcodebuild_ │
|
|
316
|
-
│ list_sims" │
|
|
317
|
-
│ }) │
|
|
318
|
-
└───────────┬────────────┘
|
|
319
|
-
│
|
|
320
|
-
▼
|
|
321
|
-
┌───────────────────────────────────────────────────────────────┐
|
|
322
|
-
│ Pi Tool Executor │
|
|
323
|
-
│ │
|
|
324
|
-
│ Looks up "mcp" in Tool Registry │
|
|
325
|
-
│ Finds the unified MCP proxy tool │
|
|
326
|
-
└───────────────────────────┬───────────────────────────────────┘
|
|
327
|
-
│
|
|
328
|
-
▼
|
|
329
|
-
┌───────────────────────────────────────────────────────────────┐
|
|
330
|
-
│ mcp tool execute() - executeCall() │
|
|
331
|
-
│ │
|
|
332
|
-
│ 1. Look up tool in toolMetadata (may be from cache) │
|
|
333
|
-
│ for (const [server, metadata] of toolMetadata) { │
|
|
334
|
-
│ const found = metadata.find(m => m.name === toolName); │
|
|
335
|
-
│ if (found) { serverName = server; toolMeta = found; } │
|
|
336
|
-
│ } │
|
|
337
|
-
│ If not found: try prefix-match → lazy connect candidate │
|
|
338
|
-
│ │
|
|
339
|
-
│ 2. Ensure connection (lazy connect if needed) │
|
|
340
|
-
│ const connection = manager.getConnection(serverName); │
|
|
341
|
-
│ if (!connection || status !== "connected") │
|
|
342
|
-
│ → check failure backoff (60s) │
|
|
343
|
-
│ → connect, refresh metadata, re-resolve toolMeta │
|
|
344
|
-
│ │
|
|
345
|
-
│ 3. Call MCP server (with in-flight tracking) │
|
|
346
|
-
│ incrementInFlight() + touch() │
|
|
347
|
-
│ if (toolMeta.resourceUri) { │
|
|
348
|
-
│ connection.client.readResource({ uri: resourceUri }); │
|
|
349
|
-
│ } else { │
|
|
350
|
-
│ connection.client.callTool({ │
|
|
351
|
-
│ name: toolMeta.originalName, ◄── Original name! │
|
|
352
|
-
│ arguments: args ?? {} │
|
|
353
|
-
│ }); │
|
|
354
|
-
│ } │
|
|
355
|
-
│ finally { decrementInFlight() + touch() } │
|
|
356
|
-
└───────────────────────────┬───────────────────────────────────┘
|
|
357
|
-
│
|
|
358
|
-
▼
|
|
359
|
-
┌───────────────────────────────────────────────────────────────┐
|
|
360
|
-
│ MCP Protocol │
|
|
361
|
-
│ │
|
|
362
|
-
│ ┌─────────────────┐ ┌─────────────────────────────┐ │
|
|
363
|
-
│ │ Pi MCP Client │ ──────► │ MCP Server (xcodebuild) │ │
|
|
364
|
-
│ │ │ JSON │ │ │
|
|
365
|
-
│ │ callTool() │ RPC │ Validates args (JSON Schema)│ │
|
|
366
|
-
│ │ │ ◄────── │ Executes list_sims │ │
|
|
367
|
-
│ └─────────────────┘ └─────────────────────────────┘ │
|
|
368
|
-
│ │
|
|
369
|
-
│ Transport: stdio (stdin/stdout) or HTTP (StreamableHTTP/SSE) │
|
|
370
|
-
│ Validation: MCP server validates args, not Pi │
|
|
371
|
-
└───────────────────────────┬───────────────────────────────────┘
|
|
372
|
-
│
|
|
373
|
-
▼
|
|
374
|
-
┌───────────────────────────────────────────────────────────────┐
|
|
375
|
-
│ Content Transformation │
|
|
376
|
-
│ transformMcpContent() │
|
|
377
|
-
│ │
|
|
378
|
-
│ MCP Content Types Pi Content Types │
|
|
379
|
-
│ ───────────────── ──────────────── │
|
|
380
|
-
│ { type: "text", ──► { type: "text", │
|
|
381
|
-
│ text: "..." } text: "..." } │
|
|
382
|
-
│ │
|
|
383
|
-
│ { type: "image", ──► { type: "image", │
|
|
384
|
-
│ data: "base64", data: "base64", │
|
|
385
|
-
│ mimeType: "..." } mimeType: "..." } │
|
|
386
|
-
│ │
|
|
387
|
-
│ { type: "resource", ──► { type: "text", │
|
|
388
|
-
│ resource: {...} } text: "[Resource: uri]\n..." } │
|
|
389
|
-
│ │
|
|
390
|
-
│ { type: "resource ──► { type: "text", │
|
|
391
|
-
│ _link", text: "[Resource Link: name]\n │
|
|
392
|
-
│ uri: "..." } URI: uri" } │
|
|
393
|
-
│ │
|
|
394
|
-
│ { type: "audio", ──► { type: "text", │
|
|
395
|
-
│ ... } text: "[Audio content: mime]" } │
|
|
396
|
-
│ │
|
|
397
|
-
└───────────────────────────┬───────────────────────────────────┘
|
|
398
|
-
│
|
|
399
|
-
▼
|
|
400
|
-
┌───────────────────────────────────────────────────────────────┐
|
|
401
|
-
│ Back to LLM │
|
|
402
|
-
│ │
|
|
403
|
-
│ { │
|
|
404
|
-
│ content: [ │
|
|
405
|
-
│ { type: "text", text: "Available iOS Simulators:..." } │
|
|
406
|
-
│ ], │
|
|
407
|
-
│ details: { mode: "call", server: "xcodebuild", ... } │
|
|
408
|
-
│ } │
|
|
409
|
-
│ │
|
|
410
|
-
│ LLM receives the result and continues conversation │
|
|
411
|
-
└───────────────────────────────────────────────────────────────┘
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
## Lifecycle & Health Checks
|
|
415
|
-
|
|
416
|
-
Servers support three lifecycle modes:
|
|
417
|
-
|
|
418
|
-
- **lazy** (default): Don't connect at startup. Connect on first tool call. Subject to idle timeout (default 10 minutes). Cached metadata enables search/list without connections.
|
|
419
|
-
- **eager**: Connect at startup. No idle timeout by default. If the connection drops, reconnects on next use (like lazy).
|
|
420
|
-
- **keep-alive**: Connect at startup. No idle timeout. Auto-reconnects via health checks if the connection drops.
|
|
421
|
-
|
|
422
|
-
```
|
|
423
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
424
|
-
│ Session Start │
|
|
425
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
426
|
-
│
|
|
427
|
-
▼
|
|
428
|
-
┌─────────────────────────────────┐
|
|
429
|
-
│ initializeMcp() │
|
|
430
|
-
│ │
|
|
431
|
-
│ 1. Load config │
|
|
432
|
-
│ 2. Create ServerManager │
|
|
433
|
-
│ 3. Create LifecycleManager │
|
|
434
|
-
│ 4. Load metadata cache │
|
|
435
|
-
│ 5. Register all servers with │
|
|
436
|
-
│ lifecycle manager │
|
|
437
|
-
│ 6. Reconstruct toolMetadata │
|
|
438
|
-
│ from cache (no connection) │
|
|
439
|
-
│ 7. Connect only eager + │
|
|
440
|
-
│ keep-alive servers │
|
|
441
|
-
│ (or all on first-run │
|
|
442
|
-
│ bootstrap) │
|
|
443
|
-
│ 8. Start health checks │
|
|
444
|
-
│ 9. Set reconnect/idle callbacks │
|
|
445
|
-
└─────────────────┬───────────────┘
|
|
446
|
-
│
|
|
447
|
-
▼
|
|
448
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
449
|
-
│ Normal Operation │
|
|
450
|
-
│ │
|
|
451
|
-
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
452
|
-
│ │ Health Check Loop (30s) │ │
|
|
453
|
-
│ │ │ │
|
|
454
|
-
│ │ for each keep-alive server: │ │
|
|
455
|
-
│ │ if (status !== "connected"): │ │
|
|
456
|
-
│ │ try reconnect → onReconnect → updates toolMetadata + cache │ │
|
|
457
|
-
│ │ │ │
|
|
458
|
-
│ │ for each non-keep-alive server: │ │
|
|
459
|
-
│ │ if idle > timeout and inFlight == 0: │ │
|
|
460
|
-
│ │ close connection → onIdleShutdown │ │
|
|
461
|
-
│ │ (toolMetadata preserved for search/list) │ │
|
|
462
|
-
│ │ │ │
|
|
463
|
-
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
464
|
-
│ │
|
|
465
|
-
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
466
|
-
│ │ Lazy Connection (on tool call) │ │
|
|
467
|
-
│ │ │ │
|
|
468
|
-
│ │ executeCall() finds tool in cached metadata but no connection: │ │
|
|
469
|
-
│ │ 1. Check failure backoff (60s) │ │
|
|
470
|
-
│ │ 2. Connect server │ │
|
|
471
|
-
│ │ 3. Refresh metadata from live connection │ │
|
|
472
|
-
│ │ 4. Re-resolve tool (may have changed since cache) │ │
|
|
473
|
-
│ │ 5. Execute tool call with in-flight tracking │ │
|
|
474
|
-
│ │ │ │
|
|
475
|
-
│ │ Prefix-match fallback: if tool name has a server prefix but │ │
|
|
476
|
-
│ │ no metadata, try connecting the matching server │ │
|
|
477
|
-
│ │ │ │
|
|
478
|
-
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
479
|
-
│ │
|
|
480
|
-
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
481
|
-
│ │ /mcp Commands │ │
|
|
482
|
-
│ │ │ │
|
|
483
|
-
│ │ /mcp status - Show all servers and connection status │ │
|
|
484
|
-
│ │ /mcp tools - List all available MCP tools │ │
|
|
485
|
-
│ │ /mcp reconnect - Force reconnect all servers │ │
|
|
486
|
-
│ │ /mcp reconnect <name> - Connect or reconnect a single server │ │
|
|
487
|
-
│ │ │ │
|
|
488
|
-
│ │ /mcp-auth <server> - Show OAuth setup instructions │ │
|
|
489
|
-
│ │ │ │
|
|
490
|
-
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
491
|
-
│ │
|
|
492
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
493
|
-
│
|
|
494
|
-
│ session_shutdown event
|
|
495
|
-
▼
|
|
496
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
497
|
-
│ Graceful Shutdown │
|
|
498
|
-
│ │
|
|
499
|
-
│ 1. Flush metadata cache for all connected servers │
|
|
500
|
-
│ 2. Clear health check interval │
|
|
501
|
-
│ 3. Close all MCP connections (client + transport) │
|
|
502
|
-
│ │
|
|
503
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
## File Structure
|
|
507
|
-
|
|
508
|
-
```
|
|
509
|
-
~/.pi/agent/extensions/pi-mcp-adapter/
|
|
510
|
-
│
|
|
511
|
-
├── index.ts Entry point: unified mcp tool, commands, event handlers
|
|
512
|
-
│ - mcp({}) status, search, list, call modes
|
|
513
|
-
│ - /mcp and /mcp-auth commands
|
|
514
|
-
│ - tool metadata management
|
|
515
|
-
│
|
|
516
|
-
├── types.ts Type definitions, formatToolName()
|
|
517
|
-
│ - McpTool, McpResource, McpContent
|
|
518
|
-
│ - ServerEntry, McpConfig, McpSettings
|
|
519
|
-
│
|
|
520
|
-
├── config.ts Config loading, import merging
|
|
521
|
-
│ - Global, project, and imported configs
|
|
522
|
-
│ - Priority: project > global > imports
|
|
523
|
-
│
|
|
524
|
-
├── server-manager.ts MCP connection management
|
|
525
|
-
│ - stdio transport
|
|
526
|
-
│ - HTTP transport (StreamableHTTP + SSE fallback)
|
|
527
|
-
│ - connection pooling and deduplication
|
|
528
|
-
│
|
|
529
|
-
├── tool-registrar.ts MCP content transformation
|
|
530
|
-
│ - transformMcpContent() - MCP → Pi content
|
|
531
|
-
│
|
|
532
|
-
├── resource-tools.ts Resource name utilities
|
|
533
|
-
│ - resourceNameToToolName()
|
|
534
|
-
│
|
|
535
|
-
├── metadata-cache.ts Persistent tool/resource metadata cache
|
|
536
|
-
│ - Per-server cache at ~/.pi/agent/mcp-cache.json
|
|
537
|
-
│ - Config hashing, staleness checks, reconstruction
|
|
538
|
-
│ - Read-merge-write for multi-session safety
|
|
539
|
-
│
|
|
540
|
-
├── npx-resolver.ts npx binary resolution (skip npm parent process)
|
|
541
|
-
│ - Probes ~/.npm/_npx/ cache directly
|
|
542
|
-
│ - Persistent cache at ~/.pi/agent/mcp-npx-cache.json
|
|
543
|
-
│ - JS detection (extension + shebang)
|
|
544
|
-
│
|
|
545
|
-
├── lifecycle.ts Health checks, reconnection, idle timeout
|
|
546
|
-
│ - keep-alive server tracking + auto-reconnect
|
|
547
|
-
│ - Idle timeout for lazy/eager servers
|
|
548
|
-
│ - Per-server and global timeout settings
|
|
549
|
-
│
|
|
550
|
-
├── oauth-handler.ts OAuth token file reading
|
|
551
|
-
│ - getStoredTokens() from ~/.pi/agent/mcp-oauth/
|
|
552
|
-
│
|
|
553
|
-
├── package.json Dependencies (@modelcontextprotocol/sdk)
|
|
554
|
-
│
|
|
555
|
-
└── tsconfig.json TypeScript configuration
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
## Key Design Decisions
|
|
559
|
-
|
|
560
|
-
```
|
|
561
|
-
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
562
|
-
│ │
|
|
563
|
-
│ 1. SINGLE PROXY TOOL (token efficiency) │
|
|
564
|
-
│ ──────────────────────────────────── │
|
|
565
|
-
│ Only ONE tool ("mcp") is registered with Pi. │
|
|
566
|
-
│ LLM discovers MCP tools on-demand via search/list. │
|
|
567
|
-
│ Saves ~15,000 tokens for a server with 75 tools. │
|
|
568
|
-
│ │
|
|
569
|
-
│ mcp({ tool: "xcodebuild_list_sims" }) // call │
|
|
570
|
-
│ mcp({ search: "simulator" }) // search │
|
|
571
|
-
│ mcp({ server: "xcodebuild" }) // list │
|
|
572
|
-
│ │
|
|
573
|
-
│ 2. SCHEMA ON-DEMAND (describe mode + error enhancement) │
|
|
574
|
-
│ ──────────────────────────────────────────────────── │
|
|
575
|
-
│ Schemas stored in metadata, formatted to human-readable on request. │
|
|
576
|
-
│ - mcp({ describe: "tool" }) returns full description + parameters │
|
|
577
|
-
│ - Error responses include expected parameters to help self-correct │
|
|
578
|
-
│ MCP server still does final validation - we just help the LLM. │
|
|
579
|
-
│ │
|
|
580
|
-
│ 3. METADATA-BASED LOOKUP │
|
|
581
|
-
│ ──────────────────────── │
|
|
582
|
-
│ Tool metadata stored in Map<server, ToolMetadata[]> │
|
|
583
|
-
│ executeCall() looks up tool by prefixed name → finds server + original │
|
|
584
|
-
│ name → calls MCP server with original name. │
|
|
585
|
-
│ │
|
|
586
|
-
│ 4. HTTP TRANSPORT FALLBACK │
|
|
587
|
-
│ ──────────────────────── │
|
|
588
|
-
│ Try StreamableHTTP first (modern), fall back to SSE (legacy). │
|
|
589
|
-
│ Probe with a test connection, close it, create fresh for real use. │
|
|
590
|
-
│ │
|
|
591
|
-
│ 5. TOOL PREFIXING │
|
|
592
|
-
│ ─────────────── │
|
|
593
|
-
│ Default "server" prefix prevents tool name collisions. │
|
|
594
|
-
│ "short" removes -mcp suffix for cleaner names. │
|
|
595
|
-
│ "none" is risky but available for single-server setups. │
|
|
596
|
-
│ │
|
|
597
|
-
│ 6. CONFIG IMPORT │
|
|
598
|
-
│ ───────────── │
|
|
599
|
-
│ Can import from Cursor, Claude, VSCode, etc. │
|
|
600
|
-
│ Allows reusing existing MCP configurations. │
|
|
601
|
-
│ Priority: project > global > imports │
|
|
602
|
-
│ │
|
|
603
|
-
│ 7. RECONNECT CALLBACK │
|
|
604
|
-
│ ────────────────── │
|
|
605
|
-
│ Lifecycle manager notifies extension after auto-reconnect. │
|
|
606
|
-
│ Extension updates tool metadata so proxy can find tools. │
|
|
607
|
-
│ │
|
|
608
|
-
│ 8. LAZY BY DEFAULT │
|
|
609
|
-
│ ─────────────── │
|
|
610
|
-
│ All servers default to lifecycle: "lazy". They only connect │
|
|
611
|
-
│ when a tool call needs them. Cached metadata enables │
|
|
612
|
-
│ search/list/describe without live connections. Idle servers │
|
|
613
|
-
│ are disconnected after 10 minutes (configurable). │
|
|
614
|
-
│ │
|
|
615
|
-
│ 9. METADATA CACHE │
|
|
616
|
-
│ ────────────── │
|
|
617
|
-
│ ~/.pi/agent/mcp-cache.json stores per-server tool/resource │
|
|
618
|
-
│ metadata with config hash validation and 7-day staleness. │
|
|
619
|
-
│ Cache stores original MCP names (not prefixed) — toolPrefix │
|
|
620
|
-
│ changes never invalidate the cache. Read-merge-write with │
|
|
621
|
-
│ per-process tmp files for multi-session safety. │
|
|
622
|
-
│ │
|
|
623
|
-
│ 10. NPX RESOLUTION │
|
|
624
|
-
│ ─────────────── │
|
|
625
|
-
│ npx-based servers resolve to direct binary paths, eliminating │
|
|
626
|
-
│ the ~143 MB npm parent process per server. Probes ~/.npm/_npx/ │
|
|
627
|
-
│ cache directly. JS files run via node, others executed directly. │
|
|
628
|
-
│ │
|
|
629
|
-
└─────────────────────────────────────────────────────────────────────────────┘
|
|
630
|
-
```
|