@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akon-labs/nexus-cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "GitNexus-native coding agent CLI",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -46,8 +46,8 @@
46
46
  "scripts": {
47
47
  "clean": "shx rm -rf dist",
48
48
  "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js dist/rpc-entry.js && npm run copy-assets && shx mkdir -p dist/extensions/gitnexus/skills && shx cp -r src/extensions/gitnexus/skills/* dist/extensions/gitnexus/skills/ && npm run bundle",
49
- "build:binary": "npm --prefix ../tui run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm --prefix ../protocol run build && npm --prefix ../client run build && npm run build && bun build --compile ./dist/bun/cli.js ./src/utils/image-resize-worker.ts --outfile dist/pi && npm run copy-binary-assets",
50
- "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
49
+ "build:binary": "npm --prefix ../tui run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm --prefix ../protocol run build && npm --prefix ../client run build && npm run build && bun build --compile ./dist/bun/cli.js ./src/utils/image-resize-worker.ts ./src/extensions/mcp/mcp-script-worker.mjs --outfile dist/pi && npm run copy-binary-assets",
50
+ "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/ && shx mkdir -p dist/extensions/mcp/skills dist/skills && shx cp src/extensions/mcp/app-bridge.bundle.js src/extensions/mcp/mcp-script-worker.mjs src/extensions/mcp/mcp-keyring-helper.cjs dist/ && shx cp src/extensions/mcp/app-bridge.bundle.js src/extensions/mcp/mcp-script-worker.mjs src/extensions/mcp/mcp-keyring-helper.cjs dist/extensions/mcp/ && shx cp -r src/extensions/mcp/skills/* dist/extensions/mcp/skills/ && shx cp -r src/extensions/mcp/skills/* dist/skills/",
51
51
  "copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
52
52
  "test": "vitest --run",
53
53
  "shrinkwrap": "node ../../scripts/generate-coding-agent-shrinkwrap.mjs",
@@ -59,9 +59,14 @@
59
59
  "@aws-sdk/client-bedrock-runtime": "3.1048.0",
60
60
  "@google/genai": "1.52.0",
61
61
  "@mistralai/mistralai": "2.2.6",
62
+ "@modelcontextprotocol/client": "2.0.0",
63
+ "@modelcontextprotocol/core": "2.0.0",
64
+ "@napi-rs/keyring": "1.3.0",
62
65
  "@opentelemetry/api": "1.9.0",
63
66
  "@silvia-odwyer/photon-node": "0.3.4",
64
67
  "@smithy/node-http-handler": "4.7.3",
68
+ "ajv": "8.20.0",
69
+ "ajv-formats": "3.0.1",
65
70
  "chalk": "5.6.2",
66
71
  "cross-spawn": "7.0.6",
67
72
  "diff": "8.0.4",
@@ -76,13 +81,19 @@
76
81
  "jiti": "2.7.0",
77
82
  "marked": "18.0.5",
78
83
  "minimatch": "10.2.5",
84
+ "open": "10.2.0",
79
85
  "openai": "6.26.0",
80
86
  "partial-json": "0.1.7",
87
+ "pi-subagents": "0.60.0",
81
88
  "proper-lockfile": "4.1.2",
89
+ "recheck": "4.5.0",
82
90
  "semver": "7.8.0",
91
+ "smol-toml": "1.6.1",
92
+ "strip-json-comments": "5.0.3",
83
93
  "typebox": "1.3.7",
84
94
  "undici": "8.9.0",
85
- "yaml": "2.9.0"
95
+ "yaml": "2.9.0",
96
+ "zod": "4.4.3"
86
97
  },
