@runfusion/fusion 0.48.0 → 0.50.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 (140) hide show
  1. package/dist/bin.js +43336 -15114
  2. package/dist/client/.vite/manifest.json +250 -249
  3. package/dist/client/assets/AgentDetailView-BzSyldiW.css +1 -0
  4. package/dist/client/assets/{AgentDetailView-CF6isJ51.js → AgentDetailView-D2oYUApl.js} +5 -5
  5. package/dist/client/assets/{AgentPermissionPolicyEditor-DFldf6aS.js → AgentPermissionPolicyEditor-DGgx46f3.js} +1 -1
  6. package/dist/client/assets/AgentsView-Dhf5Vq_T.js +29 -0
  7. package/dist/client/assets/ChatView-DpgpxHva.js +19 -0
  8. package/dist/client/assets/ChatView-DwIIrJxb.css +1 -0
  9. package/dist/client/assets/CommandCenter-B9q-zM9p.css +1 -0
  10. package/dist/client/assets/CommandCenter-DzQhGq_J.js +71 -0
  11. package/dist/client/assets/{DevServerView-D6RFaGPU.js → DevServerView-DjJYp6Jj.js} +1 -1
  12. package/dist/client/assets/{DirectoryPicker-DNBB-viz.js → DirectoryPicker-CYPYp9T4.js} +1 -1
  13. package/dist/client/assets/{DocumentsView-DXG-sctG.js → DocumentsView-CeQVRVdp.js} +1 -1
  14. package/dist/client/assets/{EvalsView-Ol6ECKz5.js → EvalsView-D250mVR5.js} +1 -1
  15. package/dist/client/assets/{ExperimentalAgentOnboardingModal-BQc3GaE_.js → ExperimentalAgentOnboardingModal-B1EEY_bl.js} +1 -1
  16. package/dist/client/assets/{GoalsView-C_m90anC.js → GoalsView-MftVwE9g.js} +1 -1
  17. package/dist/client/assets/{InsightsView-BbFgPsDH.js → InsightsView-CbWItYe8.js} +1 -1
  18. package/dist/client/assets/{MemoryView-CRS7AjWK.js → MemoryView-Ct0nOTES.js} +1 -1
  19. package/dist/client/assets/{PiExtensionsManager-BEj5xu45.js → PiExtensionsManager-DSQwngX-.js} +1 -1
  20. package/dist/client/assets/{PluginManager-CzCM3XUY.js → PluginManager-DqLxCiNg.js} +1 -1
  21. package/dist/client/assets/{PullRequestView-9yNYCunb.js → PullRequestView-D0WgwdAi.js} +1 -1
  22. package/dist/client/assets/{ResearchView-BKdirpiw.js → ResearchView-CJQL9XKC.js} +1 -1
  23. package/dist/client/assets/{SecretsView-qGjo-0Cp.js → SecretsView-ChN9aTbi.js} +1 -1
  24. package/dist/client/assets/{SessionTerminal-C7YlkdGJ.js → SessionTerminal-BVon9kbr.js} +1 -1
  25. package/dist/client/assets/SettingsModal-B4WANQnu.js +21 -0
  26. package/dist/client/assets/{SettingsModal-CvbiSIHS.css → SettingsModal-BzZNPNIh.css} +1 -1
  27. package/dist/client/assets/{SettingsModal-DihNd7g8.js → SettingsModal-CIC5jhgo.js} +1 -1
  28. package/dist/client/assets/SettingsModal-tUDR6nJ9.css +1 -0
  29. package/dist/client/assets/{SetupWizardModal-MovTj5pY.js → SetupWizardModal-C6EGjmbd.js} +2 -2
  30. package/dist/client/assets/{ShadcnColorPicker-Bnf5n5R4.js → ShadcnColorPicker-4e_aXoGX.js} +1 -1
  31. package/dist/client/assets/{ShadcnColorPicker-pqVWCC5-.css → ShadcnColorPicker-CzZo9UOD.css} +1 -1
  32. package/dist/client/assets/SkillsView-Dz8dDwfP.js +1 -0
  33. package/dist/client/assets/SkillsView-tAW7TTCz.css +1 -0
  34. package/dist/client/assets/{TodoView-CIsL_uvQ.css → TodoView--YoVYWvw.css} +1 -1
  35. package/dist/client/assets/{TodoView-BEdk6FTU.js → TodoView-DvJFzoOv.js} +1 -1
  36. package/dist/client/assets/WorkflowNodeEditor-DrdTCstS.js +8 -0
  37. package/dist/client/assets/WorkflowNodeEditor-WYIxHTxY.css +1 -0
  38. package/dist/client/assets/{agentSkills-D5nDZPRY.js → agentSkills-B2dD5Nm5.js} +1 -1
  39. package/dist/client/assets/app-BYq4S7Cb.js +13 -0
  40. package/dist/client/assets/app-D27ewSC0.js +13 -0
  41. package/dist/client/assets/app-DFLDkeOE.js +13 -0
  42. package/dist/client/assets/app-OPrfgjjn.js +13 -0
  43. package/dist/client/assets/app-dATBczl3.js +13 -0
  44. package/dist/client/assets/{app-D_IT9Buz.js → app-k1K9zAjp.js} +3 -3
  45. package/dist/client/assets/{architectureDiagram-3BPJPVTR-BbkXWBni.js → architectureDiagram-3BPJPVTR-ClO0MnzR.js} +1 -1
  46. package/dist/client/assets/{blockDiagram-GPEHLZMM-DhaqpHqZ.js → blockDiagram-GPEHLZMM-C4cyy4WE.js} +1 -1
  47. package/dist/client/assets/{c4Diagram-AAUBKEIU-qC0WSHMZ.js → c4Diagram-AAUBKEIU-NHPC9TJo.js} +1 -1
  48. package/dist/client/assets/channel-Q2WKocEP.js +1 -0
  49. package/dist/client/assets/{chunk-2J33WTMH-Dah9zMeT.js → chunk-2J33WTMH-CGYIKTVR.js} +1 -1
  50. package/dist/client/assets/{chunk-4BX2VUAB-epHwnUb_.js → chunk-4BX2VUAB-BkCCD0tE.js} +1 -1
  51. package/dist/client/assets/{chunk-55IACEB6-DpYBy44g.js → chunk-55IACEB6-Be3yOIDe.js} +1 -1
  52. package/dist/client/assets/{chunk-727SXJPM-B9b8698e.js → chunk-727SXJPM-DxjyMaQQ.js} +1 -1
  53. package/dist/client/assets/{chunk-AQP2D5EJ-DrM8nnhR.js → chunk-AQP2D5EJ-BC0PjN0W.js} +1 -1
  54. package/dist/client/assets/{chunk-FMBD7UC4-jyT6Scwe.js → chunk-FMBD7UC4-DoWxmM8k.js} +1 -1
  55. package/dist/client/assets/{chunk-ND2GUHAM-EoPfF-PK.js → chunk-ND2GUHAM-EOpuc9pP.js} +1 -1
  56. package/dist/client/assets/{chunk-QZHKN3VN-qMwLVfgJ.js → chunk-QZHKN3VN-DiRyx9QZ.js} +1 -1
  57. package/dist/client/assets/classDiagram-4FO5ZUOK-CPFl2Aqd.js +1 -0
  58. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CPFl2Aqd.js +1 -0
  59. package/dist/client/assets/{cose-bilkent-S5V4N54A-DmPFKoVD.js → cose-bilkent-S5V4N54A-CfVoUUxT.js} +1 -1
  60. package/dist/client/assets/{dagre-BM42HDAG-DeHxd-ej.js → dagre-BM42HDAG-BfILvxLb.js} +1 -1
  61. package/dist/client/assets/{dashboard-view-DXjNQ5IF.js → dashboard-view-C9YIcx-O.js} +2 -2
  62. package/dist/client/assets/{dashboard-view-Cw5mMJay.js → dashboard-view-wFC1pe4d.js} +1 -1
  63. package/dist/client/assets/{diagram-2AECGRRQ-C04A2mhr.js → diagram-2AECGRRQ-6mdmbH-5.js} +1 -1
  64. package/dist/client/assets/{diagram-5GNKFQAL-DqtOwADI.js → diagram-5GNKFQAL-CY7MMwO1.js} +1 -1
  65. package/dist/client/assets/{diagram-KO2AKTUF-CH0har-Z.js → diagram-KO2AKTUF-D05XnQqd.js} +1 -1
  66. package/dist/client/assets/{diagram-LMA3HP47-CxN-U-0W.js → diagram-LMA3HP47-B18FTOJG.js} +1 -1
  67. package/dist/client/assets/{diagram-OG6HWLK6-42A7wpwF.js → diagram-OG6HWLK6-xmXlYf5m.js} +1 -1
  68. package/dist/client/assets/{erDiagram-TEJ5UH35-CnZI5YlN.js → erDiagram-TEJ5UH35-4XlgVLCs.js} +1 -1
  69. package/dist/client/assets/{flowDiagram-I6XJVG4X-BKfQhMVF.js → flowDiagram-I6XJVG4X-BYl3ps7f.js} +1 -1
  70. package/dist/client/assets/{folder-open-STXgbemw.js → folder-open-BH6FwmO7.js} +1 -1
  71. package/dist/client/assets/{ganttDiagram-6RSMTGT7-BfA0pME8.js → ganttDiagram-6RSMTGT7-4vxuH9Dx.js} +1 -1
  72. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-BgDFB7PS.js → gitGraphDiagram-PVQCEYII-BESmFgXT.js} +1 -1
  73. package/dist/client/assets/index-B9jtBwFy.css +1 -0
  74. package/dist/client/assets/index-DFeBJ3GM.js +1302 -0
  75. package/dist/client/assets/{infoDiagram-5YYISTIA-Bb5F155s.js → infoDiagram-5YYISTIA-DvjdjtwB.js} +1 -1
  76. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-8Oati7l_.js → ishikawaDiagram-YF4QCWOH-Cis5IoEm.js} +1 -1
  77. package/dist/client/assets/{journeyDiagram-JHISSGLW-DhHTHXTM.js → journeyDiagram-JHISSGLW-C20pB1W8.js} +1 -1
  78. package/dist/client/assets/{kanban-definition-UN3LZRKU-BWB9EtUX.js → kanban-definition-UN3LZRKU-CiVfCeU3.js} +1 -1
  79. package/dist/client/assets/{mermaid.core-BD-L2gUQ.js → mermaid.core-CW0vCXA6.js} +4 -4
  80. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BSKGd7LE.js → mindmap-definition-RKZ34NQL-E5rPHUdw.js} +1 -1
  81. package/dist/client/assets/{pieDiagram-4H26LBE5-BtBFPvSW.js → pieDiagram-4H26LBE5-DVIFbPjH.js} +1 -1
  82. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-aWQWYPto.js → quadrantDiagram-W4KKPZXB-C8qPRe_x.js} +1 -1
  83. package/dist/client/assets/{requirementDiagram-4Y6WPE33-CprlEpIa.js → requirementDiagram-4Y6WPE33-DQ-pwXh3.js} +1 -1
  84. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Cw-AcRAV.js → sankeyDiagram-5OEKKPKP-BnghLuUc.js} +1 -1
  85. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BMW5slyv.js → sequenceDiagram-3UESZ5HK-ChlGxGVk.js} +1 -1
  86. package/dist/client/assets/{shield-alert-Bzf8yNOc.js → shield-alert-BfOnDO6O.js} +1 -1
  87. package/dist/client/assets/{stateDiagram-AJRCARHV-CcxvIVwM.js → stateDiagram-AJRCARHV-ByUvNEch.js} +1 -1
  88. package/dist/client/assets/{stateDiagram-v2-BHNVJYJU-awOfoFOB.js → stateDiagram-v2-BHNVJYJU-Df38ITSP.js} +1 -1
  89. package/dist/client/assets/{timeline-definition-PNZ67QCA-DIEAc2YP.js → timeline-definition-PNZ67QCA-bsAGc83D.js} +1 -1
  90. package/dist/client/assets/{upload-DuwyPTKe.js → upload-BK1crTqn.js} +1 -1
  91. package/dist/client/assets/{users-D1Qhfyro.js → users-DW8jC2Z9.js} +1 -1
  92. package/dist/client/assets/{vennDiagram-CIIHVFJN-DOkqg5Wi.js → vennDiagram-CIIHVFJN-BBIj6LFN.js} +1 -1
  93. package/dist/client/assets/{wardley-L42UT6IY-DoPK1Q6d.js → wardley-L42UT6IY-De6JFroy.js} +1 -1
  94. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-l1vun-m2.js → wardleyDiagram-YWT4CUSO-P1LOLx0_.js} +1 -1
  95. package/dist/client/assets/{xychartDiagram-2RQKCTM6-DcUOUKvl.js → xychartDiagram-2RQKCTM6-v1G4ki6U.js} +1 -1
  96. package/dist/client/index.html +2 -2
  97. package/dist/client/version.json +1 -1
  98. package/dist/droid-cli/package.json +6 -25
  99. package/dist/extension.js +37091 -9900
  100. package/dist/pi-claude-cli/index.ts +26 -22
  101. package/dist/pi-claude-cli/package.json +6 -20
  102. package/dist/pi-claude-cli/src/mcp-config.ts +58 -7
  103. package/dist/plugin-sdk/index.d.ts +246 -186
  104. package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
  105. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +2 -9
  106. package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +250 -19
  107. package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
  108. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  109. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  110. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  111. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  112. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  113. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  114. package/dist/plugins/fusion-plugin-reports/package.json +2 -10
  115. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  116. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +3 -15
  117. package/package.json +1 -1
  118. package/skill/fusion/references/engine-tools.md +5 -1
  119. package/dist/client/assets/AgentDetailView-CgbB51Iw.css +0 -1
  120. package/dist/client/assets/AgentsView-DiGseTuo.js +0 -29
  121. package/dist/client/assets/ChatView-BjQBbDyo.js +0 -19
  122. package/dist/client/assets/ChatView-BtjkE0V7.css +0 -1
  123. package/dist/client/assets/CommandCenter-DDjpYkPe.css +0 -1
  124. package/dist/client/assets/CommandCenter-DwpiADSX.js +0 -71
  125. package/dist/client/assets/SettingsModal-DpaptT6t.js +0 -21
  126. package/dist/client/assets/SettingsModal-ino68pI2.css +0 -1
  127. package/dist/client/assets/SkillsView-Br2LkA3x.js +0 -1
  128. package/dist/client/assets/SkillsView-CJ8snrma.css +0 -1
  129. package/dist/client/assets/WorkflowNodeEditor-BOouksjw.css +0 -1
  130. package/dist/client/assets/WorkflowNodeEditor-e-eR6yzw.js +0 -8
  131. package/dist/client/assets/app-BM6bAO0b.js +0 -13
  132. package/dist/client/assets/app-BglOELbc.js +0 -13
  133. package/dist/client/assets/app-ChkCVQpq.js +0 -13
  134. package/dist/client/assets/app-DMn7Jt9g.js +0 -13
  135. package/dist/client/assets/app-S8fehQv8.js +0 -13
  136. package/dist/client/assets/channel-CCKvUami.js +0 -1
  137. package/dist/client/assets/classDiagram-4FO5ZUOK-C_HqY7AI.js +0 -1
  138. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C_HqY7AI.js +0 -1
  139. package/dist/client/assets/index-BnfnLXyO.css +0 -1
  140. package/dist/client/assets/index-CHy0WNjH.js +0 -1301
