@akon-labs/nexus-cli 0.1.8 → 0.1.10

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 (320) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +1 -0
  3. package/README.md +3 -2
  4. package/dist/app-bridge.bundle.js +67 -0
  5. package/dist/cli.js +19934 -1869
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +1 -1
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/auto-model.d.ts +1 -1
  10. package/dist/core/auto-model.d.ts.map +1 -1
  11. package/dist/core/auto-model.js +1 -1
  12. package/dist/core/auto-model.js.map +1 -1
  13. package/dist/core/model-resolver.d.ts +2 -2
  14. package/dist/core/model-resolver.d.ts.map +1 -1
  15. package/dist/core/model-resolver.js +3 -3
  16. package/dist/core/model-resolver.js.map +1 -1
  17. package/dist/core/package-manager.d.ts +15 -0
  18. package/dist/core/package-manager.d.ts.map +1 -1
  19. package/dist/core/package-manager.js +50 -0
  20. package/dist/core/package-manager.js.map +1 -1
  21. package/dist/core/sdk.d.ts.map +1 -1
  22. package/dist/core/sdk.js +1 -1
  23. package/dist/core/sdk.js.map +1 -1
  24. package/dist/core/slash-commands.d.ts.map +1 -1
  25. package/dist/core/slash-commands.js +3 -3
  26. package/dist/core/slash-commands.js.map +1 -1
  27. package/dist/core/system-prompt.d.ts.map +1 -1
  28. package/dist/core/system-prompt.js +1 -1
  29. package/dist/core/system-prompt.js.map +1 -1
  30. package/dist/core/tools/index.d.ts +1 -1
  31. package/dist/core/tools/index.d.ts.map +1 -1
  32. package/dist/core/tools/index.js +5 -5
  33. package/dist/core/tools/index.js.map +1 -1
  34. package/dist/core/tools/websearch.d.ts.map +1 -1
  35. package/dist/core/tools/websearch.js +3 -3
  36. package/dist/core/tools/websearch.js.map +1 -1
  37. package/dist/extensions/gitnexus/gateway.d.ts +6 -6
  38. package/dist/extensions/gitnexus/gateway.d.ts.map +1 -1
  39. package/dist/extensions/gitnexus/gateway.js +4 -4
  40. package/dist/extensions/gitnexus/gateway.js.map +1 -1
  41. package/dist/extensions/gitnexus/index.d.ts.map +1 -1
  42. package/dist/extensions/gitnexus/index.js +4 -2
  43. package/dist/extensions/gitnexus/index.js.map +1 -1
  44. package/dist/extensions/mcp/abort.d.ts +3 -0
  45. package/dist/extensions/mcp/abort.d.ts.map +1 -0
  46. package/dist/extensions/mcp/abort.js +41 -0
  47. package/dist/extensions/mcp/abort.js.map +1 -0
  48. package/dist/extensions/mcp/agent-dir.d.ts +6 -0
  49. package/dist/extensions/mcp/agent-dir.d.ts.map +1 -0
  50. package/dist/extensions/mcp/agent-dir.js +18 -0
  51. package/dist/extensions/mcp/agent-dir.js.map +1 -0
  52. package/dist/extensions/mcp/agent-plugin-loader.d.ts +9 -0
  53. package/dist/extensions/mcp/agent-plugin-loader.d.ts.map +1 -0
  54. package/dist/extensions/mcp/agent-plugin-loader.js +384 -0
  55. package/dist/extensions/mcp/agent-plugin-loader.js.map +1 -0
  56. package/dist/extensions/mcp/app-bridge.bundle.js +67 -0
  57. package/dist/extensions/mcp/commands.d.ts +27 -0
  58. package/dist/extensions/mcp/commands.d.ts.map +1 -0
  59. package/dist/extensions/mcp/commands.js +629 -0
  60. package/dist/extensions/mcp/commands.js.map +1 -0
  61. package/dist/extensions/mcp/config.d.ts +121 -38
  62. package/dist/extensions/mcp/config.d.ts.map +1 -1
  63. package/dist/extensions/mcp/config.js +1030 -70
  64. package/dist/extensions/mcp/config.js.map +1 -1
  65. package/dist/extensions/mcp/consent-manager.d.ts +14 -0
  66. package/dist/extensions/mcp/consent-manager.d.ts.map +1 -0
  67. package/dist/extensions/mcp/consent-manager.js +60 -0
  68. package/dist/extensions/mcp/consent-manager.js.map +1 -0
  69. package/dist/extensions/mcp/direct-tools.d.ts +11 -0
  70. package/dist/extensions/mcp/direct-tools.d.ts.map +1 -0
  71. package/dist/extensions/mcp/direct-tools.js +562 -0
  72. package/dist/extensions/mcp/direct-tools.js.map +1 -0
  73. package/dist/extensions/mcp/elicitation-handler.d.ts +18 -0
  74. package/dist/extensions/mcp/elicitation-handler.d.ts.map +1 -0
  75. package/dist/extensions/mcp/elicitation-handler.js +320 -0
  76. package/dist/extensions/mcp/elicitation-handler.js.map +1 -0
  77. package/dist/extensions/mcp/error-signal.d.ts +16 -0
  78. package/dist/extensions/mcp/error-signal.d.ts.map +1 -0
  79. package/dist/extensions/mcp/error-signal.js +22 -0
  80. package/dist/extensions/mcp/error-signal.js.map +1 -0
  81. package/dist/extensions/mcp/errors.d.ts +104 -0
  82. package/dist/extensions/mcp/errors.d.ts.map +1 -0
  83. package/dist/extensions/mcp/errors.js +171 -0
  84. package/dist/extensions/mcp/errors.js.map +1 -0
  85. package/dist/extensions/mcp/glimpse-ui.d.ts +10 -0
  86. package/dist/extensions/mcp/glimpse-ui.d.ts.map +1 -0
  87. package/dist/extensions/mcp/glimpse-ui.js +67 -0
  88. package/dist/extensions/mcp/glimpse-ui.js.map +1 -0
  89. package/dist/extensions/mcp/host-html-template.d.ts +17 -0
  90. package/dist/extensions/mcp/host-html-template.d.ts.map +1 -0
  91. package/dist/extensions/mcp/host-html-template.js +442 -0
  92. package/dist/extensions/mcp/host-html-template.js.map +1 -0
  93. package/dist/extensions/mcp/index.d.ts +20 -15
  94. package/dist/extensions/mcp/index.d.ts.map +1 -1
  95. package/dist/extensions/mcp/index.js +1064 -82
  96. package/dist/extensions/mcp/index.js.map +1 -1
  97. package/dist/extensions/mcp/init.d.ts +28 -0
  98. package/dist/extensions/mcp/init.d.ts.map +1 -0
  99. package/dist/extensions/mcp/init.js +631 -0
  100. package/dist/extensions/mcp/init.js.map +1 -0
  101. package/dist/extensions/mcp/json-schema-validator.d.ts +3 -0
  102. package/dist/extensions/mcp/json-schema-validator.d.ts.map +1 -0
  103. package/dist/extensions/mcp/json-schema-validator.js +53 -0
  104. package/dist/extensions/mcp/json-schema-validator.js.map +1 -0
  105. package/dist/extensions/mcp/lifecycle.d.ts +56 -0
  106. package/dist/extensions/mcp/lifecycle.d.ts.map +1 -0
  107. package/dist/extensions/mcp/lifecycle.js +395 -0
  108. package/dist/extensions/mcp/lifecycle.js.map +1 -0
  109. package/dist/extensions/mcp/logger.d.ts +52 -0
  110. package/dist/extensions/mcp/logger.d.ts.map +1 -0
  111. package/dist/extensions/mcp/logger.js +134 -0
  112. package/dist/extensions/mcp/logger.js.map +1 -0
  113. package/dist/extensions/mcp/mcp-auth-flow.d.ts +117 -0
  114. package/dist/extensions/mcp/mcp-auth-flow.d.ts.map +1 -0
  115. package/dist/extensions/mcp/mcp-auth-flow.js +869 -0
  116. package/dist/extensions/mcp/mcp-auth-flow.js.map +1 -0
  117. package/dist/extensions/mcp/mcp-auth.d.ts +162 -0
  118. package/dist/extensions/mcp/mcp-auth.d.ts.map +1 -0
  119. package/dist/extensions/mcp/mcp-auth.js +897 -0
  120. package/dist/extensions/mcp/mcp-auth.js.map +1 -0
  121. package/dist/extensions/mcp/mcp-bearer-store.d.ts +25 -0
  122. package/dist/extensions/mcp/mcp-bearer-store.d.ts.map +1 -0
  123. package/dist/extensions/mcp/mcp-bearer-store.js +350 -0
  124. package/dist/extensions/mcp/mcp-bearer-store.js.map +1 -0
  125. package/dist/extensions/mcp/mcp-callback-server.d.ts +54 -0
  126. package/dist/extensions/mcp/mcp-callback-server.d.ts.map +1 -0
  127. package/dist/extensions/mcp/mcp-callback-server.js +454 -0
  128. package/dist/extensions/mcp/mcp-callback-server.js.map +1 -0
  129. package/dist/extensions/mcp/mcp-code.d.ts +48 -0
  130. package/dist/extensions/mcp/mcp-code.d.ts.map +1 -0
  131. package/dist/extensions/mcp/mcp-code.js +358 -0
  132. package/dist/extensions/mcp/mcp-code.js.map +1 -0
  133. package/dist/extensions/mcp/mcp-keyring-helper.cjs +89 -0
  134. package/dist/extensions/mcp/mcp-oauth-provider.d.ts +135 -0
  135. package/dist/extensions/mcp/mcp-oauth-provider.d.ts.map +1 -0
  136. package/dist/extensions/mcp/mcp-oauth-provider.js +491 -0
  137. package/dist/extensions/mcp/mcp-oauth-provider.js.map +1 -0
  138. package/dist/extensions/mcp/mcp-output-guard.d.ts +59 -0
  139. package/dist/extensions/mcp/mcp-output-guard.d.ts.map +1 -0
  140. package/dist/extensions/mcp/mcp-output-guard.js +343 -0
  141. package/dist/extensions/mcp/mcp-output-guard.js.map +1 -0
  142. package/dist/extensions/mcp/mcp-panel.d.ts +71 -0
  143. package/dist/extensions/mcp/mcp-panel.d.ts.map +1 -0
  144. package/dist/extensions/mcp/mcp-panel.js +973 -0
  145. package/dist/extensions/mcp/mcp-panel.js.map +1 -0
  146. package/dist/extensions/mcp/mcp-probe.d.ts +7 -0
  147. package/dist/extensions/mcp/mcp-probe.d.ts.map +1 -0
  148. package/dist/extensions/mcp/mcp-probe.js +155 -0
  149. package/dist/extensions/mcp/mcp-probe.js.map +1 -0
  150. package/dist/extensions/mcp/mcp-script-worker.mjs +130 -0
  151. package/dist/extensions/mcp/mcp-setup-panel.d.ts +85 -0
  152. package/dist/extensions/mcp/mcp-setup-panel.d.ts.map +1 -0
  153. package/dist/extensions/mcp/mcp-setup-panel.js +618 -0
  154. package/dist/extensions/mcp/mcp-setup-panel.js.map +1 -0
  155. package/dist/extensions/mcp/mcp-status.d.ts +12 -0
  156. package/dist/extensions/mcp/mcp-status.d.ts.map +1 -0
  157. package/dist/extensions/mcp/mcp-status.js +98 -0
  158. package/dist/extensions/mcp/mcp-status.js.map +1 -0
  159. package/dist/extensions/mcp/mcp-trace.d.ts +95 -0
  160. package/dist/extensions/mcp/mcp-trace.d.ts.map +1 -0
  161. package/dist/extensions/mcp/mcp-trace.js +252 -0
  162. package/dist/extensions/mcp/mcp-trace.js.map +1 -0
  163. package/dist/extensions/mcp/metadata-cache.d.ts +20 -0
  164. package/dist/extensions/mcp/metadata-cache.d.ts.map +1 -0
  165. package/dist/extensions/mcp/metadata-cache.js +318 -0
  166. package/dist/extensions/mcp/metadata-cache.js.map +1 -0
  167. package/dist/extensions/mcp/npx-resolver.d.ts +7 -0
  168. package/dist/extensions/mcp/npx-resolver.d.ts.map +1 -0
  169. package/dist/extensions/mcp/npx-resolver.js +505 -0
  170. package/dist/extensions/mcp/npx-resolver.js.map +1 -0
  171. package/dist/extensions/mcp/oauth.d.ts +21 -0
  172. package/dist/extensions/mcp/oauth.d.ts.map +1 -0
  173. package/dist/extensions/mcp/oauth.js +15 -0
  174. package/dist/extensions/mcp/oauth.js.map +1 -0
  175. package/dist/extensions/mcp/onboarding-state.d.ts +13 -0
  176. package/dist/extensions/mcp/onboarding-state.d.ts.map +1 -0
  177. package/dist/extensions/mcp/onboarding-state.js +65 -0
  178. package/dist/extensions/mcp/onboarding-state.js.map +1 -0
  179. package/dist/extensions/mcp/package-mcp-loader.d.ts +3 -0
  180. package/dist/extensions/mcp/package-mcp-loader.d.ts.map +1 -0
  181. package/dist/extensions/mcp/package-mcp-loader.js +170 -0
  182. package/dist/extensions/mcp/package-mcp-loader.js.map +1 -0
  183. package/dist/extensions/mcp/panel-keys.d.ts +21 -0
  184. package/dist/extensions/mcp/panel-keys.d.ts.map +1 -0
  185. package/dist/extensions/mcp/panel-keys.js +29 -0
  186. package/dist/extensions/mcp/panel-keys.js.map +1 -0
  187. package/dist/extensions/mcp/prompts.d.ts +63 -0
  188. package/dist/extensions/mcp/prompts.d.ts.map +1 -0
  189. package/dist/extensions/mcp/prompts.js +297 -0
  190. package/dist/extensions/mcp/prompts.js.map +1 -0
  191. package/dist/extensions/mcp/proxy-modes.d.ts +15 -0
  192. package/dist/extensions/mcp/proxy-modes.d.ts.map +1 -0
  193. package/dist/extensions/mcp/proxy-modes.js +1421 -0
  194. package/dist/extensions/mcp/proxy-modes.js.map +1 -0
  195. package/dist/extensions/mcp/request-headers-command.d.ts +11 -0
  196. package/dist/extensions/mcp/request-headers-command.d.ts.map +1 -0
  197. package/dist/extensions/mcp/request-headers-command.js +334 -0
  198. package/dist/extensions/mcp/request-headers-command.js.map +1 -0
  199. package/dist/extensions/mcp/resource-tools.d.ts +2 -0
  200. package/dist/extensions/mcp/resource-tools.d.ts.map +1 -0
  201. package/dist/extensions/mcp/resource-tools.js +15 -0
  202. package/dist/extensions/mcp/resource-tools.js.map +1 -0
  203. package/dist/extensions/mcp/runtime-owner.d.ts +14 -0
  204. package/dist/extensions/mcp/runtime-owner.d.ts.map +1 -0
  205. package/dist/extensions/mcp/runtime-owner.js +84 -0
  206. package/dist/extensions/mcp/runtime-owner.js.map +1 -0
  207. package/dist/extensions/mcp/sampling-handler.d.ts +18 -0
  208. package/dist/extensions/mcp/sampling-handler.d.ts.map +1 -0
  209. package/dist/extensions/mcp/sampling-handler.js +229 -0
  210. package/dist/extensions/mcp/sampling-handler.js.map +1 -0
  211. package/dist/extensions/mcp/search-ranking.d.ts +25 -0
  212. package/dist/extensions/mcp/search-ranking.d.ts.map +1 -0
  213. package/dist/extensions/mcp/search-ranking.js +234 -0
  214. package/dist/extensions/mcp/search-ranking.js.map +1 -0
  215. package/dist/extensions/mcp/server-manager.d.ts +116 -0
  216. package/dist/extensions/mcp/server-manager.d.ts.map +1 -0
  217. package/dist/extensions/mcp/server-manager.js +1052 -0
  218. package/dist/extensions/mcp/server-manager.js.map +1 -0
  219. package/dist/extensions/mcp/session-recovery.d.ts +27 -0
  220. package/dist/extensions/mcp/session-recovery.d.ts.map +1 -0
  221. package/dist/extensions/mcp/session-recovery.js +140 -0
  222. package/dist/extensions/mcp/session-recovery.js.map +1 -0
  223. package/dist/extensions/mcp/skills/mcp-scripting/SKILL.md +38 -0
  224. package/dist/extensions/mcp/state.d.ts +62 -0
  225. package/dist/extensions/mcp/state.d.ts.map +1 -0
  226. package/dist/extensions/mcp/state.js +2 -0
  227. package/dist/extensions/mcp/state.js.map +1 -0
  228. package/dist/extensions/mcp/tool-approval.d.ts +11 -0
  229. package/dist/extensions/mcp/tool-approval.d.ts.map +1 -0
  230. package/dist/extensions/mcp/tool-approval.js +156 -0
  231. package/dist/extensions/mcp/tool-approval.js.map +1 -0
  232. package/dist/extensions/mcp/tool-metadata.d.ts +11 -0
  233. package/dist/extensions/mcp/tool-metadata.d.ts.map +1 -0
  234. package/dist/extensions/mcp/tool-metadata.js +264 -0
  235. package/dist/extensions/mcp/tool-metadata.js.map +1 -0
  236. package/dist/extensions/mcp/tool-registrar.d.ts +21 -0
  237. package/dist/extensions/mcp/tool-registrar.d.ts.map +1 -0
  238. package/dist/extensions/mcp/tool-registrar.js +229 -0
  239. package/dist/extensions/mcp/tool-registrar.js.map +1 -0
  240. package/dist/extensions/mcp/tool-result-renderer.d.ts +83 -0
  241. package/dist/extensions/mcp/tool-result-renderer.d.ts.map +1 -0
  242. package/dist/extensions/mcp/tool-result-renderer.js +345 -0
  243. package/dist/extensions/mcp/tool-result-renderer.js.map +1 -0
  244. package/dist/extensions/mcp/ts-shape.d.ts +3 -0
  245. package/dist/extensions/mcp/ts-shape.d.ts.map +1 -0
  246. package/dist/extensions/mcp/ts-shape.js +160 -0
  247. package/dist/extensions/mcp/ts-shape.js.map +1 -0
  248. package/dist/extensions/mcp/types.d.ts +521 -0
  249. package/dist/extensions/mcp/types.d.ts.map +1 -0
  250. package/dist/extensions/mcp/types.js +294 -0
  251. package/dist/extensions/mcp/types.js.map +1 -0
  252. package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts +7 -0
  253. package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts.map +1 -0
  254. package/dist/extensions/mcp/ui-app-bridge-helpers.js +37 -0
  255. package/dist/extensions/mcp/ui-app-bridge-helpers.js.map +1 -0
  256. package/dist/extensions/mcp/ui-resource-handler.d.ts +18 -0
  257. package/dist/extensions/mcp/ui-resource-handler.d.ts.map +1 -0
  258. package/dist/extensions/mcp/ui-resource-handler.js +210 -0
  259. package/dist/extensions/mcp/ui-resource-handler.js.map +1 -0
  260. package/dist/extensions/mcp/ui-server.d.ts +52 -0
  261. package/dist/extensions/mcp/ui-server.d.ts.map +1 -0
  262. package/dist/extensions/mcp/ui-server.js +740 -0
  263. package/dist/extensions/mcp/ui-server.js.map +1 -0
  264. package/dist/extensions/mcp/ui-session.d.ts +40 -0
  265. package/dist/extensions/mcp/ui-session.d.ts.map +1 -0
  266. package/dist/extensions/mcp/ui-session.js +477 -0
  267. package/dist/extensions/mcp/ui-session.js.map +1 -0
  268. package/dist/extensions/mcp/ui-stream-types.d.ts +105 -0
  269. package/dist/extensions/mcp/ui-stream-types.d.ts.map +1 -0
  270. package/dist/extensions/mcp/ui-stream-types.js +61 -0
  271. package/dist/extensions/mcp/ui-stream-types.js.map +1 -0
  272. package/dist/extensions/mcp/ui-tool-visibility.d.ts +5 -0
  273. package/dist/extensions/mcp/ui-tool-visibility.d.ts.map +1 -0
  274. package/dist/extensions/mcp/ui-tool-visibility.js +27 -0
  275. package/dist/extensions/mcp/ui-tool-visibility.js.map +1 -0
  276. package/dist/extensions/mcp/unix-socket-transport.d.ts +15 -0
  277. package/dist/extensions/mcp/unix-socket-transport.d.ts.map +1 -0
  278. package/dist/extensions/mcp/unix-socket-transport.js +85 -0
  279. package/dist/extensions/mcp/unix-socket-transport.js.map +1 -0
  280. package/dist/extensions/mcp/utils.d.ts +32 -0
  281. package/dist/extensions/mcp/utils.d.ts.map +1 -0
  282. package/dist/extensions/mcp/utils.js +336 -0
  283. package/dist/extensions/mcp/utils.js.map +1 -0
  284. package/dist/extensions/subagent/index.d.ts +62 -3
  285. package/dist/extensions/subagent/index.d.ts.map +1 -1
  286. package/dist/extensions/subagent/index.js +193 -58
  287. package/dist/extensions/subagent/index.js.map +1 -1
  288. package/dist/extensions/web-access.d.ts.map +1 -1
  289. package/dist/extensions/web-access.js +1 -1
  290. package/dist/extensions/web-access.js.map +1 -1
  291. package/dist/index.d.ts +2 -0
  292. package/dist/index.d.ts.map +1 -1
  293. package/dist/index.js +20074 -2004
  294. package/dist/index.js.map +1 -1
  295. package/dist/mcp-keyring-helper.cjs +89 -0
  296. package/dist/mcp-script-worker.mjs +130 -0
  297. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  298. package/dist/modes/interactive/components/model-selector.js +1 -1
  299. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  300. package/dist/modes/interactive/components/response-separator.d.ts +1 -1
  301. package/dist/modes/interactive/components/response-separator.d.ts.map +1 -1
  302. package/dist/modes/interactive/components/response-separator.js +1 -1
  303. package/dist/modes/interactive/components/response-separator.js.map +1 -1
  304. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  305. package/dist/modes/interactive/interactive-mode.js +7 -5
  306. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  307. package/dist/rpc-entry.js +19934 -1869
  308. package/dist/skills/mcp-scripting/SKILL.md +38 -0
  309. package/dist/vendor/ai.js +282 -207
  310. package/docs/mcp.md +178 -0
  311. package/docs/pi-subagents.md +75 -0
  312. package/docs/quickstart.md +1 -1
  313. package/docs/usage.md +2 -1
  314. package/examples/extensions/pi-subagents-worker-policy.ts +86 -0
  315. package/npm-shrinkwrap.json +763 -6
  316. package/package.json +15 -4
  317. package/dist/extensions/mcp/client.d.ts +0 -86
  318. package/dist/extensions/mcp/client.d.ts.map +0 -1
  319. package/dist/extensions/mcp/client.js +0 -275
  320. package/dist/extensions/mcp/client.js.map +0 -1