87
98
  "overrides": {
88
99
  "protobufjs": "7.6.5",
@@ -1,86 +0,0 @@
1
- /**
2
- * Minimal MCP client covering the subset needed to borrow another server's
3
- * tools: initialize, notifications/initialized, tools/list, tools/call.
4
- *
5
- * Two transports, because real servers split roughly evenly between them:
6
- * stdio for anything you launch locally (filesystem, github, postgres) and
7
- * Streamable HTTP for anything hosted. Both speak the same JSON-RPC, so only
8
- * the framing differs.
9
- *
10
- * The gitnexus client is deliberately left alone: it is pinned to one server,
11
- * carries hub-specific auth headers, and its error messages name the hub.
12
- */
13
- export declare const MCP_PROTOCOL_VERSION = "2025-06-18";
14
- export interface McpToolSchema {
15
- name: string;
16
- description?: string;
17
- inputSchema?: Record<string, unknown>;
18
- }
19
- export interface McpToolResult {
20
- content?: Array<{
21
- type: string;
22
- text?: string;
23
- data?: string;
24
- mimeType?: string;
25
- }>;
26
- isError?: boolean;
27
- }
28
- interface JsonRpcResponse {
29
- id?: number | string | null;
30
- result?: unknown;
31
- error?: {
32
- code?: number;
33
- message?: string;
34
- };
35
- }
36
- export declare class McpClientError extends Error {
37
- readonly kind: "spawn" | "network" | "protocol" | "timeout";
38
- constructor(message: string, kind: McpClientError["kind"]);
39
- }
40
- /** A transport carries one JSON-RPC message and returns the matching response. */
41
- export interface McpTransport {
42
- send(body: unknown, signal?: AbortSignal): Promise<JsonRpcResponse | undefined>;
43
- close(): void;
44
- }
45
- export declare class HttpTransport implements McpTransport {
46
- private sessionId;
47
- private readonly url;
48
- private readonly headers;
49
- constructor(url: string, headers?: Record<string, string>);
50
- send(body: unknown, signal?: AbortSignal): Promise<JsonRpcResponse | undefined>;
51
- close(): void;
52
- }
53
- /** Pull the last id-bearing JSON-RPC message out of an SSE body. */
54
- export declare function parseSse(sse: string): JsonRpcResponse | undefined;
55
- export declare class StdioTransport implements McpTransport {
56
- private child;
57
- private buffer;
58
- private readonly pending;
59
- private startError;
60
- private readonly command;
61
- private readonly args;
62
- private readonly env;
63
- private readonly cwd;
64
- constructor(command: string, args?: string[], env?: Record<string, string>, cwd?: string);
65
- private ensureStarted;
66
- /** Messages are newline-delimited JSON, so split on newlines and keep the tail. */
67
- private consume;
68
- send(body: unknown, signal?: AbortSignal): Promise<JsonRpcResponse | undefined>;
69
- close(): void;
70
- }
71
- export declare class McpClient {
72
- private nextId;
73
- private initializing;
74
- readonly serverName: string;
75
- private readonly transport;
76
- constructor(serverName: string, transport: McpTransport);
77
- private ensureInitialized;
78
- private initialize;
79
- /** Tools this server offers. Paginates until the server stops sending a cursor. */
80
- listTools(signal?: AbortSignal): Promise<McpToolSchema[]>;
81
- callTool(name: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<McpToolResult>;
82
- private request;
83
- close(): void;
84
- }
85
- export {};
86
- //# sourceMappingURL=client.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/extensions/mcp/client.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAGjD,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,eAAe;IACxB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C;AAED,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAE5D,YAAY,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,EAIxD;CACD;AAED,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC5B,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;IAChF,KAAK,IAAI,IAAI,CAAC;CACd;AAID,qBAAa,aAAc,YAAW,YAAY;IACjD,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IAEjD,YAAY,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EAG5D;IAEK,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAkDpF;IAED,KAAK,IAAI,IAAI,CAEZ;CACD;AAED,oEAAoE;AACpE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAkBjE;AAID,qBAAa,cAAe,YAAW,YAAY;IAClD,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmE;IAC3F,OAAO,CAAC,UAAU,CAAqB;IAEvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAW;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAyB;IAC7C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;IAEzC,YAAY,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAO,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAK/F;IAED,OAAO,CAAC,aAAa;IA+BrB,mFAAmF;IACnF,OAAO,CAAC,OAAO;IAsBT,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAwBpF;IAED,KAAK,IAAI,IAAI,CAIZ;CACD;AAID,qBAAa,SAAS;IACrB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,YAAY,CAA4B;IAEhD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IAEzC,YAAY,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAGtD;IAED,OAAO,CAAC,iBAAiB;YASX,UAAU;IAaxB,mFAAmF;IAC7E,SAAS,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAgB9D;IAEK,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAIxG;YAEa,OAAO;IAUrB,KAAK,IAAI,IAAI,CAEZ;CACD","sourcesContent":["import type { ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { spawn } from \"node:child_process\";\n\n/**\n * Minimal MCP client covering the subset needed to borrow another server's\n * tools: initialize, notifications/initialized, tools/list, tools/call.\n *\n * Two transports, because real servers split roughly evenly between them:\n * stdio for anything you launch locally (filesystem, github, postgres) and\n * Streamable HTTP for anything hosted. Both speak the same JSON-RPC, so only\n * the framing differs.\n *\n * The gitnexus client is deliberately left alone: it is pinned to one server,\n * carries hub-specific auth headers, and its error messages name the hub.\n */\n\nexport const MCP_PROTOCOL_VERSION = \"2025-06-18\";\nconst REQUEST_TIMEOUT_MS = 60_000;\n\nexport interface McpToolSchema {\n\tname: string;\n\tdescription?: string;\n\tinputSchema?: Record<string, unknown>;\n}\n\nexport interface McpToolResult {\n\tcontent?: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tisError?: boolean;\n}\n\ninterface JsonRpcResponse {\n\tid?: number | string | null;\n\tresult?: unknown;\n\terror?: { code?: number; message?: string };\n}\n\nexport class McpClientError extends Error {\n\treadonly kind: \"spawn\" | \"network\" | \"protocol\" | \"timeout\";\n\n\tconstructor(message: string, kind: McpClientError[\"kind\"]) {\n\t\tsuper(message);\n\t\tthis.name = \"McpClientError\";\n\t\tthis.kind = kind;\n\t}\n}\n\n/** A transport carries one JSON-RPC message and returns the matching response. */\nexport interface McpTransport {\n\tsend(body: unknown, signal?: AbortSignal): Promise<JsonRpcResponse | undefined>;\n\tclose(): void;\n}\n\n// ---------------------------------------------------------------- HTTP\n\nexport class HttpTransport implements McpTransport {\n\tprivate sessionId: string | undefined;\n\tprivate readonly url: string;\n\tprivate readonly headers: Record<string, string>;\n\n\tconstructor(url: string, headers: Record<string, string> = {}) {\n\t\tthis.url = url;\n\t\tthis.headers = headers;\n\t}\n\n\tasync send(body: unknown, signal?: AbortSignal): Promise<JsonRpcResponse | undefined> {\n\t\tconst controller = new AbortController();\n\t\tconst timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n\t\tconst onAbort = () => controller.abort();\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\ttry {\n\t\t\tlet response: Response;\n\t\t\ttry {\n\t\t\t\tresponse = await fetch(this.url, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t\taccept: \"application/json, text/event-stream\",\n\t\t\t\t\t\t...this.headers,\n\t\t\t\t\t\t...(this.sessionId ? { \"mcp-session-id\": this.sessionId } : {}),\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tthrow new McpClientError(\n\t\t\t\t\t`cannot reach MCP server ${this.url}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t\"network\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst newSession = response.headers.get(\"mcp-session-id\");\n\t\t\tif (newSession) this.sessionId = newSession;\n\n\t\t\t// An unconsumed body pins its socket, and under a connection-capped\n\t\t\t// dispatcher that deadlocks every later call to the same server.\n\t\t\tif (response.status === 202) {\n\t\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (!response.ok) {\n\t\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\t\tthrow new McpClientError(`MCP server ${this.url} returned HTTP ${response.status}`, \"protocol\");\n\t\t\t}\n\n\t\t\tconst text = await response.text();\n\t\t\tif (!text) return undefined;\n\t\t\tif ((response.headers.get(\"content-type\") ?? \"\").includes(\"text/event-stream\")) {\n\t\t\t\treturn parseSse(text);\n\t\t\t}\n\t\t\treturn JSON.parse(text) as JsonRpcResponse;\n\t\t} finally {\n\t\t\tclearTimeout(timeout);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t}\n\t}\n\n\tclose(): void {\n\t\t// Nothing to tear down: each call is its own request.\n\t}\n}\n\n/** Pull the last id-bearing JSON-RPC message out of an SSE body. */\nexport function parseSse(sse: string): JsonRpcResponse | undefined {\n\tlet last: JsonRpcResponse | undefined;\n\tfor (const rawEvent of sse.split(\"\\n\\n\")) {\n\t\tconst data = rawEvent\n\t\t\t.split(\"\\n\")\n\t\t\t.filter((line) => line.startsWith(\"data:\"))\n\t\t\t.map((line) => line.slice(5).trim())\n\t\t\t.join(\"\\n\");\n\t\tif (!data) continue;\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(data) as JsonRpcResponse;\n\t\t\t// Responses carry an id; server-initiated notifications do not.\n\t\t\tif (parsed.id !== undefined && parsed.id !== null) last = parsed;\n\t\t} catch {\n\t\t\t// Ignore non-JSON keepalives.\n\t\t}\n\t}\n\treturn last;\n}\n\n// ---------------------------------------------------------------- stdio\n\nexport class StdioTransport implements McpTransport {\n\tprivate child: ChildProcessWithoutNullStreams | undefined;\n\tprivate buffer = \"\";\n\tprivate readonly pending = new Map<number | string, (response: JsonRpcResponse) => void>();\n\tprivate startError: string | undefined;\n\n\tprivate readonly command: string;\n\tprivate readonly args: string[];\n\tprivate readonly env: Record<string, string>;\n\tprivate readonly cwd: string | undefined;\n\n\tconstructor(command: string, args: string[] = [], env: Record<string, string> = {}, cwd?: string) {\n\t\tthis.command = command;\n\t\tthis.args = args;\n\t\tthis.env = env;\n\t\tthis.cwd = cwd;\n\t}\n\n\tprivate ensureStarted(): ChildProcessWithoutNullStreams {\n\t\tif (this.startError) throw new McpClientError(this.startError, \"spawn\");\n\t\tif (this.child) return this.child;\n\t\tlet child: ChildProcessWithoutNullStreams;\n\t\ttry {\n\t\t\tchild = spawn(this.command, this.args, {\n\t\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\t\tenv: { ...process.env, ...this.env },\n\t\t\t\tcwd: this.cwd,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.startError = `cannot start MCP server \"${this.command}\": ${\n\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t}`;\n\t\t\tthrow new McpClientError(this.startError, \"spawn\");\n\t\t}\n\t\tchild.on(\"error\", (error) => {\n\t\t\t// ENOENT surfaces here, not from spawn(), so record it for the next call.\n\t\t\tthis.startError = `cannot start MCP server \"${this.command}\": ${error.message}`;\n\t\t\tfor (const resolve of this.pending.values()) resolve({ error: { message: this.startError } });\n\t\t\tthis.pending.clear();\n\t\t});\n\t\tchild.stdout.setEncoding(\"utf8\");\n\t\tchild.stdout.on(\"data\", (chunk: string) => this.consume(chunk));\n\t\t// stderr is the server's log stream, not protocol. Draining it keeps a\n\t\t// chatty server from filling its pipe buffer and hanging.\n\t\tchild.stderr.resume();\n\t\tthis.child = child;\n\t\treturn child;\n\t}\n\n\t/** Messages are newline-delimited JSON, so split on newlines and keep the tail. */\n\tprivate consume(chunk: string): void {\n\t\tthis.buffer += chunk;\n\t\tlet newline = this.buffer.indexOf(\"\\n\");\n\t\twhile (newline >= 0) {\n\t\t\tconst line = this.buffer.slice(0, newline).trim();\n\t\t\tthis.buffer = this.buffer.slice(newline + 1);\n\t\t\tnewline = this.buffer.indexOf(\"\\n\");\n\t\t\tif (!line) continue;\n\t\t\ttry {\n\t\t\t\tconst message = JSON.parse(line) as JsonRpcResponse;\n\t\t\t\tif (message.id === undefined || message.id === null) continue;\n\t\t\t\tconst resolve = this.pending.get(message.id);\n\t\t\t\tif (resolve) {\n\t\t\t\t\tthis.pending.delete(message.id);\n\t\t\t\t\tresolve(message);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// A server that prints non-JSON to stdout is not fatal; skip the line.\n\t\t\t}\n\t\t}\n\t}\n\n\tasync send(body: unknown, signal?: AbortSignal): Promise<JsonRpcResponse | undefined> {\n\t\tconst child = this.ensureStarted();\n\t\tconst id = (body as { id?: number | string }).id;\n\t\tchild.stdin.write(`${JSON.stringify(body)}\\n`);\n\t\t// Notifications carry no id and get no reply — waiting would always time out.\n\t\tif (id === undefined || id === null) return undefined;\n\n\t\treturn await new Promise<JsonRpcResponse>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.pending.delete(id);\n\t\t\t\treject(new McpClientError(`MCP server \"${this.command}\" timed out`, \"timeout\"));\n\t\t\t}, REQUEST_TIMEOUT_MS);\n\t\t\tconst onAbort = () => {\n\t\t\t\tthis.pending.delete(id);\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(new McpClientError(`MCP call to \"${this.command}\" aborted`, \"timeout\"));\n\t\t\t};\n\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\tthis.pending.set(id, (response) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve(response);\n\t\t\t});\n\t\t});\n\t}\n\n\tclose(): void {\n\t\tthis.child?.kill();\n\t\tthis.child = undefined;\n\t\tthis.pending.clear();\n\t}\n}\n\n// ---------------------------------------------------------------- client\n\nexport class McpClient {\n\tprivate nextId = 1;\n\tprivate initializing: Promise<void> | undefined;\n\n\treadonly serverName: string;\n\tprivate readonly transport: McpTransport;\n\n\tconstructor(serverName: string, transport: McpTransport) {\n\t\tthis.serverName = serverName;\n\t\tthis.transport = transport;\n\t}\n\n\tprivate ensureInitialized(signal?: AbortSignal): Promise<void> {\n\t\tthis.initializing ??= this.initialize(signal).catch((error) => {\n\t\t\t// Clear the memo so a later call can retry rather than replaying the failure.\n\t\t\tthis.initializing = undefined;\n\t\t\tthrow error;\n\t\t});\n\t\treturn this.initializing;\n\t}\n\n\tprivate async initialize(signal?: AbortSignal): Promise<void> {\n\t\tawait this.request(\n\t\t\t\"initialize\",\n\t\t\t{\n\t\t\t\tprotocolVersion: MCP_PROTOCOL_VERSION,\n\t\t\t\tcapabilities: {},\n\t\t\t\tclientInfo: { name: \"nexus-cli\", version: \"0.1.0\" },\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tawait this.transport.send({ jsonrpc: \"2.0\", method: \"notifications/initialized\" });\n\t}\n\n\t/** Tools this server offers. Paginates until the server stops sending a cursor. */\n\tasync listTools(signal?: AbortSignal): Promise<McpToolSchema[]> {\n\t\tawait this.ensureInitialized(signal);\n\t\tconst tools: McpToolSchema[] = [];\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = (await this.request(\"tools/list\", cursor ? { cursor } : {}, signal)) as\n\t\t\t\t| { tools?: McpToolSchema[]; nextCursor?: string }\n\t\t\t\t| undefined;\n\t\t\tfor (const tool of result?.tools ?? []) {\n\t\t\t\tif (tool?.name) tools.push(tool);\n\t\t\t}\n\t\t\tconst next = result?.nextCursor;\n\t\t\t// A server that echoes the same cursor would loop forever.\n\t\t\tcursor = next && next !== cursor ? next : undefined;\n\t\t} while (cursor);\n\t\treturn tools;\n\t}\n\n\tasync callTool(name: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<McpToolResult> {\n\t\tawait this.ensureInitialized(signal);\n\t\tconst result = await this.request(\"tools/call\", { name, arguments: args }, signal);\n\t\treturn (result ?? {}) as McpToolResult;\n\t}\n\n\tprivate async request(method: string, params: unknown, signal?: AbortSignal): Promise<unknown> {\n\t\tconst id = this.nextId++;\n\t\tconst response = await this.transport.send({ jsonrpc: \"2.0\", id, method, params }, signal);\n\t\tif (!response) throw new McpClientError(`empty response for ${method}`, \"protocol\");\n\t\tif (response.error) {\n\t\t\tthrow new McpClientError(`${this.serverName} ${method} failed: ${response.error.message}`, \"protocol\");\n\t\t}\n\t\treturn response.result;\n\t}\n\n\tclose(): void {\n\t\tthis.transport.close();\n\t}\n}\n"]}
@@ -1,275 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- /**
3
- * Minimal MCP client covering the subset needed to borrow another server's
4
- * tools: initialize, notifications/initialized, tools/list, tools/call.
5
- *
6
- * Two transports, because real servers split roughly evenly between them:
7
- * stdio for anything you launch locally (filesystem, github, postgres) and
8
- * Streamable HTTP for anything hosted. Both speak the same JSON-RPC, so only
9
- * the framing differs.
10
- *
11
- * The gitnexus client is deliberately left alone: it is pinned to one server,
12
- * carries hub-specific auth headers, and its error messages name the hub.
13
- */
14
- export const MCP_PROTOCOL_VERSION = "2025-06-18";
15
- const REQUEST_TIMEOUT_MS = 60_000;
16
- export class McpClientError extends Error {
17
- kind;
18
- constructor(message, kind) {
19
- super(message);
20
- this.name = "McpClientError";
21
- this.kind = kind;
22
- }
23
- }
24
- // ---------------------------------------------------------------- HTTP
25
- export class HttpTransport {
26
- sessionId;
27
- url;
28
- headers;
29
- constructor(url, headers = {}) {
30
- this.url = url;
31
- this.headers = headers;
32
- }
33
- async send(body, signal) {
34
- const controller = new AbortController();
35
- const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
36
- const onAbort = () => controller.abort();
37
- signal?.addEventListener("abort", onAbort, { once: true });
38
- try {
39
- let response;
40
- try {
41
- response = await fetch(this.url, {
42
- method: "POST",
43
- headers: {
44
- "content-type": "application/json",
45
- accept: "application/json, text/event-stream",
46
- ...this.headers,
47
- ...(this.sessionId ? { "mcp-session-id": this.sessionId } : {}),
48
- },
49
- body: JSON.stringify(body),
50
- signal: controller.signal,
51
- });
52
- }
53
- catch (error) {
54
- throw new McpClientError(`cannot reach MCP server ${this.url}: ${error instanceof Error ? error.message : String(error)}`, "network");
55
- }
56
- const newSession = response.headers.get("mcp-session-id");
57
- if (newSession)
58
- this.sessionId = newSession;
59
- // An unconsumed body pins its socket, and under a connection-capped
60
- // dispatcher that deadlocks every later call to the same server.
61
- if (response.status === 202) {
62
- await response.body?.cancel().catch(() => { });
63
- return undefined;
64
- }
65
- if (!response.ok) {
66
- await response.body?.cancel().catch(() => { });
67
- throw new McpClientError(`MCP server ${this.url} returned HTTP ${response.status}`, "protocol");
68
- }
69
- const text = await response.text();
70
- if (!text)
71
- return undefined;
72
- if ((response.headers.get("content-type") ?? "").includes("text/event-stream")) {
73
- return parseSse(text);
74
- }
75
- return JSON.parse(text);
76
- }
77
- finally {
78
- clearTimeout(timeout);
79
- signal?.removeEventListener("abort", onAbort);
80
- }
81
- }
82
- close() {
83
- // Nothing to tear down: each call is its own request.
84
- }
85
- }
86
- /** Pull the last id-bearing JSON-RPC message out of an SSE body. */
87
- export function parseSse(sse) {
88
- let last;
89
- for (const rawEvent of sse.split("\n\n")) {
90
- const data = rawEvent
91
- .split("\n")
92
- .filter((line) => line.startsWith("data:"))
93
- .map((line) => line.slice(5).trim())
94
- .join("\n");
95
- if (!data)
96
- continue;
97
- try {
98
- const parsed = JSON.parse(data);
99
- // Responses carry an id; server-initiated notifications do not.
100
- if (parsed.id !== undefined && parsed.id !== null)
101
- last = parsed;
102
- }
103
- catch {
104
- // Ignore non-JSON keepalives.
105
- }
106
- }
107
- return last;
108
- }
109
- // ---------------------------------------------------------------- stdio
110
- export class StdioTransport {
111
- child;
112
- buffer = "";
113
- pending = new Map();
114
- startError;
115
- command;
116
- args;
117
- env;
118
- cwd;
119
- constructor(command, args = [], env = {}, cwd) {
120
- this.command = command;
121
- this.args = args;
122
- this.env = env;
123
- this.cwd = cwd;
124
- }
125
- ensureStarted() {
126
- if (this.startError)
127
- throw new McpClientError(this.startError, "spawn");
128
- if (this.child)
129
- return this.child;
130
- let child;
131
- try {
132
- child = spawn(this.command, this.args, {
133
- stdio: ["pipe", "pipe", "pipe"],
134
- env: { ...process.env, ...this.env },
135
- cwd: this.cwd,
136
- });
137
- }
138
- catch (error) {
139
- this.startError = `cannot start MCP server "${this.command}": ${error instanceof Error ? error.message : String(error)}`;
140
- throw new McpClientError(this.startError, "spawn");
141
- }
142
- child.on("error", (error) => {
143
- // ENOENT surfaces here, not from spawn(), so record it for the next call.
144
- this.startError = `cannot start MCP server "${this.command}": ${error.message}`;
145
- for (const resolve of this.pending.values())
146
- resolve({ error: { message: this.startError } });
147
- this.pending.clear();
148
- });
149
- child.stdout.setEncoding("utf8");
150
- child.stdout.on("data", (chunk) => this.consume(chunk));
151
- // stderr is the server's log stream, not protocol. Draining it keeps a
152
- // chatty server from filling its pipe buffer and hanging.
153
- child.stderr.resume();
154
- this.child = child;
155
- return child;
156
- }
157
- /** Messages are newline-delimited JSON, so split on newlines and keep the tail. */
158
- consume(chunk) {
159
- this.buffer += chunk;
160
- let newline = this.buffer.indexOf("\n");
161
- while (newline >= 0) {
162
- const line = this.buffer.slice(0, newline).trim();
163
- this.buffer = this.buffer.slice(newline + 1);
164
- newline = this.buffer.indexOf("\n");
165
- if (!line)
166
- continue;
167
- try {
168
- const message = JSON.parse(line);
169
- if (message.id === undefined || message.id === null)
170
- continue;
171
- const resolve = this.pending.get(message.id);
172
- if (resolve) {
173
- this.pending.delete(message.id);
174
- resolve(message);
175
- }
176
- }
177
- catch {
178
- // A server that prints non-JSON to stdout is not fatal; skip the line.
179
- }
180
- }
181
- }
182
- async send(body, signal) {
183
- const child = this.ensureStarted();
184
- const id = body.id;
185
- child.stdin.write(`${JSON.stringify(body)}\n`);
186
- // Notifications carry no id and get no reply — waiting would always time out.
187
- if (id === undefined || id === null)
188
- return undefined;
189
- return await new Promise((resolve, reject) => {
190
- const timer = setTimeout(() => {
191
- this.pending.delete(id);
192
- reject(new McpClientError(`MCP server "${this.command}" timed out`, "timeout"));
193
- }, REQUEST_TIMEOUT_MS);
194
- const onAbort = () => {
195
- this.pending.delete(id);
196
- clearTimeout(timer);
197
- reject(new McpClientError(`MCP call to "${this.command}" aborted`, "timeout"));
198
- };
199
- signal?.addEventListener("abort", onAbort, { once: true });
200
- this.pending.set(id, (response) => {
201
- clearTimeout(timer);
202
- signal?.removeEventListener("abort", onAbort);
203
- resolve(response);
204
- });
205
- });
206
- }
207
- close() {
208
- this.child?.kill();
209
- this.child = undefined;
210
- this.pending.clear();
211
- }
212
- }
213
- // ---------------------------------------------------------------- client
214
- export class McpClient {
215
- nextId = 1;
216
- initializing;
217
- serverName;
218
- transport;
219
- constructor(serverName, transport) {
220
- this.serverName = serverName;
221
- this.transport = transport;
222
- }
223
- ensureInitialized(signal) {
224
- this.initializing ??= this.initialize(signal).catch((error) => {
225
- // Clear the memo so a later call can retry rather than replaying the failure.
226
- this.initializing = undefined;
227
- throw error;
228
- });
229
- return this.initializing;
230
- }
231
- async initialize(signal) {
232
- await this.request("initialize", {
233
- protocolVersion: MCP_PROTOCOL_VERSION,
234
- capabilities: {},
235
- clientInfo: { name: "nexus-cli", version: "0.1.0" },
236
- }, signal);
237
- await this.transport.send({ jsonrpc: "2.0", method: "notifications/initialized" });
238
- }
239
- /** Tools this server offers. Paginates until the server stops sending a cursor. */
240
- async listTools(signal) {
241
- await this.ensureInitialized(signal);
242
- const tools = [];
243
- let cursor;
244
- do {
245
- const result = (await this.request("tools/list", cursor ? { cursor } : {}, signal));
246
- for (const tool of result?.tools ?? []) {
247
- if (tool?.name)
248
- tools.push(tool);
249
- }
250
- const next = result?.nextCursor;
251
- // A server that echoes the same cursor would loop forever.
252
- cursor = next && next !== cursor ? next : undefined;
253
- } while (cursor);
254
- return tools;
255
- }
256
- async callTool(name, args, signal) {
257
- await this.ensureInitialized(signal);
258
- const result = await this.request("tools/call", { name, arguments: args }, signal);
259
- return (result ?? {});
260
- }
261
- async request(method, params, signal) {
262
- const id = this.nextId++;
263
- const response = await this.transport.send({ jsonrpc: "2.0", id, method, params }, signal);
264
- if (!response)
265
- throw new McpClientError(`empty response for ${method}`, "protocol");
266
- if (response.error) {
267
- throw new McpClientError(`${this.serverName} ${method} failed: ${response.error.message}`, "protocol");
268
- }
269
- return response.result;
270
- }
271
- close() {
272
- this.transport.close();
273
- }
274
- }
275
- //# sourceMappingURL=client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/extensions/mcp/client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { spawn } from \"node:child_process\";\n\n/**\n * Minimal MCP client covering the subset needed to borrow another server's\n * tools: initialize, notifications/initialized, tools/list, tools/call.\n *\n * Two transports, because real servers split roughly evenly between them:\n * stdio for anything you launch locally (filesystem, github, postgres) and\n * Streamable HTTP for anything hosted. Both speak the same JSON-RPC, so only\n * the framing differs.\n *\n * The gitnexus client is deliberately left alone: it is pinned to one server,\n * carries hub-specific auth headers, and its error messages name the hub.\n */\n\nexport const MCP_PROTOCOL_VERSION = \"2025-06-18\";\nconst REQUEST_TIMEOUT_MS = 60_000;\n\nexport interface McpToolSchema {\n\tname: string;\n\tdescription?: string;\n\tinputSchema?: Record<string, unknown>;\n}\n\nexport interface McpToolResult {\n\tcontent?: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tisError?: boolean;\n}\n\ninterface JsonRpcResponse {\n\tid?: number | string | null;\n\tresult?: unknown;\n\terror?: { code?: number; message?: string };\n}\n\nexport class McpClientError extends Error {\n\treadonly kind: \"spawn\" | \"network\" | \"protocol\" | \"timeout\";\n\n\tconstructor(message: string, kind: McpClientError[\"kind\"]) {\n\t\tsuper(message);\n\t\tthis.name = \"McpClientError\";\n\t\tthis.kind = kind;\n\t}\n}\n\n/** A transport carries one JSON-RPC message and returns the matching response. */\nexport interface McpTransport {\n\tsend(body: unknown, signal?: AbortSignal): Promise<JsonRpcResponse | undefined>;\n\tclose(): void;\n}\n\n// ---------------------------------------------------------------- HTTP\n\nexport class HttpTransport implements McpTransport {\n\tprivate sessionId: string | undefined;\n\tprivate readonly url: string;\n\tprivate readonly headers: Record<string, string>;\n\n\tconstructor(url: string, headers: Record<string, string> = {}) {\n\t\tthis.url = url;\n\t\tthis.headers = headers;\n\t}\n\n\tasync send(body: unknown, signal?: AbortSignal): Promise<JsonRpcResponse | undefined> {\n\t\tconst controller = new AbortController();\n\t\tconst timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);\n\t\tconst onAbort = () => controller.abort();\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\ttry {\n\t\t\tlet response: Response;\n\t\t\ttry {\n\t\t\t\tresponse = await fetch(this.url, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t\taccept: \"application/json, text/event-stream\",\n\t\t\t\t\t\t...this.headers,\n\t\t\t\t\t\t...(this.sessionId ? { \"mcp-session-id\": this.sessionId } : {}),\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tthrow new McpClientError(\n\t\t\t\t\t`cannot reach MCP server ${this.url}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t\t\"network\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst newSession = response.headers.get(\"mcp-session-id\");\n\t\t\tif (newSession) this.sessionId = newSession;\n\n\t\t\t// An unconsumed body pins its socket, and under a connection-capped\n\t\t\t// dispatcher that deadlocks every later call to the same server.\n\t\t\tif (response.status === 202) {\n\t\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (!response.ok) {\n\t\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\t\tthrow new McpClientError(`MCP server ${this.url} returned HTTP ${response.status}`, \"protocol\");\n\t\t\t}\n\n\t\t\tconst text = await response.text();\n\t\t\tif (!text) return undefined;\n\t\t\tif ((response.headers.get(\"content-type\") ?? \"\").includes(\"text/event-stream\")) {\n\t\t\t\treturn parseSse(text);\n\t\t\t}\n\t\t\treturn JSON.parse(text) as JsonRpcResponse;\n\t\t} finally {\n\t\t\tclearTimeout(timeout);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t}\n\t}\n\n\tclose(): void {\n\t\t// Nothing to tear down: each call is its own request.\n\t}\n}\n\n/** Pull the last id-bearing JSON-RPC message out of an SSE body. */\nexport function parseSse(sse: string): JsonRpcResponse | undefined {\n\tlet last: JsonRpcResponse | undefined;\n\tfor (const rawEvent of sse.split(\"\\n\\n\")) {\n\t\tconst data = rawEvent\n\t\t\t.split(\"\\n\")\n\t\t\t.filter((line) => line.startsWith(\"data:\"))\n\t\t\t.map((line) => line.slice(5).trim())\n\t\t\t.join(\"\\n\");\n\t\tif (!data) continue;\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(data) as JsonRpcResponse;\n\t\t\t// Responses carry an id; server-initiated notifications do not.\n\t\t\tif (parsed.id !== undefined && parsed.id !== null) last = parsed;\n\t\t} catch {\n\t\t\t// Ignore non-JSON keepalives.\n\t\t}\n\t}\n\treturn last;\n}\n\n// ---------------------------------------------------------------- stdio\n\nexport class StdioTransport implements McpTransport {\n\tprivate child: ChildProcessWithoutNullStreams | undefined;\n\tprivate buffer = \"\";\n\tprivate readonly pending = new Map<number | string, (response: JsonRpcResponse) => void>();\n\tprivate startError: string | undefined;\n\n\tprivate readonly command: string;\n\tprivate readonly args: string[];\n\tprivate readonly env: Record<string, string>;\n\tprivate readonly cwd: string | undefined;\n\n\tconstructor(command: string, args: string[] = [], env: Record<string, string> = {}, cwd?: string) {\n\t\tthis.command = command;\n\t\tthis.args = args;\n\t\tthis.env = env;\n\t\tthis.cwd = cwd;\n\t}\n\n\tprivate ensureStarted(): ChildProcessWithoutNullStreams {\n\t\tif (this.startError) throw new McpClientError(this.startError, \"spawn\");\n\t\tif (this.child) return this.child;\n\t\tlet child: ChildProcessWithoutNullStreams;\n\t\ttry {\n\t\t\tchild = spawn(this.command, this.args, {\n\t\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t\t\tenv: { ...process.env, ...this.env },\n\t\t\t\tcwd: this.cwd,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.startError = `cannot start MCP server \"${this.command}\": ${\n\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t}`;\n\t\t\tthrow new McpClientError(this.startError, \"spawn\");\n\t\t}\n\t\tchild.on(\"error\", (error) => {\n\t\t\t// ENOENT surfaces here, not from spawn(), so record it for the next call.\n\t\t\tthis.startError = `cannot start MCP server \"${this.command}\": ${error.message}`;\n\t\t\tfor (const resolve of this.pending.values()) resolve({ error: { message: this.startError } });\n\t\t\tthis.pending.clear();\n\t\t});\n\t\tchild.stdout.setEncoding(\"utf8\");\n\t\tchild.stdout.on(\"data\", (chunk: string) => this.consume(chunk));\n\t\t// stderr is the server's log stream, not protocol. Draining it keeps a\n\t\t// chatty server from filling its pipe buffer and hanging.\n\t\tchild.stderr.resume();\n\t\tthis.child = child;\n\t\treturn child;\n\t}\n\n\t/** Messages are newline-delimited JSON, so split on newlines and keep the tail. */\n\tprivate consume(chunk: string): void {\n\t\tthis.buffer += chunk;\n\t\tlet newline = this.buffer.indexOf(\"\\n\");\n\t\twhile (newline >= 0) {\n\t\t\tconst line = this.buffer.slice(0, newline).trim();\n\t\t\tthis.buffer = this.buffer.slice(newline + 1);\n\t\t\tnewline = this.buffer.indexOf(\"\\n\");\n\t\t\tif (!line) continue;\n\t\t\ttry {\n\t\t\t\tconst message = JSON.parse(line) as JsonRpcResponse;\n\t\t\t\tif (message.id === undefined || message.id === null) continue;\n\t\t\t\tconst resolve = this.pending.get(message.id);\n\t\t\t\tif (resolve) {\n\t\t\t\t\tthis.pending.delete(message.id);\n\t\t\t\t\tresolve(message);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// A server that prints non-JSON to stdout is not fatal; skip the line.\n\t\t\t}\n\t\t}\n\t}\n\n\tasync send(body: unknown, signal?: AbortSignal): Promise<JsonRpcResponse | undefined> {\n\t\tconst child = this.ensureStarted();\n\t\tconst id = (body as { id?: number | string }).id;\n\t\tchild.stdin.write(`${JSON.stringify(body)}\\n`);\n\t\t// Notifications carry no id and get no reply — waiting would always time out.\n\t\tif (id === undefined || id === null) return undefined;\n\n\t\treturn await new Promise<JsonRpcResponse>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.pending.delete(id);\n\t\t\t\treject(new McpClientError(`MCP server \"${this.command}\" timed out`, \"timeout\"));\n\t\t\t}, REQUEST_TIMEOUT_MS);\n\t\t\tconst onAbort = () => {\n\t\t\t\tthis.pending.delete(id);\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(new McpClientError(`MCP call to \"${this.command}\" aborted`, \"timeout\"));\n\t\t\t};\n\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\tthis.pending.set(id, (response) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve(response);\n\t\t\t});\n\t\t});\n\t}\n\n\tclose(): void {\n\t\tthis.child?.kill();\n\t\tthis.child = undefined;\n\t\tthis.pending.clear();\n\t}\n}\n\n// ---------------------------------------------------------------- client\n\nexport class McpClient {\n\tprivate nextId = 1;\n\tprivate initializing: Promise<void> | undefined;\n\n\treadonly serverName: string;\n\tprivate readonly transport: McpTransport;\n\n\tconstructor(serverName: string, transport: McpTransport) {\n\t\tthis.serverName = serverName;\n\t\tthis.transport = transport;\n\t}\n\n\tprivate ensureInitialized(signal?: AbortSignal): Promise<void> {\n\t\tthis.initializing ??= this.initialize(signal).catch((error) => {\n\t\t\t// Clear the memo so a later call can retry rather than replaying the failure.\n\t\t\tthis.initializing = undefined;\n\t\t\tthrow error;\n\t\t});\n\t\treturn this.initializing;\n\t}\n\n\tprivate async initialize(signal?: AbortSignal): Promise<void> {\n\t\tawait this.request(\n\t\t\t\"initialize\",\n\t\t\t{\n\t\t\t\tprotocolVersion: MCP_PROTOCOL_VERSION,\n\t\t\t\tcapabilities: {},\n\t\t\t\tclientInfo: { name: \"nexus-cli\", version: \"0.1.0\" },\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tawait this.transport.send({ jsonrpc: \"2.0\", method: \"notifications/initialized\" });\n\t}\n\n\t/** Tools this server offers. Paginates until the server stops sending a cursor. */\n\tasync listTools(signal?: AbortSignal): Promise<McpToolSchema[]> {\n\t\tawait this.ensureInitialized(signal);\n\t\tconst tools: McpToolSchema[] = [];\n\t\tlet cursor: string | undefined;\n\t\tdo {\n\t\t\tconst result = (await this.request(\"tools/list\", cursor ? { cursor } : {}, signal)) as\n\t\t\t\t| { tools?: McpToolSchema[]; nextCursor?: string }\n\t\t\t\t| undefined;\n\t\t\tfor (const tool of result?.tools ?? []) {\n\t\t\t\tif (tool?.name) tools.push(tool);\n\t\t\t}\n\t\t\tconst next = result?.nextCursor;\n\t\t\t// A server that echoes the same cursor would loop forever.\n\t\t\tcursor = next && next !== cursor ? next : undefined;\n\t\t} while (cursor);\n\t\treturn tools;\n\t}\n\n\tasync callTool(name: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<McpToolResult> {\n\t\tawait this.ensureInitialized(signal);\n\t\tconst result = await this.request(\"tools/call\", { name, arguments: args }, signal);\n\t\treturn (result ?? {}) as McpToolResult;\n\t}\n\n\tprivate async request(method: string, params: unknown, signal?: AbortSignal): Promise<unknown> {\n\t\tconst id = this.nextId++;\n\t\tconst response = await this.transport.send({ jsonrpc: \"2.0\", id, method, params }, signal);\n\t\tif (!response) throw new McpClientError(`empty response for ${method}`, \"protocol\");\n\t\tif (response.error) {\n\t\t\tthrow new McpClientError(`${this.serverName} ${method} failed: ${response.error.message}`, \"protocol\");\n\t\t}\n\t\treturn response.result;\n\t}\n\n\tclose(): void {\n\t\tthis.transport.close();\n\t}\n}\n"]}