@@ -21,13 +21,14 @@ import {
21
21
  writeMcpConfig,
22
22
  buildAcpMcpServers,
23
23
  type McpToolDef,
24
+ type UserMcpServerSpec,
24
25
  } from "./src/mcp-config.js";
25
26
 
26
27
  /**
27
- * Route A kill-switch (U11). When `FUSION_CLAUDE_ACP=1` AND a bridge binary path
28
- * is available (`FUSION_CLAUDE_ACP_BRIDGE`, injected by the engine seam per
29
- * KTD10), the provider drives Claude through the ACP bridge instead of
30
- * `claude -p`. OFF by default: the live `-p` path is untouched until soak.
28
+ * FNXC:pi-claude-cli 2026-06-27-06:39:
29
+ * Route A drives Claude through the ACP bridge only when `FUSION_CLAUDE_ACP=1`
30
+ * AND a bridge binary path is injected via `FUSION_CLAUDE_ACP_BRIDGE`.
31
+ * OFF by default: the live `claude -p` path stays untouched until soak.
31
32
  */
32
33
  function resolveAcpBridgePath(): string | undefined {
33
34
  if (process.env.FUSION_CLAUDE_ACP !== "1") return undefined;
@@ -51,14 +52,12 @@ process.on("exit", killAllProcesses);
51
52
  const PROVIDER_ID = "pi-claude-cli";
52
53
 
53
54
  /**
54
- * Run CLI presence + auth probes at most once per process, asynchronously.
55
- *
56
- * The factory below is invoked on every `createFnAgent` call (the dashboard
57
- * does this per chat message). Doing the probes synchronously with execSync
58
- * froze the entire Node event loop for a few seconds while `claude` cold-
59
- * started. Memoizing as a Promise + spawning the probes async means the
60
- * factory returns immediately and other requests keep flowing; the result
61
- * is logged once on first run and reused thereafter.
55
+ * FNXC:pi-claude-cli 2026-06-27-06:39:
56
+ * The factory runs per `createFnAgent` call, which the dashboard does for each
57
+ * chat message. Synchronous `execSync` presence + auth probes froze the Node
58
+ * event loop while `claude` cold-started, so memoize the probes as an async
59
+ * Promise: the factory returns immediately, the result logs once, and later
60
+ * calls reuse it.
62
61
  */
63
62
  let cliValidationPromise: Promise<void> | undefined;
64
63
 
@@ -83,6 +82,11 @@ function debugMcp(message: string): void {
83
82
  console.error(`[pi-claude-cli] ${message}`);
84
83
  }
85
84
 
85
+ function getUserMcpServers(options: unknown): UserMcpServerSpec[] {
86
+ const servers = (options as { mcpServers?: unknown } | undefined)?.mcpServers;
87
+ return Array.isArray(servers) ? servers.filter((server): server is UserMcpServerSpec => Boolean(server && typeof server === "object" && "name" in server)) : [];
88
+ }
89
+
86
90
  /**
87
91
  * Resolve the MCP config path for the current request, regenerating it when
88
92
  * the set of custom tools changes.
@@ -113,6 +117,7 @@ function ensureMcpConfig(
113
117
  description: string;
114
118
  parameters: Record<string, unknown>;
115
119
  }>,
120
+ userMcpServers: UserMcpServerSpec[] = [],
116
121
  ): string | undefined {
117
122
  try {
118
123
  let toolDefs: McpToolDef[] = toolsFromContext(contextTools);
@@ -132,13 +137,13 @@ function ensureMcpConfig(
132
137
  toolDefs = getCustomToolDefs(pi);
133
138
  }
134
139
 
135
- if (toolDefs.length === 0) {
140
+ if (toolDefs.length === 0 && userMcpServers.length === 0) {
136
141
  cachedMcpConfig = undefined;
137
142
  return undefined;
138
143
  }
139
144
 
140
145
  const hash = createHash("sha1")
141
- .update(JSON.stringify(toolDefs))
146
+ .update(JSON.stringify({ toolDefs, userMcpServerNames: userMcpServers.map((server) => server.name) }))
142
147
  .digest("hex")
143
148
  .slice(0, 12);
144
149
 
@@ -147,7 +152,7 @@ function ensureMcpConfig(
147
152
  return cachedMcpConfig.configPath;
148
153
  }
149
154
 
150
- const configPath = writeMcpConfig(toolDefs, hash);
155
+ const configPath = writeMcpConfig(toolDefs, hash, userMcpServers);
151
156
  cachedMcpConfig = { hash, configPath };
152
157
  const toolNames = toolDefs.map((t) => t.name).join(", ");
153
158
  debugMcp(
@@ -250,23 +255,22 @@ export default function (pi: ExtensionAPI) {
250
255
  parameters: Record<string, unknown>;
251
256
  }> }).tools;
252
257
 
253
- // Route A (U11): drive Claude through the ACP bridge when the kill-switch
254
- // is on AND a bridge path is injected. OFF by default → `-p` path below.
258
+ // FNXC:pi-claude-cli 2026-06-27-06:39: Route A drives Claude through the ACP bridge only when the kill-switch is on AND a bridge path is injected. OFF by default → `-p` path below.
255
259
  const bridgePath = resolveAcpBridgePath();
256
260
  if (bridgePath) {
257
261
  const toolDefs = resolveToolDefs(pi, contextTools);
258
- const hash = createHash("sha1").update(JSON.stringify(toolDefs)).digest("hex").slice(0, 12);
262
+ const userMcpServers = getUserMcpServers(options);
263
+ const hash = createHash("sha1").update(JSON.stringify({ toolDefs, userMcpServerNames: userMcpServers.map((server) => server.name) })).digest("hex").slice(0, 12);
259
264
  return streamViaAcp(model, context, {
260
265
  ...options,
261
266
  bridgePath,
262
- mcpServers: buildAcpMcpServers(toolDefs, hash),
263
- // Forward only HOME/PATH so the bridged `claude` authenticates from the
264
- // login/keychain session (R17); never inherited process.env or API keys.
267
+ mcpServers: buildAcpMcpServers(toolDefs, hash, userMcpServers),
268
+ // FNXC:pi-claude-cli 2026-06-27-06:39: Forward only HOME/PATH so the bridged `claude` authenticates from the login/keychain session (R17); never inherited process.env or API keys.
265
269
  bridgeEnv: { HOME: process.env.HOME, PATH: process.env.PATH },
266
270
  });
267
271
  }
268
272
 
269
- const configPath = ensureMcpConfig(pi, contextTools);
273
+ const configPath = ensureMcpConfig(pi, contextTools, getUserMcpServers(options));
270
274
  return streamViaCli(model, context, {
271
275
  ...options,
272
276
  mcpConfigPath: configPath,
@@ -1,38 +1,24 @@
1
1
  {
2
2
  "name": "@fusion/pi-claude-cli",
3
- "version": "0.48.0",
4
- "description": "Fusion vendored fork: pi coding-agent extension that routes LLM calls through the Claude Code CLI. Forked from rchern/pi-claude-cli (MIT). See UPSTREAM.md.",
5
- "license": "MIT",
6
- "private": true,
3
+ "version": "0.50.0",
7
4
  "type": "module",
5
+ "private": true,
8
6
  "main": "index.ts",
9
- "keywords": [
10
- "pi-package"
11
- ],
12
7
  "pi": {
13
8
  "extensions": [
14
9
  "index.ts"
15
10
  ]
16
11
  },
17
- "repository": {
18
- "type": "git",
19
- "url": "https://github.com/Runfusion/Fusion",
20
- "directory": "packages/pi-claude-cli"
21
- },
22
12
  "dependencies": {
23
13
  "@agentclientprotocol/sdk": "0.24.0"
24
14
  },
25
- "peerDependencies": {
26
- "@earendil-works/pi-ai": "*",
27
- "@earendil-works/pi-coding-agent": "*"
28
- },
29
15
  "devDependencies": {
30
16
  "@types/node": "^22.0.0",
31
17
  "typescript": "^5.7.0",
32
18
  "vitest": "^4.1.0"
33
19
  },
34
- "scripts": {
35
- "test": "vitest run --reporter=dot",
36
- "typecheck": "tsc --noEmit"
20
+ "peerDependencies": {
21
+ "@earendil-works/pi-ai": "*",
22
+ "@earendil-works/pi-coding-agent": "*"
37
23
  }
38
- }
24
+ }
@@ -92,6 +92,39 @@ export function toolsFromContext(
92
92
  }));
93
93
  }
94
94
 
95
+ export interface UserMcpServerSpec {
96
+ name: string;
97
+ enabled?: boolean;
98
+ transport?: "stdio" | "sse" | "streamable-http";
99
+ command?: string;
100
+ args?: string[];
101
+ env?: Record<string, string>;
102
+ url?: string;
103
+ headers?: Record<string, string>;
104
+ }
105
+
106
+ function userServerToConfig(server: UserMcpServerSpec): Record<string, unknown> | undefined {
107
+ if (server.enabled === false) return undefined;
108
+ if (server.transport === "sse" || server.transport === "streamable-http") {
109
+ if (!server.url) return undefined;
110
+ return {
111
+ transport: server.transport,
112
+ url: server.url,
113
+ ...(server.headers ? { headers: server.headers } : {}),
114
+ };
115
+ }
116
+ if (!server.command) return undefined;
117
+ return {
118
+ command: server.command,
119
+ ...(server.args ? { args: server.args } : {}),
120
+ ...(server.env ? { env: server.env } : {}),
121
+ };
122
+ }
123
+
124
+ /**
125
+ * FNXC:McpConfig 2026-06-25-22:12:
126
+ * The Claude CLI MCP config combines Fusion's schema-only custom-tools server with operator-enabled user MCP servers. User env/header values are already secret-materialized by the engine, so this module must write them only to the transient MCP config and never log server contents.
127
+ */
95
128
  /**
96
129
  * Write MCP config and tool schemas to temp files.
97
130
  *
@@ -108,6 +141,7 @@ export function toolsFromContext(
108
141
  export function writeMcpConfig(
109
142
  toolDefs: McpToolDef[],
110
143
  cacheKey?: string,
144
+ userMcpServers: UserMcpServerSpec[] = [],
111
145
  ): string {
112
146
  const suffix = cacheKey ? `${process.pid}-${cacheKey}` : `${process.pid}`;
113
147
 
@@ -124,14 +158,17 @@ export function writeMcpConfig(
124
158
  const serverPath = join(__dirname, "mcp-schema-server.cjs");
125
159
 
126
160
  // Build MCP config
127
- const config = {
128
- mcpServers: {
129
- "custom-tools": {
130
- command: "node",
131
- args: [serverPath, schemaFilePath],
132
- },
161
+ const mcpServers: Record<string, unknown> = {
162
+ "custom-tools": {
163
+ command: "node",
164
+ args: [serverPath, schemaFilePath],
133
165
  },
134
166
  };
167
+ for (const server of userMcpServers) {
168
+ const config = userServerToConfig(server);
169
+ if (config) mcpServers[server.name] = config;
170
+ }
171
+ const config = { mcpServers };
135
172
 
136
173
  // Write config to temp file
137
174
  const configFilePath = join(
@@ -151,6 +188,16 @@ export interface AcpMcpServerSpec {
151
188
  env: { name: string; value: string }[];
152
189
  }
153
190
 
191
+ function userServerToAcp(server: UserMcpServerSpec): AcpMcpServerSpec | undefined {
192
+ if (server.enabled === false || (server.transport && server.transport !== "stdio") || !server.command) return undefined;
193
+ return {
194
+ name: server.name,
195
+ command: server.command,
196
+ args: server.args ?? [],
197
+ env: Object.entries(server.env ?? {}).map(([name, value]) => ({ name, value })),
198
+ };
199
+ }
200
+
154
201
  /**
155
202
  * Build the ACP `mcpServers` spec for the same schema-only `custom-tools` server
156
203
  * `writeMcpConfig` produces for `--mcp-config` — but as the inline ACP shape
@@ -161,13 +208,17 @@ export interface AcpMcpServerSpec {
161
208
  export function buildAcpMcpServers(
162
209
  toolDefs: McpToolDef[],
163
210
  cacheKey?: string,
211
+ userMcpServers: UserMcpServerSpec[] = [],
164
212
  ): AcpMcpServerSpec[] {
165
- if (toolDefs.length === 0) return [];
213
+ if (toolDefs.length === 0) {
214
+ return userMcpServers.map(userServerToAcp).filter((server): server is AcpMcpServerSpec => Boolean(server));
215
+ }
166
216
  const suffix = cacheKey ? `${process.pid}-${cacheKey}` : `${process.pid}`;
167
217
  const schemaFilePath = join(tmpdir(), `pi-claude-mcp-schemas-${suffix}.json`);
168
218
  writeFileSync(schemaFilePath, JSON.stringify(toolDefs));
169
219
  const serverPath = join(dirname(fileURLToPath(import.meta.url)), "mcp-schema-server.cjs");
170
220
  return [
171
221
  { name: "custom-tools", command: "node", args: [serverPath, schemaFilePath], env: [] },
222
+ ...userMcpServers.map(userServerToAcp).filter((server): server is AcpMcpServerSpec => Boolean(server)),
172
223
  ];
173
224
  }