@fitchmultz/pi-mcp-adapter 5.0.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.
Files changed (176) hide show
  1. package/CHANGELOG.md +750 -0
  2. package/LICENSE +21 -0
  3. package/OAUTH.md +462 -0
  4. package/README.md +820 -0
  5. package/abort.ts +34 -0
  6. package/agent-dir.ts +24 -0
  7. package/app-bridge.bundle.js +67 -0
  8. package/banner.png +0 -0
  9. package/checkpoint.ts +55 -0
  10. package/cli.js +222 -0
  11. package/commands.ts +619 -0
  12. package/config.ts +1204 -0
  13. package/consent-manager.ts +25 -0
  14. package/direct-tools.ts +408 -0
  15. package/dist/abort.d.ts +2 -0
  16. package/dist/abort.js +35 -0
  17. package/dist/agent-dir.d.ts +3 -0
  18. package/dist/agent-dir.js +21 -0
  19. package/dist/app-bridge.bundle.js +67 -0
  20. package/dist/checkpoint.d.ts +18 -0
  21. package/dist/checkpoint.js +53 -0
  22. package/dist/commands.d.ts +20 -0
  23. package/dist/commands.js +521 -0
  24. package/dist/config.d.ts +109 -0
  25. package/dist/config.js +1013 -0
  26. package/dist/consent-manager.d.ts +8 -0
  27. package/dist/consent-manager.js +21 -0
  28. package/dist/direct-tools.d.ts +10 -0
  29. package/dist/direct-tools.js +350 -0
  30. package/dist/docs/client-metadata.json +14 -0
  31. package/dist/elicitation-handler.d.ts +17 -0
  32. package/dist/elicitation-handler.js +308 -0
  33. package/dist/error-signal.d.ts +15 -0
  34. package/dist/error-signal.js +21 -0
  35. package/dist/glimpse-ui.d.ts +9 -0
  36. package/dist/glimpse-ui.js +75 -0
  37. package/dist/host-html-template.d.ts +15 -0
  38. package/dist/host-html-template.js +429 -0
  39. package/dist/index.d.ts +7 -0
  40. package/dist/index.js +840 -0
  41. package/dist/init.d.ts +25 -0
  42. package/dist/init.js +562 -0
  43. package/dist/legacy-migration.d.ts +18 -0
  44. package/dist/legacy-migration.js +45 -0
  45. package/dist/lifecycle.d.ts +40 -0
  46. package/dist/lifecycle.js +167 -0
  47. package/dist/logger.d.ts +38 -0
  48. package/dist/logger.js +102 -0
  49. package/dist/mcp-auth-flow.d.ts +113 -0
  50. package/dist/mcp-auth-flow.js +834 -0
  51. package/dist/mcp-auth.d.ts +127 -0
  52. package/dist/mcp-auth.js +658 -0
  53. package/dist/mcp-callback-server.d.ts +56 -0
  54. package/dist/mcp-callback-server.js +371 -0
  55. package/dist/mcp-code.d.ts +51 -0
  56. package/dist/mcp-code.js +366 -0
  57. package/dist/mcp-keyring-helper.cjs +89 -0
  58. package/dist/mcp-oauth-provider.d.ts +154 -0
  59. package/dist/mcp-oauth-provider.js +672 -0
  60. package/dist/mcp-output-guard.d.ts +59 -0
  61. package/dist/mcp-output-guard.js +332 -0
  62. package/dist/mcp-panel.d.ts +66 -0
  63. package/dist/mcp-panel.js +889 -0
  64. package/dist/mcp-script-worker.mjs +128 -0
  65. package/dist/mcp-setup-panel.d.ts +84 -0
  66. package/dist/mcp-setup-panel.js +561 -0
  67. package/dist/mcp-status.d.ts +11 -0
  68. package/dist/mcp-status.js +96 -0
  69. package/dist/mcp-trace.d.ts +95 -0
  70. package/dist/mcp-trace.js +233 -0
  71. package/dist/metadata-cache.d.ts +18 -0
  72. package/dist/metadata-cache.js +277 -0
  73. package/dist/npx-resolver.d.ts +6 -0
  74. package/dist/npx-resolver.js +448 -0
  75. package/dist/onboarding-state.d.ts +11 -0
  76. package/dist/onboarding-state.js +47 -0
  77. package/dist/panel-keys.d.ts +16 -0
  78. package/dist/panel-keys.js +15 -0
  79. package/dist/prompts.d.ts +56 -0
  80. package/dist/prompts.js +284 -0
  81. package/dist/proxy-modes.d.ts +38 -0
  82. package/dist/proxy-modes.js +1235 -0
  83. package/dist/resource-tools.d.ts +1 -0
  84. package/dist/resource-tools.js +14 -0
  85. package/dist/runtime-owner.d.ts +18 -0
  86. package/dist/runtime-owner.js +117 -0
  87. package/dist/sampling-handler.d.ts +15 -0
  88. package/dist/sampling-handler.js +211 -0
  89. package/dist/search-ranking.d.ts +20 -0
  90. package/dist/search-ranking.js +180 -0
  91. package/dist/server-manager.d.ts +114 -0
  92. package/dist/server-manager.js +1175 -0
  93. package/dist/session-recovery.d.ts +31 -0
  94. package/dist/session-recovery.js +310 -0
  95. package/dist/state.d.ts +63 -0
  96. package/dist/state.js +1 -0
  97. package/dist/tool-approval.d.ts +10 -0
  98. package/dist/tool-approval.js +42 -0
  99. package/dist/tool-metadata.d.ts +9 -0
  100. package/dist/tool-metadata.js +193 -0
  101. package/dist/tool-registrar.d.ts +10 -0
  102. package/dist/tool-registrar.js +64 -0
  103. package/dist/tool-result-renderer.d.ts +45 -0
  104. package/dist/tool-result-renderer.js +224 -0
  105. package/dist/ts-shape.d.ts +2 -0
  106. package/dist/ts-shape.js +147 -0
  107. package/dist/types.d.ts +516 -0
  108. package/dist/types.js +168 -0
  109. package/dist/ui-resource-handler.d.ts +17 -0
  110. package/dist/ui-resource-handler.js +209 -0
  111. package/dist/ui-server.d.ts +51 -0
  112. package/dist/ui-server.js +669 -0
  113. package/dist/ui-session.d.ts +39 -0
  114. package/dist/ui-session.js +463 -0
  115. package/dist/ui-stream-types.d.ts +104 -0
  116. package/dist/ui-stream-types.js +58 -0
  117. package/dist/ui-tool-visibility.d.ts +4 -0
  118. package/dist/ui-tool-visibility.js +26 -0
  119. package/dist/unix-socket-transport.d.ts +15 -0
  120. package/dist/unix-socket-transport.js +84 -0
  121. package/dist/utils.d.ts +30 -0
  122. package/dist/utils.js +276 -0
  123. package/docs/client-metadata.json +14 -0
  124. package/elicitation-handler.ts +349 -0
  125. package/error-signal.ts +21 -0
  126. package/glimpse-ui.ts +80 -0
  127. package/host-html-template.ts +456 -0
  128. package/index.ts +900 -0
  129. package/init.ts +627 -0
  130. package/legacy-migration.ts +47 -0
  131. package/lifecycle.ts +165 -0
  132. package/logger.ts +127 -0
  133. package/mcp-auth-flow.ts +975 -0
  134. package/mcp-auth.ts +793 -0
  135. package/mcp-callback-server.ts +450 -0
  136. package/mcp-code.ts +386 -0
  137. package/mcp-keyring-helper.cjs +89 -0
  138. package/mcp-oauth-provider.ts +774 -0
  139. package/mcp-output-guard.ts +412 -0
  140. package/mcp-panel.ts +978 -0
  141. package/mcp-script-worker.mjs +128 -0
  142. package/mcp-setup-panel.ts +666 -0
  143. package/mcp-status.ts +109 -0
  144. package/mcp-trace.ts +295 -0
  145. package/metadata-cache.ts +333 -0
  146. package/npx-resolver.ts +488 -0
  147. package/onboarding-state.ts +58 -0
  148. package/package.json +182 -0
  149. package/panel-keys.ts +37 -0
  150. package/prompts.ts +337 -0
  151. package/proxy-modes.ts +1361 -0
  152. package/resource-tools.ts +17 -0
  153. package/runtime-owner.ts +123 -0
  154. package/sampling-handler.ts +273 -0
  155. package/scripts/build.mjs +131 -0
  156. package/scripts/prepare.mjs +102 -0
  157. package/search-ranking.ts +196 -0
  158. package/server-manager.ts +1320 -0
  159. package/session-recovery.ts +316 -0
  160. package/skills/mcp-scripting/SKILL.md +38 -0
  161. package/state.ts +64 -0
  162. package/tool-approval.ts +78 -0
  163. package/tool-metadata.ts +235 -0
  164. package/tool-registrar.ts +69 -0
  165. package/tool-result-renderer.ts +286 -0
  166. package/ts-shape.ts +145 -0
  167. package/tsconfig.build.json +13 -0
  168. package/tsconfig.json +16 -0
  169. package/types.ts +810 -0
  170. package/ui-resource-handler.ts +249 -0
  171. package/ui-server.ts +808 -0
  172. package/ui-session.ts +568 -0
  173. package/ui-stream-types.ts +89 -0
  174. package/ui-tool-visibility.ts +25 -0
  175. package/unix-socket-transport.ts +85 -0
  176. package/utils.ts +309 -0