@@ -1,95 +1,1077 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
1
3
  import { Type } from "typebox";
2
- import { HttpTransport, McpClient, StdioTransport } from "./client.js";
3
- import { loadMcpConfig } from "./config.js";
4
- /**
5
- * MCP adapter: connects the servers named in mcp.json and exposes their tools
6
- * to the model.
7
- *
8
- * Every failure here is non-fatal. A server that is missing, misconfigured, or
9
- * down costs you its tools and a startup notice — it must never stop the agent
10
- * from running, the same contract the gitnexus extension follows.
11
- */
12
- /** Tool names are namespaced by server, so two servers can both export "search". */
13
- export function toolName(serverName, tool) {
14
- const safe = `${serverName}_${tool}`.replace(/[^a-zA-Z0-9_-]/g, "_");
15
- // Keep well under provider name limits; collisions stay unlikely because the
16
- // server prefix is preserved.
17
- return safe.length <= 64 ? safe : safe.slice(0, 64);
18
- }
19
- /** Flatten an MCP result into the text the agent shows and feeds back to the model. */
20
- export function resultToText(result) {
21
- const text = (result.content ?? [])
22
- .filter((block) => block.type === "text" && typeof block.text === "string")
23
- .map((block) => block.text)
24
- .join("\n");
25
- if (text)
26
- return text;
27
- // A server that answers with only non-text blocks should say so rather than
28
- // handing the model a blank string it will read as success-with-no-data.
29
- const kinds = [...new Set((result.content ?? []).map((block) => block.type))];
30
- return kinds.length > 0 ? `[no text content: ${kinds.join(", ")}]` : "[no content]";
31
- }
32
- function connect(server) {
33
- const transport = server.type === "stdio"
34
- ? new StdioTransport(server.command, server.args, server.env, server.cwd)
35
- : new HttpTransport(server.url, server.headers);
36
- return new McpClient(server.name, transport);
4
+ import { authenticateServer, logoutServer, manageBearerToken, openMcpAuthPanel, openMcpPanel, openMcpSetup, reconnectServer, reconnectServers, showPrompts, showStatus, showTools, } from "./commands.js";
5
+ import { cloneMcpConfig, loadMcpConfig, writeProjectServerDisabledOverride } from "./config.js";
6
+ import { buildProxyDescription, createDirectToolExecutor, getMissingConfiguredDirectToolServers, resolveDirectTools, } from "./direct-tools.js";
7
+ import { toolErrorOverride } from "./error-signal.js";
8
+ import { flushMetadataCache, initializeMcp, updateStatusBar } from "./init.js";
9
+ import { logger } from "./logger.js";
10
+ import { createOAuthRuntime, shutdownOAuth } from "./mcp-auth-flow.js";
11
+ import { runMcpScript } from "./mcp-code.js";
12
+ import { publishMcpStatusShutdown } from "./mcp-status.js";
13
+ import { loadMetadataCache } from "./metadata-cache.js";
14
+ import { createPromptCommand, resolveCachedPrompts } from "./prompts.js";
15
+ import { executeAuthComplete, executeAuthStart, executeCall, executeConnect, executeDescribe, executeInstructions, executeList, executeSearch, executeStatus, executeUiMessages, } from "./proxy-modes.js";
16
+ import { createMcpRuntimeOwner, createOwnedUi, isAbortError } from "./runtime-owner.js";
17
+ import { cleanupMaterializedBinaryResources } from "./tool-registrar.js";
18
+ import { createMcpDirectToolCallRenderer, createMcpProxyToolCallRenderer, createMcpToolResultRenderer, resolveMcpToolRenderOptions, } from "./tool-result-renderer.js";
19
+ import { formatTerminalError, getConfigPathFromArgv, normalizeDirectToolInputSchema, truncateAtWord } from "./utils.js";
20
+ export { MCP_STATUS_EVENT, MCP_STATUS_SNAPSHOT_VERSION, MCP_TOOL_APPROVAL_REQUEST_EVENT, } from "./types.js";
21
+ const INIT_WAIT_TIMEOUT_MS = 30_000;
22
+ const INIT_WAIT_TIMED_OUT = Symbol("init-wait-timed-out");
23
+ // Routes runtime registrations to the adapter installed for a specific Nexus
24
+ // instance, so a process with several adapters cannot cross-register.
25
+ const runtimeRegistrars = new WeakMap();
26
+ async function awaitWithTimeout(promise, timeoutMs) {
27
+ let timer;
28
+ try {
29
+ return await Promise.race([
30
+ promise,
31
+ new Promise((resolve) => {
32
+ timer = setTimeout(() => resolve(INIT_WAIT_TIMED_OUT), timeoutMs);
33
+ timer.unref?.();
34
+ }),
35
+ ]);
36
+ }
37
+ finally {
38
+ clearTimeout(timer);
39
+ }
37
40
  }
38
- /** Build a nexus tool that proxies one MCP tool. */
39
- export function defineProxyTool(client, serverName, schema) {
40
- return {
41
- name: toolName(serverName, schema.name),
42
- label: `${serverName}:${schema.name}`,
43
- description: schema.description ?? `${schema.name} (via ${serverName} MCP server)`,
44
- // Pass the server's own JSON Schema straight through. Type.Unsafe wraps it
45
- // without reinterpreting it, so the model sees the parameters the server
46
- // documented and we never reject a payload the server would have accepted.
47
- parameters: Type.Unsafe(schema.inputSchema ?? { type: "object", properties: {} }),
48
- execute: async (_id, params, signal) => {
49
- const result = await client.callTool(schema.name, (params ?? {}), signal);
50
- return {
51
- content: [{ type: "text", text: resultToText(result) }],
52
- isError: result.isError === true,
53
- details: { server: serverName, tool: schema.name },
54
- };
55
- },
56
- };
41
+ // TypeBox 1.x annotates raw objects passed to Type.Optional with an enumerable
42
+ // "~optional" key that survives serialization into provider tool schemas (Gemini
43
+ // rejects it with 400 INVALID_ARGUMENT). Prefer a real Type.Number schema; fall
44
+ // back to a plain raw schema for host TypeBox shims that omit Type.Number, since
45
+ // a property left out of `required` is optional by default.
46
+ function optionalNumber(options) {
47
+ const number = Type.Number;
48
+ return typeof number === "function"
49
+ ? Type.Optional(number(options))
50
+ : { type: "number", ...options };
57
51
  }
58
- export default function mcpExtension(pi) {
59
- const clients = [];
60
- pi.on("session_start", async (_event, ctx) => {
61
- const { servers, diagnostics } = loadMcpConfig(process.cwd());
62
- const notices = [...diagnostics];
63
- const enabled = servers.filter((server) => server.enabled);
64
- for (const server of enabled) {
65
- const client = connect(server);
66
- try {
67
- const tools = await client.listTools();
68
- if (tools.length === 0) {
69
- notices.push(`MCP ${server.name}: connected but exposes no tools`);
70
- client.close();
52
+ function installMcpAdapter(nexus, options) {
53
+ const sessionConfig = options.config !== undefined ? cloneMcpConfig(options.config) : undefined;
54
+ const programmaticConfig = sessionConfig !== undefined;
55
+ let state = null;
56
+ let initPromise = null;
57
+ let currentOwner = null;
58
+ let currentOAuthRuntime = null;
59
+ let currentModel;
60
+ let lifecycleGeneration = 0;
61
+ async function shutdownState(currentState, reason) {
62
+ if (!currentState) {
63
+ publishMcpStatusShutdown(nexus.events);
64
+ return;
65
+ }
66
+ publishMcpStatusShutdown(currentState.statusEvents);
67
+ if (currentState.uiServer) {
68
+ currentState.uiServer.close(reason);
69
+ currentState.uiServer = null;
70
+ }
71
+ let flushError;
72
+ try {
73
+ flushMetadataCache(currentState);
74
+ }
75
+ catch (error) {
76
+ flushError = error;
77
+ }
78
+ try {
79
+ if (currentState.owner) {
80
+ await currentState.owner.stop(reason);
81
+ }
82
+ else {
83
+ await currentState.lifecycle.gracefulShutdown();
84
+ }
85
+ }
86
+ catch (error) {
87
+ if (flushError) {
88
+ console.error(`MCP: graceful shutdown failed after metadata flush error: ${formatTerminalError(error)}`);
89
+ }
90
+ else {
91
+ throw error;
92
+ }
93
+ }
94
+ if (flushError) {
95
+ throw flushError;
96
+ }
97
+ }
98
+ const earlyConfigPath = programmaticConfig ? undefined : (options.configPath ?? getConfigPathFromArgv());
99
+ const earlyConfig = programmaticConfig
100
+ ? cloneMcpConfig(sessionConfig)
101
+ : loadMcpConfig(earlyConfigPath, process.cwd(), false);
102
+ const earlyCache = loadMetadataCache();
103
+ const envRaw = process.env.MCP_DIRECT_TOOLS;
104
+ const envDirectToolOverride = envRaw
105
+ ?.split(",")
106
+ .map((s) => s.trim())
107
+ .filter(Boolean);
108
+ const registeredDirectTools = new Map();
109
+ const fallbackDeactivatedTools = new Set();
110
+ const toolRenderOptions = resolveMcpToolRenderOptions(earlyConfig.settings);
111
+ const toolRenderShell = toolRenderOptions.resultRendering === "compact" ? "self" : "default";
112
+ const renderMcpToolResult = createMcpToolResultRenderer(toolRenderOptions);
113
+ let proxyToolRegistered = false;
114
+ let proxyToolDescription = null;
115
+ let directToolsFrozen = false;
116
+ // Session/runtime scoped server registrations from other extensions. They
117
+ // survive session restarts within this install and die with the process.
118
+ const runtimeServers = new Map();
119
+ // Mirrors init's per-server lifecycle registration so runtime servers get
120
+ // idle cleanup and keep-alive health recovery like configured servers.
121
+ function attachRuntimeServerLifecycle(targetState, name, definition) {
122
+ const lifecycleMode = definition.lifecycle ?? "lazy";
123
+ const persistsAfterFirstSpawn = lifecycleMode === "eager" || lifecycleMode === "lazy-keep-alive";
124
+ const idleOverride = definition.idleTimeout ?? (persistsAfterFirstSpawn ? 0 : undefined);
125
+ targetState.lifecycle.registerServer(name, definition, idleOverride !== undefined ? { idleTimeout: idleOverride } : undefined);
126
+ if (lifecycleMode === "keep-alive")
127
+ targetState.lifecycle.markKeepAlive(name, definition);
128
+ }
129
+ // OMP remaps `typebox` to a host shim that historically lacked Type.Unsafe.
130
+ // Prefer Unsafe when present (real TypeBox / fixed OMP shim); otherwise pass
131
+ // the normalized JSON Schema through as a plain object so toolWireSchema and
132
+ // validateToolArguments still treat it as JSON Schema.
133
+ const toToolParameters = (schema) => typeof Type.Unsafe === "function"
134
+ ? Type.Unsafe(schema)
135
+ : schema;
136
+ function directToolFingerprint(spec) {
137
+ return JSON.stringify({
138
+ serverName: spec.serverName,
139
+ originalName: spec.originalName,
140
+ prefixedName: spec.prefixedName,
141
+ description: spec.description,
142
+ inputSchema: spec.inputSchema,
143
+ resourceUri: spec.resourceUri,
144
+ uiResourceUri: spec.uiResourceUri,
145
+ uiStreamMode: spec.uiStreamMode,
146
+ });
147
+ }
148
+ function registerDirectTool(spec) {
149
+ nexus.registerTool({
150
+ name: spec.prefixedName,
151
+ label: `MCP: ${spec.originalName}`,
152
+ description: spec.description || "(no description)",
153
+ promptSnippet: truncateAtWord(spec.description, 100) || `MCP tool from ${spec.serverName}`,
154
+ parameters: toToolParameters(normalizeDirectToolInputSchema(spec.inputSchema)),
155
+ execute: createDirectToolExecutor(() => state, () => initPromise, spec),
156
+ renderShell: toolRenderShell,
157
+ renderCall: createMcpDirectToolCallRenderer(spec.prefixedName, toolRenderOptions),
158
+ renderResult: renderMcpToolResult,
159
+ });
160
+ }
161
+ function resolveCurrentDirectTools(config, cache) {
162
+ if (envRaw === "__none__")
163
+ return [];
164
+ const prefix = config.settings?.toolPrefix ?? "server";
165
+ return resolveDirectTools(config, cache, prefix, envDirectToolOverride);
166
+ }
167
+ function getActiveToolsIfReady() {
168
+ try {
169
+ return nexus.getActiveTools?.();
170
+ }
171
+ catch (error) {
172
+ if (error instanceof Error &&
173
+ error.message.includes("Action methods cannot be called during extension loading"))
174
+ return undefined;
175
+ throw error;
176
+ }
177
+ }
178
+ function deactivateTools(toolNames) {
179
+ if (toolNames.length === 0)
180
+ return [];
181
+ const unregisterTool = nexus.unregisterTool;
182
+ const unregistered = toolNames.filter((toolName) => unregisterTool?.(toolName) === true);
183
+ const fallbackNames = toolNames.filter((toolName) => !unregistered.includes(toolName));
184
+ const remove = new Set(toolNames);
185
+ const activeTools = getActiveToolsIfReady();
186
+ if (!activeTools || activeTools.length === 0) {
187
+ for (const toolName of fallbackNames)
188
+ fallbackDeactivatedTools.add(toolName);
189
+ return unregistered;
190
+ }
191
+ const nextActiveTools = activeTools.filter((name) => !remove.has(name));
192
+ if (nextActiveTools.length !== activeTools.length) {
193
+ for (const toolName of fallbackNames)
194
+ fallbackDeactivatedTools.add(toolName);
195
+ nexus.setActiveTools(nextActiveTools);
196
+ }
197
+ return unregistered;
198
+ }
199
+ function syncDirectTools(config, cache) {
200
+ const specs = resolveCurrentDirectTools(config, cache);
201
+ const nextNames = new Set(specs.map((spec) => spec.prefixedName));
202
+ const added = [];
203
+ const updated = [];
204
+ const deactivated = [];
205
+ for (const spec of specs) {
206
+ const fingerprint = directToolFingerprint(spec);
207
+ const previous = registeredDirectTools.get(spec.prefixedName);
208
+ if (previous !== fingerprint) {
209
+ registerDirectTool(spec);
210
+ registeredDirectTools.set(spec.prefixedName, fingerprint);
211
+ if (fallbackDeactivatedTools.delete(spec.prefixedName)) {
212
+ const activeTools = getActiveToolsIfReady();
213
+ if (activeTools && !activeTools.includes(spec.prefixedName)) {
214
+ nexus.setActiveTools([...activeTools, spec.prefixedName]);
215
+ }
216
+ }
217
+ (previous ? updated : added).push(spec.prefixedName);
218
+ }
219
+ }
220
+ for (const toolName of [...registeredDirectTools.keys()]) {
221
+ if (nextNames.has(toolName))
222
+ continue;
223
+ registeredDirectTools.delete(toolName);
224
+ deactivated.push(toolName);
225
+ }
226
+ deactivateTools(deactivated);
227
+ return { specs, added, updated, deactivated };
228
+ }
229
+ function applyDirectToolConfigChanges(changes) {
230
+ if (!state)
231
+ return;
232
+ for (const [serverName, value] of changes) {
233
+ const definition = state.config.mcpServers[serverName];
234
+ if (!definition)
235
+ continue;
236
+ state.config.mcpServers[serverName] = { ...definition, directTools: value };
237
+ }
238
+ }
239
+ function syncToolSurface(ctx) {
240
+ const config = state?.config ?? earlyConfig;
241
+ const cache = loadMetadataCache();
242
+ const result = syncDirectTools(config, cache);
243
+ syncProxyTool(config, cache, result.specs);
244
+ const changed = result.added.length + result.updated.length + result.deactivated.length;
245
+ if (changed > 0 && ctx?.hasUI) {
246
+ ctx.ui.notify(`MCP: direct tools refreshed (+${result.added.length}, ~${result.updated.length}, -${result.deactivated.length})`, "info");
247
+ }
248
+ }
249
+ const registeredPromptCommands = new Set();
250
+ function registerPromptCommands(specs) {
251
+ for (const spec of specs) {
252
+ if (registeredPromptCommands.has(spec.commandName)) {
253
+ logger.debug(`MCP: prompt "${spec.originalName}" on ${spec.serverName} skipped; /${spec.commandName} is already registered`);
254
+ continue;
255
+ }
256
+ registeredPromptCommands.add(spec.commandName);
257
+ nexus.registerCommand(spec.commandName, createPromptCommand(nexus, () => state, spec));
258
+ }
259
+ }
260
+ function syncPromptCommands() {
261
+ registerPromptCommands([...(state?.promptMetadata?.values() ?? [])].flat());
262
+ }
263
+ registerPromptCommands(resolveCachedPrompts(earlyConfig));
264
+ runtimeRegistrars.set(nexus, (name, definition) => {
265
+ if (typeof name !== "string" || name.trim() === "") {
266
+ throw new Error("MCP server name must be a non-empty string");
267
+ }
268
+ if (typeof definition !== "object" || definition === null || Array.isArray(definition)) {
269
+ throw new Error(`MCP server definition for "${name}" must be an object`);
270
+ }
271
+ const effective = state?.config ?? earlyConfig;
272
+ if (runtimeServers.has(name) || Object.hasOwn(effective.mcpServers, name)) {
273
+ throw new Error(`MCP server "${name}" is already registered`);
274
+ }
275
+ // Runtime-registered servers are proxy-tool-only: direct tools are frozen
276
+ // at startup and must not be rebuilt for late registrations.
277
+ const entry = { ...structuredClone(definition), directTools: false };
278
+ runtimeServers.set(name, entry);
279
+ const registeredState = state;
280
+ if (registeredState) {
281
+ registeredState.config.mcpServers[name] = entry;
282
+ attachRuntimeServerLifecycle(registeredState, name, entry);
283
+ syncToolSurface();
284
+ updateStatusBar(registeredState);
285
+ }
286
+ let disposed = false;
287
+ return {
288
+ dispose: async () => {
289
+ if (disposed)
290
+ return;
291
+ disposed = true;
292
+ runtimeServers.delete(name);
293
+ const currentState = state;
294
+ if (!currentState || currentState.config.mcpServers[name] !== entry)
295
+ return;
296
+ delete currentState.config.mcpServers[name];
297
+ currentState.lifecycle.unregisterServer(name);
298
+ await currentState.manager.close(name);
299
+ syncToolSurface();
300
+ updateStatusBar(currentState);
301
+ },
302
+ };
303
+ });
304
+ const getNexusTools = () => nexus.getAllTools();
305
+ nexus.registerFlag("mcp-config", {
306
+ description: "Path to MCP config file",
307
+ type: "string",
308
+ });
309
+ function startInitialization(ctx, owner, oauthRuntime, generation, staleReason) {
310
+ owner.addCleanup(() => cleanupMaterializedBinaryResources(owner.signal));
311
+ const promise = initializeMcp(nexus, ctx, owner, {
312
+ ...(programmaticConfig || options.configPath !== undefined
313
+ ? {
314
+ ...(earlyConfigPath !== undefined ? { configPath: earlyConfigPath } : {}),
315
+ ...(sessionConfig !== undefined ? { config: sessionConfig } : {}),
316
+ }
317
+ : {}),
318
+ oauthRuntime,
319
+ getCurrentModel: () => currentModel,
320
+ });
321
+ initPromise = promise;
322
+ return promise
323
+ .then(async (nextState) => {
324
+ if (!owner.isActive() || generation !== lifecycleGeneration || initPromise !== promise) {
325
+ try {
326
+ await shutdownState(nextState, staleReason);
327
+ }
328
+ catch (error) {
329
+ console.error(`MCP: failed to clean stale initialization state: ${formatTerminalError(error)}`);
330
+ }
331
+ return;
332
+ }
333
+ state = nextState;
334
+ for (const [name, definition] of runtimeServers) {
335
+ if (Object.hasOwn(nextState.config.mcpServers, name)) {
336
+ console.error(`MCP: runtime-registered server "${name}" now collides with a configured server; keeping the configured server`);
71
337
  continue;
72
338
  }
73
- for (const tool of tools)
74
- pi.registerTool(defineProxyTool(client, server.name, tool));
75
- clients.push(client);
76
- notices.push(`MCP ${server.name}: ${tools.length} tool${tools.length === 1 ? "" : "s"}`);
339
+ nextState.config.mcpServers[name] = definition;
340
+ attachRuntimeServerLifecycle(nextState, name, definition);
341
+ }
342
+ nextState.onToolMetadataUpdated = (_serverName, _reason) => {
343
+ if (state !== nextState || !owner.isActive())
344
+ return;
345
+ syncPromptCommands();
346
+ if (directToolsFrozen) {
347
+ logger.debug(`MCP: metadata update for ${_serverName} (${_reason}) skipped — directTools frozen`);
348
+ return;
349
+ }
350
+ syncToolSurface(ctx);
351
+ };
352
+ syncPromptCommands();
353
+ syncToolSurface(ctx);
354
+ // A connected snapshot is readiness-like external state. Publish it only
355
+ // after Nexus's model-facing direct-tool surface reflects live metadata.
356
+ nextState.statusEvents = nexus.events;
357
+ updateStatusBar(nextState);
358
+ initPromise = null;
359
+ if (nextState.config.settings?.freezeDirectTools === true) {
360
+ directToolsFrozen = true;
361
+ logger.info('MCP: direct tools frozen after initial sync — reconnects won\'t rebuild the system prompt; use mcp({ connect: "server" }) to rediscover');
362
+ }
363
+ })
364
+ .catch(async (err) => {
365
+ if (!owner.isActive() || generation !== lifecycleGeneration) {
366
+ return;
367
+ }
368
+ if (initPromise !== promise && initPromise !== null) {
369
+ return;
370
+ }
371
+ console.error(`MCP initialization failed: ${formatTerminalError(err)}`);
372
+ initPromise = null;
373
+ if (state)
374
+ return;
375
+ try {
376
+ await Promise.all([owner.stop("MCP initialization failed"), shutdownOAuth(oauthRuntime)]);
77
377
  }
78
378
  catch (error) {
79
- // One unreachable server must not cost the others their tools.
80
- client.close();
81
- notices.push(`MCP ${server.name}: ${error instanceof Error ? error.message : String(error)}`);
379
+ console.error(`MCP: failed to clean rejected initialization: ${formatTerminalError(error)}`);
380
+ }
381
+ });
382
+ }
383
+ function startLoadTimeInitialization() {
384
+ const hasStartupServer = Object.values(earlyConfig.mcpServers).some((definition) => {
385
+ if (definition.disabled === true)
386
+ return false;
387
+ return definition.lifecycle === "eager" || definition.lifecycle === "keep-alive";
388
+ });
389
+ if (!hasStartupServer)
390
+ return;
391
+ setImmediate(() => {
392
+ if (lifecycleGeneration !== 0 || state || initPromise)
393
+ return;
394
+ const generation = ++lifecycleGeneration;
395
+ const owner = createMcpRuntimeOwner();
396
+ const oauthRuntime = createOAuthRuntime(owner.signal);
397
+ currentOwner = owner;
398
+ currentOAuthRuntime = oauthRuntime;
399
+ startInitialization({
400
+ mode: "print",
401
+ hasUI: false,
402
+ cwd: process.cwd(),
403
+ model: undefined,
404
+ modelRegistry: undefined,
405
+ signal: undefined,
406
+ isProjectTrusted: () => false,
407
+ }, owner, oauthRuntime, generation, "stale_load_time_initialization");
408
+ });
409
+ }
410
+ nexus.on("session_start", async (_event, ctx) => {
411
+ currentModel = ctx.model;
412
+ directToolsFrozen = false;
413
+ const generation = ++lifecycleGeneration;
414
+ const previousState = state;
415
+ const previousOwner = currentOwner;
416
+ const previousOAuthRuntime = currentOAuthRuntime;
417
+ const owner = createMcpRuntimeOwner();
418
+ const oauthRuntime = createOAuthRuntime(owner.signal);
419
+ currentOwner = owner;
420
+ currentOAuthRuntime = oauthRuntime;
421
+ state = null;
422
+ initPromise = null;
423
+ // Abort synchronously before awaiting cleanup so old callbacks and startup
424
+ // work cannot resume into a stale ExtensionContext.
425
+ const stopPrevious = previousOwner?.stop("MCP extension session restarted") ?? Promise.resolve();
426
+ try {
427
+ await Promise.all([
428
+ stopPrevious,
429
+ shutdownState(previousState, "session_restart"),
430
+ previousOAuthRuntime ? shutdownOAuth(previousOAuthRuntime) : Promise.resolve(),
431
+ ]);
432
+ }
433
+ catch (error) {
434
+ console.error(`MCP: failed to shut down previous session state: ${formatTerminalError(error)}`);
435
+ }
436
+ if (generation !== lifecycleGeneration || !owner.isActive())
437
+ return;
438
+ const initialization = startInitialization(ctx, owner, oauthRuntime, generation, "stale_session_start");
439
+ if (envRaw !== undefined && envRaw !== "__none__") {
440
+ const missingEnvDirectTools = getMissingConfiguredDirectToolServers(earlyConfig, loadMetadataCache(), envDirectToolOverride);
441
+ if (missingEnvDirectTools.length > 0) {
442
+ await initialization;
443
+ }
444
+ }
445
+ });
446
+ nexus.on("model_select", (event) => {
447
+ currentModel = event.model;
448
+ });
449
+ nexus.on("input", async () => {
450
+ const inputOwner = currentOwner;
451
+ if (!inputOwner?.isActive())
452
+ return;
453
+ if (!state && initPromise) {
454
+ try {
455
+ await awaitWithTimeout(initPromise, INIT_WAIT_TIMEOUT_MS);
456
+ }
457
+ catch {
458
+ return;
459
+ }
460
+ }
461
+ const inputState = state;
462
+ if (!inputState || !inputOwner.isActive())
463
+ return;
464
+ try {
465
+ await inputState.lifecycle.ensureConverged(inputOwner.signal);
466
+ }
467
+ catch (error) {
468
+ if (!isAbortError(error, inputOwner.signal)) {
469
+ logger.debug(`MCP: keep-alive convergence failed before input: ${formatTerminalError(error)}`);
82
470
  }
83
471
  }
84
- if (notices.length > 0)
85
- ctx.ui.notify(notices.join("\n"), "info");
86
472
  });
87
- pi.on("session_shutdown", async () => {
88
- // stdio servers are child processes; leaving them running would leak one
89
- // per session.
90
- for (const client of clients)
91
- client.close();
92
- clients.length = 0;
473
+ nexus.on("session_shutdown", async () => {
474
+ ++lifecycleGeneration;
475
+ const currentState = state;
476
+ const owner = currentOwner;
477
+ const oauthRuntime = currentOAuthRuntime;
478
+ currentOwner = null;
479
+ currentOAuthRuntime = null;
480
+ currentModel = undefined;
481
+ state = null;
482
+ initPromise = null;
483
+ // Abort before awaiting cleanup so delayed initialization cannot touch stale
484
+ // Nexus context after session shutdown.
485
+ const stopOwner = owner?.stop("MCP extension session shutdown") ?? Promise.resolve();
486
+ try {
487
+ await Promise.all([
488
+ stopOwner,
489
+ shutdownState(currentState, "session_shutdown"),
490
+ oauthRuntime ? shutdownOAuth(oauthRuntime) : Promise.resolve(),
491
+ ]);
492
+ }
493
+ catch (error) {
494
+ console.error(`MCP: session shutdown cleanup failed: ${formatTerminalError(error)}`);
495
+ }
496
+ });
497
+ // Re-flag returned MCP tool failures so nexus registers them as errors (see toolErrorOverride).
498
+ nexus.on("tool_result", (event) => toolErrorOverride(event.details));
499
+ const registerMcpCommand = (commandName) => nexus.registerCommand(commandName, {
500
+ description: "Show MCP server status",
501
+ getArgumentCompletions: (prefix) => {
502
+ const normalized = prefix.trimStart();
503
+ const argumentMatch = normalized.match(/^(\S+)\s+(.*)$/);
504
+ if (!argumentMatch) {
505
+ const subcommands = [
506
+ { value: "reconnect", label: "reconnect — Reconnect servers" },
507
+ { value: "tools", label: "tools — List all tools" },
508
+ { value: "prompts", label: "prompts — List all MCP prompts" },
509
+ { value: "setup", label: "setup — Configure MCP servers" },
510
+ { value: "logout", label: "logout — Clear server credentials" },
511
+ { value: "token", label: "token — Manage stored bearer tokens" },
512
+ { value: "disable", label: "disable — Disable a server" },
513
+ { value: "enable", label: "enable — Enable a server" },
514
+ { value: "status", label: "status — Show server status" },
515
+ ].filter(({ value }) => value.startsWith(normalized));
516
+ return subcommands.length > 0 ? subcommands : null;
517
+ }
518
+ const [, subcommand, argumentPrefix] = argumentMatch;
519
+ if ((subcommand !== "reconnect" &&
520
+ subcommand !== "logout" &&
521
+ subcommand !== "disable" &&
522
+ subcommand !== "enable" &&
523
+ subcommand !== "token") ||
524
+ argumentPrefix === undefined ||
525
+ !state)
526
+ return null;
527
+ if (subcommand === "token") {
528
+ const tokenMatch = argumentPrefix.trimStart().match(/^(set|remove|status)\s+(.*)$/);
529
+ if (!tokenMatch) {
530
+ const actions = ["set", "remove", "status"]
531
+ .filter((action) => action.startsWith(argumentPrefix.trimStart()))
532
+ .map((action) => ({ value: `token ${action} `, label: `${action} — Bearer token ${action}` }));
533
+ return actions.length > 0 ? actions : null;
534
+ }
535
+ const action = tokenMatch[1] ?? "";
536
+ const serverPrefix = tokenMatch[2] ?? "";
537
+ const servers = Object.keys(state.config.mcpServers)
538
+ .filter((serverName) => serverName.startsWith(serverPrefix.trimStart()))
539
+ .map((serverName) => ({ value: `token ${action} ${serverName}`, label: serverName }));
540
+ return servers.length > 0 ? servers : null;
541
+ }
542
+ const servers = Object.keys(state.config.mcpServers)
543
+ .filter((serverName) => serverName.startsWith(argumentPrefix.trimStart()))
544
+ .map((serverName) => ({ value: `${subcommand} ${serverName}`, label: serverName }));
545
+ return servers.length > 0 ? servers : null;
546
+ },
547
+ handler: async (args, ctx) => {
548
+ const commandOwner = currentOwner;
549
+ const commandReload = typeof ctx.reload === "function" ? ctx.reload.bind(ctx) : async () => { };
550
+ const commandHasUI = ctx.hasUI;
551
+ const commandCtx = {
552
+ hasUI: commandHasUI,
553
+ ui: commandHasUI ? (commandOwner ? createOwnedUi(ctx.ui, commandOwner) : ctx.ui) : undefined,
554
+ cwd: ctx.cwd,
555
+ mode: ctx.mode,
556
+ signal: commandOwner?.signal ?? ctx.signal,
557
+ isProjectTrusted: () => ctx.isProjectTrusted(),
558
+ };
559
+ if (!state && initPromise) {
560
+ try {
561
+ const initialized = await initPromise;
562
+ commandOwner?.throwIfInactive();
563
+ state = initialized;
564
+ }
565
+ catch (error) {
566
+ const message = error instanceof Error ? error.message : String(error);
567
+ if (commandCtx.hasUI)
568
+ commandCtx.ui?.notify(`MCP initialization failed: ${message}`, "error");
569
+ return;
570
+ }
571
+ }
572
+ if (!state) {
573
+ if (commandCtx.hasUI)
574
+ commandCtx.ui?.notify("MCP not initialized", "error");
575
+ return;
576
+ }
577
+ const parts = args?.trim()?.split(/\s+/) ?? [];
578
+ const subcommand = parts[0] ?? "";
579
+ const targetServer = parts[1];
580
+ const rest = parts.slice(1).join(" ");
581
+ switch (subcommand) {
582
+ case "reconnect":
583
+ commandOwner?.throwIfInactive();
584
+ await reconnectServers(state, commandCtx, targetServer);
585
+ if (directToolsFrozen)
586
+ syncToolSurface(commandCtx);
587
+ break;
588
+ case "tools":
589
+ await showTools(state, commandCtx);
590
+ break;
591
+ case "prompts":
592
+ await showPrompts(state, commandCtx);
593
+ break;
594
+ case "setup": {
595
+ commandOwner?.throwIfInactive();
596
+ if (!ctx.isProjectTrusted()) {
597
+ commandCtx.ui?.notify("Trust this project before opening MCP setup.", "warning");
598
+ break;
599
+ }
600
+ if (programmaticConfig) {
601
+ commandCtx.ui?.notify("MCP setup is unavailable when config is supplied by createMcpAdapter().", "info");
602
+ break;
603
+ }
604
+ const result = await openMcpSetup(state, nexus, commandCtx, earlyConfigPath, "setup");
605
+ if (result?.configChanged) {
606
+ commandOwner?.throwIfInactive();
607
+ await commandReload();
608
+ return;
609
+ }
610
+ break;
611
+ }
612
+ case "logout": {
613
+ const serverName = rest;
614
+ if (!serverName) {
615
+ if (commandCtx.hasUI)
616
+ commandCtx.ui?.notify("Usage: /mcp logout <server>", "error");
617
+ return;
618
+ }
619
+ commandOwner?.throwIfInactive();
620
+ await logoutServer(serverName, state, commandCtx);
621
+ break;
622
+ }
623
+ case "token": {
624
+ const action = parts[1];
625
+ const serverName = parts.slice(2).join(" ");
626
+ if (action !== "set" && action !== "remove" && action !== "status") {
627
+ if (commandCtx.hasUI)
628
+ commandCtx.ui?.notify("Usage: /mcp token set|remove|status <server>", "error");
629
+ return;
630
+ }
631
+ if (!serverName) {
632
+ if (commandCtx.hasUI)
633
+ commandCtx.ui?.notify("Usage: /mcp token set|remove|status <server>", "error");
634
+ return;
635
+ }
636
+ commandOwner?.throwIfInactive();
637
+ await manageBearerToken(action, serverName, state, commandCtx);
638
+ break;
639
+ }
640
+ case "disable":
641
+ case "enable": {
642
+ const serverName = rest;
643
+ if (!ctx.isProjectTrusted()) {
644
+ commandCtx.ui?.notify(`Trust this project before running /mcp ${subcommand}.`, "warning");
645
+ break;
646
+ }
647
+ if (programmaticConfig) {
648
+ commandCtx.ui?.notify(`/mcp ${subcommand} is unavailable when config is supplied by createMcpAdapter().`, "info");
649
+ break;
650
+ }
651
+ if (!serverName) {
652
+ commandCtx.ui?.notify(`Usage: /mcp ${subcommand} <server>`, "error");
653
+ break;
654
+ }
655
+ if (!state.config.mcpServers[serverName]) {
656
+ commandCtx.ui?.notify(`Server "${serverName}" not found in effective config`, "error");
657
+ break;
658
+ }
659
+ commandOwner?.throwIfInactive();
660
+ const result = writeProjectServerDisabledOverride(earlyConfigPath, commandCtx.cwd, serverName, subcommand === "disable");
661
+ if (result.changed) {
662
+ commandCtx.ui?.notify(`${subcommand === "disable" ? "Disabled" : "Enabled"} server "${serverName}" in ${result.path} — run /reload to apply`, "info");
663
+ }
664
+ else {
665
+ commandCtx.ui?.notify(`Server "${serverName}" is already ${subcommand === "disable" ? "disabled" : "enabled"}`, "info");
666
+ }
667
+ break;
668
+ }
669
+ default:
670
+ if (commandCtx.hasUI) {
671
+ commandOwner?.throwIfInactive();
672
+ if (programmaticConfig || !ctx.isProjectTrusted()) {
673
+ if (programmaticConfig) {
674
+ commandCtx.ui?.notify("MCP status is shown from the in-memory SDK config; configuration discovery is unavailable.", "info");
675
+ }
676
+ await showStatus(state, commandCtx);
677
+ break;
678
+ }
679
+ const result = await openMcpPanel(state, nexus, commandCtx, earlyConfigPath, (changes) => {
680
+ applyDirectToolConfigChanges(changes);
681
+ syncToolSurface(commandCtx);
682
+ });
683
+ if (result?.configChanged) {
684
+ commandOwner?.throwIfInactive();
685
+ await commandReload();
686
+ return;
687
+ }
688
+ }
689
+ else {
690
+ await showStatus(state, commandCtx);
691
+ }
692
+ break;
693
+ }
694
+ },
695
+ });
696
+ registerMcpCommand("mcp");
697
+ nexus.registerCommand("mcp-auth", {
698
+ description: "Authenticate with an MCP server (OAuth)",
699
+ handler: async (args, ctx) => {
700
+ const commandOwner = currentOwner;
701
+ const commandHasUI = ctx.hasUI;
702
+ const commandCtx = {
703
+ hasUI: commandHasUI,
704
+ ui: commandHasUI ? (commandOwner ? createOwnedUi(ctx.ui, commandOwner) : ctx.ui) : undefined,
705
+ cwd: ctx.cwd,
706
+ mode: ctx.mode,
707
+ signal: commandOwner?.signal ?? ctx.signal,
708
+ isProjectTrusted: () => ctx.isProjectTrusted(),
709
+ };
710
+ const serverName = args?.trim();
711
+ if (!serverName && !commandCtx.hasUI) {
712
+ return;
713
+ }
714
+ if (!state && initPromise) {
715
+ try {
716
+ const initialized = await initPromise;
717
+ commandOwner?.throwIfInactive();
718
+ state = initialized;
719
+ }
720
+ catch (error) {
721
+ const message = error instanceof Error ? error.message : String(error);
722
+ if (commandCtx.hasUI)
723
+ commandCtx.ui?.notify(`MCP initialization failed: ${message}`, "error");
724
+ return;
725
+ }
726
+ }
727
+ if (!state) {
728
+ if (commandCtx.hasUI)
729
+ commandCtx.ui?.notify("MCP not initialized", "error");
730
+ return;
731
+ }
732
+ if (!serverName) {
733
+ if (!ctx.isProjectTrusted()) {
734
+ commandCtx.ui?.notify("Trust this project or specify a global MCP server with /mcp-auth <server>.", "warning");
735
+ return;
736
+ }
737
+ if (programmaticConfig) {
738
+ commandCtx.ui?.notify("Use /mcp-auth <server> to authenticate a server from the in-memory SDK config.", "info");
739
+ return;
740
+ }
741
+ await openMcpAuthPanel(state, nexus, commandCtx, earlyConfigPath);
742
+ return;
743
+ }
744
+ const result = await authenticateServer(serverName, state.config, commandCtx, commandCtx.signal, state.oauthRuntime);
745
+ if (result.ok) {
746
+ commandOwner?.throwIfInactive();
747
+ await reconnectServer(state, commandCtx, serverName);
748
+ }
749
+ },
750
+ });
751
+ if (earlyConfig.settings?.scriptMode !== false) {
752
+ nexus.registerTool({
753
+ name: "mcpScript",
754
+ label: "MCP Script",
755
+ description: "Run trusted JavaScript that makes multiple MCP tool calls in one request — loop, filter, chain, or fan out between calls. For a single MCP call, search, describe, status check, or auth action, use the mcp tool instead. Discover with await tools.search({ query }) — resolves to { items: [{ path, name, server, description? }], total, hasMore, nextOffset }, not an { ok, data } envelope. Inspect with await tools.describe({ path }) — resolves to the tool descriptor with inputTypeScript, or { path, error: { code, message, suggestions } }. Then call tools.call(path, args) — resolves to { ok: true, data } or { ok: false, error: { code, message } } — or use direct flat calls when the name is already known; use emit(value) for user-visible output. Load the mcp-scripting skill for the full workflow guide.",
756
+ promptSnippet: "Batch multiple MCP tool calls in one JavaScript request (loop, filter, chain)",
757
+ parameters: Type.Object({
758
+ code: Type.String({
759
+ description: "Trusted JavaScript MCP script. Use tools.<prefixedToolName>(args) and emit(value).",
760
+ }),
761
+ timeoutMs: optionalNumber({
762
+ minimum: 1,
763
+ description: "Execution timeout in milliseconds (default: 30000)",
764
+ }),
765
+ }),
766
+ renderResult: renderMcpToolResult,
767
+ async execute(_toolCallId, params, signal) {
768
+ const executeOwner = currentOwner;
769
+ if (!state && initPromise) {
770
+ try {
771
+ const initialized = await awaitWithTimeout(initPromise, INIT_WAIT_TIMEOUT_MS);
772
+ if (initialized === INIT_WAIT_TIMED_OUT) {
773
+ return {
774
+ content: [
775
+ {
776
+ type: "text",
777
+ text: "MCP initialization is still in progress. Try again shortly.",
778
+ },
779
+ ],
780
+ details: { mode: "script", error: "init_timeout", timeoutMs: INIT_WAIT_TIMEOUT_MS },
781
+ };
782
+ }
783
+ executeOwner?.throwIfInactive();
784
+ state = initialized;
785
+ }
786
+ catch (error) {
787
+ if (executeOwner && isAbortError(error, executeOwner.signal))
788
+ throw error;
789
+ const message = error instanceof Error ? error.message : String(error);
790
+ return {
791
+ content: [{ type: "text", text: `MCP initialization failed: ${message}` }],
792
+ details: { mode: "script", error: "init_failed", message },
793
+ };
794
+ }
795
+ }
796
+ if (!state) {
797
+ return {
798
+ content: [{ type: "text", text: "MCP not initialized" }],
799
+ details: { mode: "script", error: "not_initialized" },
800
+ };
801
+ }
802
+ executeOwner?.throwIfInactive();
803
+ return runMcpScript(state, params.code, params.timeoutMs, getNexusTools, signal);
804
+ },
805
+ });
806
+ }
807
+ function registerProxyTool(description) {
808
+ nexus.registerTool({
809
+ name: "mcp",
810
+ label: "MCP",
811
+ description,
812
+ promptSnippet: "MCP gateway — status, search, describe, auth, and single MCP tool calls",
813
+ renderShell: toolRenderShell,
814
+ renderCall: createMcpProxyToolCallRenderer(toolRenderOptions),
815
+ parameters: Type.Object({
816
+ tool: Type.Optional(Type.String({ description: "Tool name to call (e.g., 'xcodebuild_list_sims')" })),
817
+ args: Type.Optional(Type.Union([
818
+ Type.String({ description: 'Arguments as a JSON string (e.g., \'{"key": "value"}\')' }),
819
+ Type.Object({}, {
820
+ additionalProperties: true,
821
+ description: 'Arguments as a JSON object (e.g., { "key": "value" })',
822
+ }),
823
+ ], { description: "Tool arguments as a JSON object, or as a JSON string encoding one" })),
824
+ connect: Type.Optional(Type.String({ description: "Server name to connect (lazy connect + metadata refresh)" })),
825
+ describe: Type.Optional(Type.String({ description: "Tool name to describe (shows parameters)" })),
826
+ instructions: Type.Optional(Type.String({ description: "Server name to show that server's usage instructions" })),
827
+ search: Type.Optional(Type.String({ description: "Search tools by name/description" })),
828
+ regex: Type.Optional(Type.Boolean({ description: "Treat search as regex (default: substring match)" })),
829
+ includeSchemas: Type.Optional(Type.Boolean({ description: "Include parameter schemas in search results (default: true)" })),
830
+ limit: optionalNumber({ minimum: 1, description: "Maximum search results to return (default: 12)" }),
831
+ offset: optionalNumber({ minimum: 0, description: "Search result offset (default: 0)" }),
832
+ server: Type.Optional(Type.String({ description: "Filter to specific server (also disambiguates tool calls)" })),
833
+ action: Type.Optional(Type.String({ description: "Action: 'ui-messages', 'auth-start', or 'auth-complete'" })),
834
+ }),
835
+ renderResult: renderMcpToolResult,
836
+ async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
837
+ const executeOwner = currentOwner;
838
+ const parseArgs = (value) => {
839
+ if (value === undefined || value === "")
840
+ return undefined;
841
+ let args;
842
+ if (typeof value === "string") {
843
+ try {
844
+ args = JSON.parse(value);
845
+ }
846
+ catch (error) {
847
+ if (error instanceof SyntaxError) {
848
+ throw new Error(`Invalid args JSON: ${error.message}`, { cause: error });
849
+ }
850
+ throw error;
851
+ }
852
+ }
853
+ else {
854
+ args = value;
855
+ }
856
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
857
+ const gotType = Array.isArray(args) ? "array" : args === null ? "null" : typeof args;
858
+ throw new Error(`Invalid args: expected a JSON object, got ${gotType}`);
859
+ }
860
+ return args;
861
+ };
862
+ const validateNestedGatewayParams = (value) => {
863
+ for (const key of [
864
+ "tool",
865
+ "connect",
866
+ "describe",
867
+ "instructions",
868
+ "search",
869
+ "server",
870
+ "action",
871
+ ]) {
872
+ if (value[key] !== undefined && typeof value[key] !== "string") {
873
+ throw new Error(`Invalid nested gateway param \`${key}\`: expected string, got ${Array.isArray(value[key]) ? "array" : typeof value[key]}`);
874
+ }
875
+ }
876
+ for (const key of ["regex", "includeSchemas"]) {
877
+ if (value[key] !== undefined && typeof value[key] !== "boolean") {
878
+ throw new Error(`Invalid nested gateway param \`${key}\`: expected boolean, got ${Array.isArray(value[key]) ? "array" : typeof value[key]}`);
879
+ }
880
+ }
881
+ for (const key of ["limit", "offset"]) {
882
+ if (value[key] !== undefined && typeof value[key] !== "number") {
883
+ throw new Error(`Invalid nested gateway param \`${key}\`: expected number, got ${Array.isArray(value[key]) ? "array" : typeof value[key]}`);
884
+ }
885
+ }
886
+ const args = value.args;
887
+ if (args !== undefined &&
888
+ typeof args !== "string" &&
889
+ (typeof args !== "object" || args === null || Array.isArray(args))) {
890
+ throw new Error(`Invalid nested gateway param \`args\`: expected JSON object or JSON string, got ${Array.isArray(args) ? "array" : args === null ? "null" : typeof args}`);
891
+ }
892
+ return value;
893
+ };
894
+ let parsedArgs = parseArgs(params.args);
895
+ let dispatchParams = params;
896
+ const hasGatewayMode = (value) => value.tool !== undefined ||
897
+ value.connect !== undefined ||
898
+ value.describe !== undefined ||
899
+ value.instructions !== undefined ||
900
+ value.search !== undefined ||
901
+ value.server !== undefined ||
902
+ value.action !== undefined;
903
+ if (!hasGatewayMode(params) && parsedArgs) {
904
+ const nestedParams = validateNestedGatewayParams(parsedArgs);
905
+ if (hasGatewayMode(nestedParams)) {
906
+ dispatchParams = nestedParams;
907
+ parsedArgs = parseArgs(nestedParams.args);
908
+ }
909
+ else {
910
+ throw new Error('Gateway params were nested inside `args`; pass them top-level (for example, mcp({ search: "..." }) or mcp({ tool: "...", args: {} })).');
911
+ }
912
+ }
913
+ else if (!hasGatewayMode(params) && params.args !== undefined) {
914
+ throw new Error('Gateway params were nested inside `args`; pass them top-level (for example, mcp({ search: "..." }) or mcp({ tool: "...", args: {} })).');
915
+ }
916
+ if (!state && initPromise) {
917
+ try {
918
+ const initialized = await awaitWithTimeout(initPromise, INIT_WAIT_TIMEOUT_MS);
919
+ if (initialized === INIT_WAIT_TIMED_OUT) {
920
+ return {
921
+ content: [
922
+ {
923
+ type: "text",
924
+ text: "MCP initialization is still in progress. Try again shortly.",
925
+ },
926
+ ],
927
+ details: { error: "init_timeout", timeoutMs: INIT_WAIT_TIMEOUT_MS },
928
+ };
929
+ }
930
+ executeOwner?.throwIfInactive();
931
+ state = initialized;
932
+ }
933
+ catch (error) {
934
+ if (executeOwner && isAbortError(error, executeOwner.signal))
935
+ throw error;
936
+ const message = error instanceof Error ? error.message : String(error);
937
+ return {
938
+ content: [{ type: "text", text: `MCP initialization failed: ${message}` }],
939
+ details: { error: "init_failed", message },
940
+ };
941
+ }
942
+ }
943
+ if (!state) {
944
+ return {
945
+ content: [{ type: "text", text: "MCP not initialized" }],
946
+ details: { error: "not_initialized" },
947
+ };
948
+ }
949
+ executeOwner?.throwIfInactive();
950
+ if (dispatchParams.action === "ui-messages") {
951
+ return executeUiMessages(state);
952
+ }
953
+ if (dispatchParams.action === "auth-start") {
954
+ if (!dispatchParams.server) {
955
+ return {
956
+ content: [
957
+ {
958
+ type: "text",
959
+ text: 'auth-start requires `server`. Example: mcp({ action: "auth-start", server: "linear-server" })',
960
+ },
961
+ ],
962
+ details: { mode: "auth-start", error: "missing_server" },
963
+ };
964
+ }
965
+ return signal
966
+ ? executeAuthStart(state, dispatchParams.server, signal)
967
+ : executeAuthStart(state, dispatchParams.server);
968
+ }
969
+ if (dispatchParams.action === "auth-complete") {
970
+ if (!dispatchParams.server) {
971
+ return {
972
+ content: [{ type: "text", text: "auth-complete requires `server`." }],
973
+ details: { mode: "auth-complete", error: "missing_server" },
974
+ };
975
+ }
976
+ const input = parsedArgs?.redirectUrl ?? parsedArgs?.code ?? parsedArgs?.input;
977
+ if (typeof input !== "string" || input.trim().length === 0) {
978
+ return {
979
+ content: [
980
+ {
981
+ type: "text",
982
+ text: "auth-complete requires args with `redirectUrl`, `code`, or `input`.",
983
+ },
984
+ ],
985
+ details: { mode: "auth-complete", error: "missing_input" },
986
+ };
987
+ }
988
+ return signal
989
+ ? executeAuthComplete(state, dispatchParams.server, input, signal)
990
+ : executeAuthComplete(state, dispatchParams.server, input);
991
+ }
992
+ if (dispatchParams.tool) {
993
+ return executeCall(state, dispatchParams.tool, parsedArgs, dispatchParams.server, getNexusTools, signal);
994
+ }
995
+ if (dispatchParams.connect) {
996
+ const result = await executeConnect(state, dispatchParams.connect, signal);
997
+ syncToolSurface(_ctx);
998
+ return result;
999
+ }
1000
+ if (dispatchParams.describe) {
1001
+ return executeDescribe(state, dispatchParams.describe);
1002
+ }
1003
+ if (dispatchParams.instructions) {
1004
+ return executeInstructions(state, dispatchParams.instructions);
1005
+ }
1006
+ if (dispatchParams.search !== undefined) {
1007
+ return executeSearch(state, dispatchParams.search, dispatchParams.regex, dispatchParams.server, dispatchParams.includeSchemas, dispatchParams.limit, dispatchParams.offset);
1008
+ }
1009
+ if (dispatchParams.server) {
1010
+ return executeList(state, dispatchParams.server);
1011
+ }
1012
+ return executeStatus(state);
1013
+ },
1014
+ });
1015
+ proxyToolRegistered = true;
1016
+ proxyToolDescription = description;
1017
+ }
1018
+ function syncProxyTool(config, cache, directSpecs) {
1019
+ const missingConfiguredDirectToolServers = getMissingConfiguredDirectToolServers(config, cache, envRaw === undefined || envRaw === "__none__" ? undefined : envDirectToolOverride);
1020
+ const shouldRegisterProxyTool = config.settings?.disableProxyTool !== true ||
1021
+ directSpecs.length === 0 ||
1022
+ missingConfiguredDirectToolServers.length > 0;
1023
+ if (shouldRegisterProxyTool) {
1024
+ const description = buildProxyDescription(config, cache, directSpecs);
1025
+ if (!proxyToolRegistered || proxyToolDescription !== description) {
1026
+ registerProxyTool(description);
1027
+ return;
1028
+ }
1029
+ const activeTools = getActiveToolsIfReady();
1030
+ if (activeTools && !activeTools.includes("mcp")) {
1031
+ nexus.setActiveTools([...activeTools, "mcp"]);
1032
+ }
1033
+ return;
1034
+ }
1035
+ if (proxyToolRegistered) {
1036
+ const unregistered = deactivateTools(["mcp"]);
1037
+ if (unregistered.includes("mcp")) {
1038
+ proxyToolRegistered = false;
1039
+ proxyToolDescription = null;
1040
+ }
1041
+ }
1042
+ }
1043
+ nexus.on("resources_discover", () => {
1044
+ const skillsPath = join(import.meta.dirname, "skills");
1045
+ if (existsSync(skillsPath))
1046
+ return { skillPaths: [skillsPath] };
93
1047
  });
1048
+ const initialDirectTools = syncDirectTools(earlyConfig, earlyCache).specs;
1049
+ syncProxyTool(earlyConfig, earlyCache, initialDirectTools);
1050
+ startLoadTimeInitialization();
1051
+ }
1052
+ export function createMcpAdapter(options = {}) {
1053
+ const factoryConfig = options.config !== undefined ? cloneMcpConfig(options.config) : undefined;
1054
+ return function mcpAdapter(nexus) {
1055
+ installMcpAdapter(nexus, {
1056
+ ...(options.configPath !== undefined ? { configPath: options.configPath } : {}),
1057
+ ...(factoryConfig !== undefined ? { config: cloneMcpConfig(factoryConfig) } : {}),
1058
+ });
1059
+ };
1060
+ }
1061
+ /**
1062
+ * Register an MCP server with the adapter installed for this Nexus instance.
1063
+ * Registrations are session/runtime scoped and never persisted. Duplicate
1064
+ * names fail closed. Registered servers are proxy-tool-only; their tools
1065
+ * become visible at the next tool sync. To change a definition, dispose the
1066
+ * registration and register again.
1067
+ */
1068
+ export function registerMcpServer(options) {
1069
+ const { nexus, name, definition } = options;
1070
+ const register = runtimeRegistrars.get(nexus);
1071
+ if (!register) {
1072
+ throw new Error("The native Nexus MCP runtime is not installed for this instance");
1073
+ }
1074
+ return register(name, definition);
94
1075
  }
1076
+ export default createMcpAdapter();
95
1077
  //# sourceMappingURL=index.js.map