package/banner.png ADDED
Binary file
package/checkpoint.ts ADDED
@@ -0,0 +1,55 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import type { McpExtensionState } from "./state.ts";
3
+ import { flushMetadataCache } from "./init.ts";
4
+ import { getOAuthCheckpointBlocker, holdOAuthCheckpoint } from "./mcp-auth-flow.ts";
5
+ import { isServerDisabled } from "./types.ts";
6
+
7
+ /** Optional host event. Published Pi hosts without this event simply never emit it.
8
+ * Keep its structural typing local; do not require a private host package/version. */
9
+ export interface McpCheckpointEvent {
10
+ readonly boundary: "turn" | "settled";
11
+ readonly signal: AbortSignal;
12
+ invalidate(): void;
13
+ }
14
+ export type McpCheckpointResult = { sleepReady: true } | { sleepReady: false; reason: string };
15
+ export function onSessionCheckpoint(pi: ExtensionAPI, handler: (event: McpCheckpointEvent) => Promise<McpCheckpointResult>): void {
16
+ (pi.on as unknown as (event: "session_checkpoint", callback: (event: McpCheckpointEvent) => Promise<McpCheckpointResult>) => unknown)("session_checkpoint", handler);
17
+ }
18
+
19
+ /** No shutdown, credential copying, connection replay or permanent owner cancellation. */
20
+ export async function prepareMcpCheckpoint(state: McpExtensionState, event: McpCheckpointEvent): Promise<McpCheckpointResult> {
21
+ event.signal.throwIfAborted();
22
+ const releases: Array<() => void> = [];
23
+ const release = () => { for (const undo of releases.splice(0).reverse()) undo(); };
24
+ // Fence all owned sources synchronously, before the first await (including trace flush).
25
+ event.signal.addEventListener("abort", release, { once: true });
26
+ const undo = () => { release(); event.signal.removeEventListener("abort", release); };
27
+ const blocked = (reason: string): McpCheckpointResult => { undo(); return { sleepReady: false, reason }; };
28
+ try {
29
+ releases.push(state.owner.holdCheckpoint(event));
30
+ releases.push(state.manager.holdCheckpoint(event));
31
+ releases.push(holdOAuthCheckpoint(state.oauthRuntime, event));
32
+ releases.push(state.lifecycle.pauseForCheckpoint());
33
+ if (state.lifecycle.hasActiveHealthCheck()) return blocked("MCP health check is active");
34
+ for (const [name, definition] of Object.entries(state.config.mcpServers)) {
35
+ if (!isServerDisabled(definition) && !definition.url) return blocked(`MCP ${name}: stdio/Unix server state is not reconstructible`);
36
+ }
37
+ const callback = state.owner.getCheckpointBlocker();
38
+ if (callback) return blocked(callback);
39
+ if (state.activeScripts) return blocked("MCP script is active");
40
+ if (state.uiServer || state.completedUiSessions.length) return blocked("MCP UI session/messages require the running runtime");
41
+ if (state.approvedToolCalls.size || state.consentManager.hasDecisions()) return blocked("MCP session approvals are not persisted");
42
+ const oauth = getOAuthCheckpointBlocker(state.oauthRuntime);
43
+ if (oauth) return blocked(oauth);
44
+ const manager = state.manager.getCheckpointBlocker();
45
+ if (manager) return blocked(manager);
46
+ flushMetadataCache(state);
47
+ await state.manager.flushForCheckpoint();
48
+ event.signal.throwIfAborted();
49
+ // Release/cancellation is host-owned. Existing clients/timers remain usable afterwards.
50
+ return { sleepReady: true };
51
+ } catch (error) {
52
+ undo();
53
+ throw error;
54
+ }
55
+ }
package/cli.js ADDED
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import os from "node:os";
6
+ import { pathToFileURL } from "node:url";
7
+ import stripJsonComments from "strip-json-comments";
8
+
9
+ const HOME = os.homedir();
10
+
11
+ function expandHome(input) {
12
+ if (input === "~") return HOME;
13
+ if (input.startsWith("~/")) return path.resolve(HOME, input.slice(2));
14
+ return path.resolve(input);
15
+ }
16
+
17
+ const AGENT_DIR = process.env.PI_CODING_AGENT_DIR?.trim()
18
+ ? expandHome(process.env.PI_CODING_AGENT_DIR.trim())
19
+ : path.join(HOME, ".pi", "agent");
20
+ const PI_CONFIG_PATH = path.join(AGENT_DIR, "fitch-mcp-adapter", "mcp.json");
21
+ const GENERIC_GLOBAL_CONFIG_PATH = path.join(HOME, ".config", "mcp", "mcp.json");
22
+ const AGENTS_GLOBAL_CONFIG_PATH = path.join(HOME, ".agents", "mcp.json");
23
+ const AGENTS_NESTED_GLOBAL_CONFIG_PATH = path.join(HOME, ".agents", "mcp", "mcp.json");
24
+ const PROJECT_CONFIG_PATH = path.resolve(process.cwd(), ".mcp.json");
25
+ const PROJECT_PI_CONFIG_PATH = path.resolve(process.cwd(), ".pi", "fitch-mcp-adapter", "mcp.json");
26
+
27
+ const IMPORT_PATHS = {
28
+ cursor: [path.join(HOME, ".cursor", "mcp.json")],
29
+ "claude-code": [
30
+ path.join(HOME, ".claude", "mcp.json"),
31
+ path.join(HOME, ".claude.json"),
32
+ path.join(HOME, ".claude", "claude_desktop_config.json"),
33
+ ],
34
+ "claude-desktop": [path.join(HOME, "Library", "Application Support", "Claude", "claude_desktop_config.json")],
35
+ codex: [
36
+ path.join(HOME, ".codex", "config.toml"),
37
+ path.join(HOME, ".codex", "config.json"),
38
+ ],
39
+ opencode: [
40
+ path.join(HOME, ".config", "opencode", "opencode.json"),
41
+ path.resolve(process.cwd(), "opencode.json"),
42
+ ],
43
+ windsurf: [path.join(HOME, ".windsurf", "mcp.json")],
44
+ vscode: [path.resolve(process.cwd(), ".vscode", "mcp.json")],
45
+ };
46
+
47
+ function printHelp(log = console.log) {
48
+ log("Fitch MCP Adapter helper\n");
49
+ log("Install the package with:");
50
+ log(" pi install npm:@fitchmultz/pi-mcp-adapter\n");
51
+ log("Then optionally run:");
52
+ log(" fitch-mcp-adapter init Detect host configs and scaffold Pi imports");
53
+ log(" fitch-mcp-adapter init --dry-run");
54
+ log(" fitch-mcp-adapter init --discover-host-configs Opt in to host config fallback discovery");
55
+ log(" fitch-mcp-adapter migrate [--dry-run] Copy pre-v5 state without changing its sources");
56
+ }
57
+
58
+ function readJsonFile(filePath) {
59
+ return JSON.parse(stripJsonComments(fs.readFileSync(filePath, "utf-8"), { trailingCommas: true }));
60
+ }
61
+
62
+ function loadPiConfig() {
63
+ if (!fs.existsSync(PI_CONFIG_PATH)) {
64
+ return { mcpServers: {} };
65
+ }
66
+
67
+ const raw = readJsonFile(PI_CONFIG_PATH);
68
+ const mcpServers = raw.mcpServers ?? raw["mcp-servers"] ?? {};
69
+ if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) {
70
+ throw new Error(`Invalid MCP config at ${PI_CONFIG_PATH}: expected \"mcpServers\" to be an object`);
71
+ }
72
+
73
+ const normalized = { ...raw };
74
+ delete normalized["mcp-servers"];
75
+
76
+ const imports = Array.isArray(raw.imports) ? raw.imports.filter((value) => typeof value === "string") : undefined;
77
+ return {
78
+ ...normalized,
79
+ mcpServers,
80
+ imports,
81
+ };
82
+ }
83
+
84
+ function findAvailableImports() {
85
+ const found = [];
86
+
87
+ for (const [kind, candidates] of Object.entries(IMPORT_PATHS)) {
88
+ const existing = candidates.find((candidate) => fs.existsSync(candidate));
89
+ if (existing) {
90
+ found.push({ kind, path: existing });
91
+ }
92
+ }
93
+
94
+ return found;
95
+ }
96
+
97
+ function printDiscovery(log, imports) {
98
+ log("Config discovery:\n");
99
+
100
+ const paths = [
101
+ ["User-global standard MCP", GENERIC_GLOBAL_CONFIG_PATH],
102
+ ["User-global .agents MCP", AGENTS_GLOBAL_CONFIG_PATH],
103
+ ["User-global .agents nested MCP", AGENTS_NESTED_GLOBAL_CONFIG_PATH],
104
+ ["Pi global override", PI_CONFIG_PATH],
105
+ ["Project standard MCP", PROJECT_CONFIG_PATH],
106
+ ["Project Pi override", PROJECT_PI_CONFIG_PATH],
107
+ ];
108
+
109
+ for (const [label, filePath] of paths) {
110
+ const prefix = fs.existsSync(filePath) ? "✓" : "-";
111
+ log(`${prefix} ${label}: ${filePath}`);
112
+ }
113
+
114
+ log("\nCompatibility imports:\n");
115
+ if (imports.length === 0) {
116
+ log("- No host-specific MCP configs detected");
117
+ return;
118
+ }
119
+
120
+ for (const entry of imports) {
121
+ log(`✓ ${entry.kind}: ${entry.path}`);
122
+ }
123
+ }
124
+
125
+ function writePiConfig(config) {
126
+ fs.mkdirSync(path.dirname(PI_CONFIG_PATH), { recursive: true });
127
+ fs.writeFileSync(PI_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
128
+ }
129
+
130
+ async function runInit(argv, log = console.log) {
131
+ const dryRun = argv.includes("--dry-run");
132
+ const discoverHostConfigs = argv.includes("--discover-host-configs");
133
+ const foundImports = findAvailableImports();
134
+ const existingConfig = loadPiConfig();
135
+ const existingImports = new Set(existingConfig.imports ?? []);
136
+ const importsToAdd = foundImports
137
+ .map((entry) => entry.kind)
138
+ .filter((kind) => !existingImports.has(kind));
139
+
140
+ printDiscovery(log, foundImports);
141
+
142
+ const discoverySettingChanged = discoverHostConfigs && existingConfig.settings?.hostConfigDiscovery !== "on";
143
+ if (importsToAdd.length === 0 && !discoverySettingChanged) {
144
+ log("\nNo Pi config changes needed.");
145
+ log("Standard MCP configs are discovered automatically, and host-specific imports are already configured or unavailable.");
146
+ return 0;
147
+ }
148
+
149
+ const nextConfig = {
150
+ ...existingConfig,
151
+ ...(discoverySettingChanged ? { settings: { ...existingConfig.settings, hostConfigDiscovery: "on" } } : {}),
152
+ ...(importsToAdd.length > 0 ? { imports: [...existingImports, ...importsToAdd] } : {}),
153
+ mcpServers: existingConfig.mcpServers ?? {},
154
+ };
155
+
156
+ if (importsToAdd.length > 0) {
157
+ log(`\nDetected host configs to import into Pi: ${importsToAdd.join(", ")}`);
158
+ }
159
+ if (discoverySettingChanged) {
160
+ log("Opting in to host-specific fallback discovery (standard and Pi-owned configs still take precedence).");
161
+ }
162
+
163
+ if (dryRun) {
164
+ log(`Dry run: would update ${PI_CONFIG_PATH}`);
165
+ return 0;
166
+ }
167
+
168
+ writePiConfig(nextConfig);
169
+ log(`Updated ${PI_CONFIG_PATH}`);
170
+ log("Pi will now keep reading standard MCP configs automatically, while these imports cover host-specific config formats.");
171
+ if (discoverySettingChanged) {
172
+ log("Host config discovery is explicit and does not write to or execute commands from external host files.");
173
+ }
174
+ return 0;
175
+ }
176
+
177
+ export async function main(argv = process.argv.slice(2), log = console.log, error = console.error) {
178
+ const [command, ...rest] = argv;
179
+
180
+ if (!command || command === "help" || command === "--help" || command === "-h") {
181
+ printHelp(log);
182
+ return 0;
183
+ }
184
+
185
+ if (command === "install") {
186
+ error("The custom downloader has been retired.");
187
+ error("Use `pi install npm:@fitchmultz/pi-mcp-adapter` instead, then optionally run `fitch-mcp-adapter init`.");
188
+ return 1;
189
+ }
190
+
191
+ if (command === "init") {
192
+ return runInit(rest, log);
193
+ }
194
+
195
+ if (command === "migrate") {
196
+ const { migrateLegacyState } = await import("./dist/legacy-migration.js");
197
+ const dryRun = rest.includes("--dry-run");
198
+ const result = migrateLegacyState({ dryRun });
199
+ for (const file of result.files) log(`${file.status}: ${file.source} -> ${file.destination}`);
200
+ for (const credential of result.credentials) log(`OAuth ${credential.server}: ${credential.status}`);
201
+ log(dryRun
202
+ ? "Dry run: no files changed and no credential store accessed."
203
+ : "Migration complete. Existing destinations and all source files/credentials were left intact. Restart Pi to use the independent adapter.");
204
+ return 0;
205
+ }
206
+
207
+ error(`Unknown command: ${command}`);
208
+ printHelp(log);
209
+ return 1;
210
+ }
211
+
212
+ const resolvedEntrypoint = process.argv[1] ? fs.realpathSync(process.argv[1]) : undefined;
213
+ const isEntrypoint = resolvedEntrypoint && import.meta.url === pathToFileURL(resolvedEntrypoint).href;
214
+
215
+ if (isEntrypoint) {
216
+ main().then((code) => {
217
+ process.exitCode = code;
218
+ }).catch((err) => {
219
+ console.error(`\nHelper failed: ${err instanceof Error ? err.message : String(err)}`);
220
+ process.exit(1);
221
+ });
222
+ }