@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
@@ -0,0 +1,973 @@
1
+ import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
+ import { copyToClipboard } from "../../utils/clipboard.js";
3
+ import { isServerCacheValid } from "./metadata-cache.js";
4
+ import { createPanelKeys } from "./panel-keys.js";
5
+ import { resourceNameToToolName } from "./resource-tools.js";
6
+ import { getToolNameCandidates, isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.js";
7
+ import { isUiToolVisibleToModel } from "./ui-tool-visibility.js";
8
+ import { sanitizeTerminalText, stripOscSequences } from "./utils.js";
9
+ const DEFAULT_THEME = {
10
+ border: "2",
11
+ title: "2",
12
+ selected: "36",
13
+ direct: "32",
14
+ needsAuth: "33",
15
+ placeholder: "2;3",
16
+ description: "2",
17
+ hint: "2",
18
+ confirm: "32",
19
+ cancel: "31",
20
+ };
21
+ function fg(code, text) {
22
+ if (!code)
23
+ return text;
24
+ return `\x1b[${code}m${text}\x1b[0m`;
25
+ }
26
+ const RAINBOW_COLORS = [
27
+ "38;2;178;129;214",
28
+ "38;2;215;135;175",
29
+ "38;2;254;188;56",
30
+ "38;2;228;192;15",
31
+ "38;2;137;210;129",
32
+ "38;2;0;175;175",
33
+ "38;2;23;143;185",
34
+ ];
35
+ function rainbowProgress(filled, total) {
36
+ const dots = [];
37
+ for (let i = 0; i < total; i++) {
38
+ const color = RAINBOW_COLORS[i % RAINBOW_COLORS.length];
39
+ if (!color)
40
+ continue;
41
+ dots.push(fg(color, i < filled ? "●" : "○"));
42
+ }
43
+ return dots.join(" ");
44
+ }
45
+ function fuzzyScore(query, text) {
46
+ const lq = query.toLowerCase();
47
+ const lt = text.toLowerCase();
48
+ if (lt.includes(lq))
49
+ return 100 + (lq.length / lt.length) * 50;
50
+ let score = 0;
51
+ let qi = 0;
52
+ let consecutive = 0;
53
+ for (let i = 0; i < lt.length && qi < lq.length; i++) {
54
+ if (lt[i] === lq[qi]) {
55
+ score += 10 + consecutive;
56
+ consecutive += 5;
57
+ qi++;
58
+ }
59
+ else {
60
+ consecutive = 0;
61
+ }
62
+ }
63
+ return qi === lq.length ? score : 0;
64
+ }
65
+ function sanitizeDisplayText(text) {
66
+ return sanitizeTerminalText(text ?? "");
67
+ }
68
+ function sanitizeRowContent(content) {
69
+ const withoutOsc = stripOscSequences(content);
70
+ let result = "";
71
+ let pendingSpace = false;
72
+ for (let i = 0; i < withoutOsc.length; i++) {
73
+ const rest = withoutOsc.slice(i);
74
+ const ansi = rest.match(/^(?:\x1b\[[0-?]*[ -/]*[@-~]|\x1b[@-Z\\-_])/);
75
+ if (ansi) {
76
+ result += ansi[0];
77
+ i += ansi[0].length - 1;
78
+ continue;
79
+ }
80
+ const code = withoutOsc.charCodeAt(i);
81
+ if (code <= 0x1f || code === 0x7f || (code >= 0x80 && code <= 0x9f)) {
82
+ pendingSpace = true;
83
+ continue;
84
+ }
85
+ if (pendingSpace && result && !result.endsWith(" ")) {
86
+ result += " ";
87
+ }
88
+ pendingSpace = false;
89
+ result += withoutOsc[i];
90
+ }
91
+ return result;
92
+ }
93
+ function estimateTokens(tool) {
94
+ const schemaLen = JSON.stringify(tool.inputSchema ?? {}).length;
95
+ const descLen = tool.description?.length ?? 0;
96
+ return Math.ceil((tool.name.length + descLen + schemaLen) / 4) + 10;
97
+ }
98
+ class McpPanel {
99
+ noticeLines;
100
+ prefix;
101
+ servers = [];
102
+ cursorIndex = 0;
103
+ nameQuery = "";
104
+ descSearchActive = false;
105
+ descQuery = "";
106
+ dirty = false;
107
+ confirmingDiscard = false;
108
+ discardSelected = 1;
109
+ importNotice = null;
110
+ authNotice = null;
111
+ authInFlight = null;
112
+ inactivityTimeout = null;
113
+ visibleItems = [];
114
+ tui;
115
+ t = DEFAULT_THEME;
116
+ authOnly;
117
+ keys;
118
+ config;
119
+ cache;
120
+ callbacks;
121
+ done;
122
+ static MAX_VISIBLE = 12;
123
+ static INACTIVITY_MS = 60_000;
124
+ constructor(config, cache, provenance, callbacks, tui, done, options = {}) {
125
+ this.config = config;
126
+ this.cache = cache;
127
+ this.callbacks = callbacks;
128
+ this.done = done;
129
+ this.tui = tui;
130
+ this.noticeLines = options.noticeLines ?? [];
131
+ this.authOnly = options.authOnly === true;
132
+ this.keys = createPanelKeys(options.keybindings);
133
+ this.prefix = config.settings?.toolPrefix ?? "server";
134
+ for (const [serverName, definition] of Object.entries(config.mcpServers)) {
135
+ if (this.authOnly && !callbacks.canAuthenticate(serverName))
136
+ continue;
137
+ const prov = provenance.get(serverName);
138
+ const cachedEntry = this.cache?.servers?.[serverName];
139
+ const serverCache = cachedEntry && isServerCacheValid(cachedEntry, definition) ? cachedEntry : undefined;
140
+ const globalDirect = config.settings?.directTools;
141
+ let toolFilter = false;
142
+ if (definition.directTools !== undefined) {
143
+ toolFilter = definition.directTools;
144
+ }
145
+ else if (globalDirect) {
146
+ toolFilter = globalDirect;
147
+ }
148
+ const tools = [];
149
+ if (serverCache && !this.authOnly && !isServerDisabled(definition)) {
150
+ for (const tool of serverCache.tools ?? []) {
151
+ if (!isUiToolVisibleToModel(tool.uiVisibility))
152
+ continue;
153
+ if (!isToolAllowed(tool.name, serverName, this.prefix, definition.includeTools, definition.excludeTools, this.getOtherCurrentCandidates(serverName, definition, serverCache, tool.name))) {
154
+ continue;
155
+ }
156
+ const isDirect = toolFilter === true || (Array.isArray(toolFilter) && toolFilter.includes(tool.name));
157
+ tools.push({
158
+ name: tool.name,
159
+ description: tool.description ?? "",
160
+ isDirect,
161
+ wasDirect: isDirect,
162
+ estimatedTokens: estimateTokens(tool),
163
+ });
164
+ }
165
+ if (definition.exposeResources !== false) {
166
+ for (const resource of serverCache.resources ?? []) {
167
+ const baseName = `read_${resourceNameToToolName(resource.name)}`;
168
+ if (!isToolAllowed(baseName, serverName, this.prefix, definition.includeTools, definition.excludeTools, this.getOtherCurrentCandidates(serverName, definition, serverCache, baseName))) {
169
+ continue;
170
+ }
171
+ const isDirect = toolFilter === true || (Array.isArray(toolFilter) && toolFilter.includes(baseName));
172
+ const ct = {
173
+ name: baseName,
174
+ ...(resource.description !== undefined ? { description: resource.description } : {}),
175
+ };
176
+ tools.push({
177
+ name: baseName,
178
+ description: resource.description ?? `Read resource: ${resource.uri}`,
179
+ isDirect,
180
+ wasDirect: isDirect,
181
+ estimatedTokens: estimateTokens(ct),
182
+ });
183
+ }
184
+ }
185
+ }
186
+ const status = callbacks.getConnectionStatus(serverName);
187
+ const failureMessage = callbacks.getFailureMessage?.(serverName) ?? null;
188
+ let directCount = 0;
189
+ let directTokens = 0;
190
+ for (const tool of tools) {
191
+ if (!tool.isDirect)
192
+ continue;
193
+ directCount++;
194
+ directTokens += tool.estimatedTokens;
195
+ }
196
+ this.servers.push({
197
+ name: serverName,
198
+ expanded: false,
199
+ source: prov?.kind ?? "user",
200
+ ...(prov?.importKind !== undefined ? { importKind: prov.importKind } : {}),
201
+ ...(definition.includeTools !== undefined ? { includeTools: definition.includeTools } : {}),
202
+ ...(definition.excludeTools !== undefined ? { excludeTools: definition.excludeTools } : {}),
203
+ exposeResources: definition.exposeResources !== false,
204
+ connectionStatus: status,
205
+ failureMessage,
206
+ tools,
207
+ directCount,
208
+ directTokens,
209
+ hasCachedData: !!serverCache,
210
+ });
211
+ }
212
+ this.rebuildVisibleItems();
213
+ this.resetInactivityTimeout();
214
+ }
215
+ resetInactivityTimeout() {
216
+ if (this.inactivityTimeout)
217
+ clearTimeout(this.inactivityTimeout);
218
+ this.inactivityTimeout = setTimeout(() => {
219
+ this.cleanup();
220
+ this.done({ cancelled: true, changes: new Map() });
221
+ }, McpPanel.INACTIVITY_MS);
222
+ }
223
+ cleanup() {
224
+ if (this.inactivityTimeout) {
225
+ clearTimeout(this.inactivityTimeout);
226
+ this.inactivityTimeout = null;
227
+ }
228
+ }
229
+ rebuildVisibleItems() {
230
+ const query = this.descSearchActive ? this.descQuery : this.nameQuery;
231
+ const mode = this.descSearchActive ? "desc" : "name";
232
+ this.visibleItems = [];
233
+ for (let si = 0; si < this.servers.length; si++) {
234
+ const server = this.servers[si];
235
+ if (!server)
236
+ continue;
237
+ if (query && this.authOnly) {
238
+ const score = mode === "name" ? fuzzyScore(query, server.name) : 0;
239
+ if (score > 0) {
240
+ this.visibleItems.push({ type: "server", serverIndex: si });
241
+ }
242
+ continue;
243
+ }
244
+ this.visibleItems.push({ type: "server", serverIndex: si });
245
+ if (server.expanded || query) {
246
+ for (let ti = 0; ti < server.tools.length; ti++) {
247
+ const tool = server.tools[ti];
248
+ if (!tool)
249
+ continue;
250
+ if (query) {
251
+ const score = mode === "name"
252
+ ? Math.max(fuzzyScore(query, tool.name), fuzzyScore(query, server.name) * 0.6)
253
+ : fuzzyScore(query, tool.description);
254
+ if (score === 0)
255
+ continue;
256
+ }
257
+ this.visibleItems.push({ type: "tool", serverIndex: si, toolIndex: ti });
258
+ }
259
+ }
260
+ }
261
+ if (query && !this.authOnly) {
262
+ this.visibleItems = this.visibleItems.filter((item) => {
263
+ if (item.type === "server") {
264
+ return this.visibleItems.some((other) => other.type === "tool" && other.serverIndex === item.serverIndex);
265
+ }
266
+ return true;
267
+ });
268
+ }
269
+ }
270
+ updateDirty() {
271
+ this.dirty = this.servers.some((s) => s.tools.some((t) => t.isDirect !== t.wasDirect));
272
+ }
273
+ buildResult() {
274
+ const changes = new Map();
275
+ for (const server of this.servers) {
276
+ const changed = server.tools.some((t) => t.isDirect !== t.wasDirect);
277
+ if (!changed)
278
+ continue;
279
+ const directTools = server.tools.filter((t) => t.isDirect);
280
+ if (directTools.length === server.tools.length && server.tools.length > 0) {
281
+ changes.set(server.name, true);
282
+ }
283
+ else if (directTools.length === 0) {
284
+ changes.set(server.name, false);
285
+ }
286
+ else {
287
+ changes.set(server.name, directTools.map((t) => t.name));
288
+ }
289
+ }
290
+ return { changes, cancelled: false };
291
+ }
292
+ handleInput(data) {
293
+ this.resetInactivityTimeout();
294
+ this.importNotice = null;
295
+ if (!this.authInFlight)
296
+ this.authNotice = null;
297
+ if (this.confirmingDiscard) {
298
+ this.handleDiscardInput(data);
299
+ return;
300
+ }
301
+ // Global shortcuts — always work, even during desc search
302
+ if (matchesKey(data, "ctrl+c")) {
303
+ this.cleanup();
304
+ this.done({ cancelled: true, changes: new Map() });
305
+ return;
306
+ }
307
+ if (this.keys.save(data)) {
308
+ this.cleanup();
309
+ this.done(this.buildResult());
310
+ return;
311
+ }
312
+ // Modal description search mode
313
+ if (this.descSearchActive) {
314
+ if (matchesKey(data, "escape") || this.keys.selectConfirm(data)) {
315
+ this.descSearchActive = false;
316
+ this.descQuery = "";
317
+ this.rebuildVisibleItems();
318
+ this.cursorIndex = Math.min(this.cursorIndex, Math.max(0, this.visibleItems.length - 1));
319
+ return;
320
+ }
321
+ if (matchesKey(data, "backspace")) {
322
+ if (this.descQuery.length > 0) {
323
+ this.descQuery = this.descQuery.slice(0, -1);
324
+ this.rebuildVisibleItems();
325
+ this.cursorIndex = Math.min(this.cursorIndex, Math.max(0, this.visibleItems.length - 1));
326
+ }
327
+ return;
328
+ }
329
+ if (this.keys.selectUp(data)) {
330
+ this.moveCursor(-1);
331
+ return;
332
+ }
333
+ if (this.keys.selectDown(data)) {
334
+ this.moveCursor(1);
335
+ return;
336
+ }
337
+ if (matchesKey(data, "space")) {
338
+ // Toggle even while in desc search
339
+ const item = this.visibleItems[this.cursorIndex];
340
+ if (item)
341
+ this.toggleItem(item);
342
+ return;
343
+ }
344
+ if (data.length === 1 && data.charCodeAt(0) >= 32) {
345
+ this.descQuery += data;
346
+ this.rebuildVisibleItems();
347
+ this.cursorIndex = Math.min(this.cursorIndex, Math.max(0, this.visibleItems.length - 1));
348
+ return;
349
+ }
350
+ return;
351
+ }
352
+ if (matchesKey(data, "escape")) {
353
+ if (this.nameQuery) {
354
+ this.nameQuery = "";
355
+ this.rebuildVisibleItems();
356
+ this.cursorIndex = Math.min(this.cursorIndex, Math.max(0, this.visibleItems.length - 1));
357
+ return;
358
+ }
359
+ if (this.dirty) {
360
+ this.confirmingDiscard = true;
361
+ this.discardSelected = 1;
362
+ return;
363
+ }
364
+ this.cleanup();
365
+ this.done({ cancelled: true, changes: new Map() });
366
+ return;
367
+ }
368
+ if (this.keys.selectUp(data)) {
369
+ this.moveCursor(-1);
370
+ return;
371
+ }
372
+ if (this.keys.selectDown(data)) {
373
+ this.moveCursor(1);
374
+ return;
375
+ }
376
+ if (matchesKey(data, "space")) {
377
+ const item = this.visibleItems[this.cursorIndex];
378
+ if (item && !this.authOnly)
379
+ this.toggleItem(item);
380
+ return;
381
+ }
382
+ if (this.keys.selectConfirm(data)) {
383
+ const item = this.visibleItems[this.cursorIndex];
384
+ if (!item)
385
+ return;
386
+ const server = this.servers[item.serverIndex];
387
+ if (!server)
388
+ return;
389
+ if (item.type === "server") {
390
+ if (server.connectionStatus === "disabled")
391
+ return;
392
+ if (this.authOnly || server.connectionStatus === "needs-auth") {
393
+ this.authenticateServer(server);
394
+ return;
395
+ }
396
+ server.expanded = !server.expanded;
397
+ this.rebuildVisibleItems();
398
+ this.cursorIndex = Math.min(this.cursorIndex, Math.max(0, this.visibleItems.length - 1));
399
+ }
400
+ else if (item.toolIndex !== undefined) {
401
+ const tool = server.tools[item.toolIndex];
402
+ if (!tool)
403
+ return;
404
+ this.toggleToolDirect(server, tool);
405
+ if (tool.isDirect && server.source === "import") {
406
+ this.importNotice = `Imported from ${sanitizeDisplayText(server.importKind ?? "external")} — will copy to user config on save`;
407
+ }
408
+ this.updateDirty();
409
+ }
410
+ return;
411
+ }
412
+ if (matchesKey(data, "ctrl+a")) {
413
+ const item = this.visibleItems[this.cursorIndex];
414
+ if (item)
415
+ this.authenticateSelectedServer(item);
416
+ return;
417
+ }
418
+ if (matchesKey(data, "ctrl+r")) {
419
+ const item = this.visibleItems[this.cursorIndex];
420
+ if (!item)
421
+ return;
422
+ const server = this.servers[item.serverIndex];
423
+ if (server)
424
+ this.reconnectServer(server);
425
+ return;
426
+ }
427
+ if (matchesKey(data, "ctrl+y")) {
428
+ const item = this.visibleItems[this.cursorIndex];
429
+ if (!item)
430
+ return;
431
+ const server = this.servers[item.serverIndex];
432
+ if (!server || server.connectionStatus !== "failed" || !server.failureMessage)
433
+ return;
434
+ const serverName = sanitizeDisplayText(server.name);
435
+ const failureMessage = sanitizeDisplayText(server.failureMessage);
436
+ copyToClipboard(failureMessage)
437
+ .then(() => {
438
+ this.authNotice = `Copied error for ${serverName} to clipboard`;
439
+ this.tui.requestRender();
440
+ })
441
+ .catch((error) => {
442
+ const message = sanitizeDisplayText(error instanceof Error ? error.message : String(error));
443
+ this.authNotice = `Failed to copy error for ${serverName}: ${message}`;
444
+ this.tui.requestRender();
445
+ });
446
+ return;
447
+ }
448
+ if (data === "?") {
449
+ if (this.authOnly)
450
+ return;
451
+ this.descSearchActive = true;
452
+ this.descQuery = "";
453
+ this.rebuildVisibleItems();
454
+ this.cursorIndex = Math.min(this.cursorIndex, Math.max(0, this.visibleItems.length - 1));
455
+ return;
456
+ }
457
+ // Backspace removes from name query
458
+ if (matchesKey(data, "backspace")) {
459
+ if (this.nameQuery.length > 0) {
460
+ this.nameQuery = this.nameQuery.slice(0, -1);
461
+ this.rebuildVisibleItems();
462
+ this.cursorIndex = Math.min(this.cursorIndex, Math.max(0, this.visibleItems.length - 1));
463
+ }
464
+ return;
465
+ }
466
+ // All other printable chars → always-on name search
467
+ if (data.length === 1 && data.charCodeAt(0) >= 32) {
468
+ this.nameQuery += data;
469
+ this.rebuildVisibleItems();
470
+ this.cursorIndex = Math.min(this.cursorIndex, Math.max(0, this.visibleItems.length - 1));
471
+ return;
472
+ }
473
+ }
474
+ authenticateSelectedServer(item) {
475
+ const server = this.servers[item.serverIndex];
476
+ if (server)
477
+ this.authenticateServer(server);
478
+ }
479
+ authenticateServer(server) {
480
+ if (this.authInFlight)
481
+ return;
482
+ if (server.connectionStatus === "connecting" || server.connectionStatus === "disabled")
483
+ return;
484
+ const serverName = sanitizeDisplayText(server.name);
485
+ if (!this.callbacks.canAuthenticate(server.name)) {
486
+ this.authNotice = `${serverName} does not use OAuth authentication.`;
487
+ return;
488
+ }
489
+ this.authInFlight = server.name;
490
+ this.authNotice = `Authenticating ${serverName}...`;
491
+ this.tui.requestRender();
492
+ this.callbacks
493
+ .authenticate(server.name)
494
+ .then((result) => {
495
+ server.connectionStatus = this.callbacks.getConnectionStatus(server.name);
496
+ if (result.ok) {
497
+ this.authNotice = `OAuth finished for ${serverName}. Reconnecting...`;
498
+ this.authInFlight = null;
499
+ this.tui.requestRender();
500
+ this.reconnectServer(server, { afterAuth: true });
501
+ return;
502
+ }
503
+ const message = sanitizeDisplayText(result.message);
504
+ this.authNotice = `OAuth failed for ${serverName}${message ? `: ${message}` : ". Check the notification for details."}`;
505
+ this.authInFlight = null;
506
+ this.tui.requestRender();
507
+ })
508
+ .catch((error) => {
509
+ const message = sanitizeDisplayText(error instanceof Error ? error.message : String(error));
510
+ server.connectionStatus = this.callbacks.getConnectionStatus(server.name);
511
+ this.authNotice = `OAuth failed for ${serverName}: ${message}`;
512
+ this.authInFlight = null;
513
+ this.tui.requestRender();
514
+ });
515
+ }
516
+ reconnectServer(server, options = {}) {
517
+ if (server.connectionStatus === "connecting" || server.connectionStatus === "disabled")
518
+ return;
519
+ const serverName = sanitizeDisplayText(server.name);
520
+ server.connectionStatus = "connecting";
521
+ this.tui.requestRender();
522
+ this.callbacks
523
+ .reconnect(server.name)
524
+ .then((connected) => {
525
+ server.connectionStatus = this.callbacks.getConnectionStatus(server.name);
526
+ server.failureMessage = this.callbacks.getFailureMessage?.(server.name) ?? null;
527
+ if (server.connectionStatus === "connected") {
528
+ const entry = this.callbacks.refreshCacheAfterReconnect(server.name);
529
+ if (entry) {
530
+ this.cache ??= { version: 1, servers: {} };
531
+ this.cache.servers[server.name] = entry;
532
+ this.rebuildServerTools(server, entry);
533
+ }
534
+ server.hasCachedData = true;
535
+ }
536
+ if (options.afterAuth) {
537
+ this.authNotice =
538
+ connected && server.connectionStatus === "connected"
539
+ ? `OAuth finished for ${serverName}. Reconnected.`
540
+ : `OAuth finished for ${serverName}, but reconnect did not complete. Press ctrl+r to retry.`;
541
+ }
542
+ this.tui.requestRender();
543
+ })
544
+ .catch((error) => {
545
+ server.connectionStatus = "failed";
546
+ const message = sanitizeDisplayText(error instanceof Error ? error.message : String(error));
547
+ this.authNotice = `Reconnect failed for ${serverName}: ${message}`;
548
+ this.tui.requestRender();
549
+ });
550
+ }
551
+ toggleItem(item) {
552
+ if (this.authOnly)
553
+ return;
554
+ const server = this.servers[item.serverIndex];
555
+ if (!server)
556
+ return;
557
+ if (item.type === "server") {
558
+ const newState = !server.tools.every((t) => t.isDirect);
559
+ if (server.source === "import" && newState) {
560
+ this.importNotice = `Imported from ${sanitizeDisplayText(server.importKind ?? "external")} — will copy to user config on save`;
561
+ }
562
+ let directTokens = 0;
563
+ for (const tool of server.tools) {
564
+ tool.isDirect = newState;
565
+ if (newState)
566
+ directTokens += tool.estimatedTokens;
567
+ }
568
+ server.directCount = newState ? server.tools.length : 0;
569
+ server.directTokens = directTokens;
570
+ }
571
+ else if (item.toolIndex !== undefined) {
572
+ const tool = server.tools[item.toolIndex];
573
+ if (!tool)
574
+ return;
575
+ this.toggleToolDirect(server, tool);
576
+ if (tool.isDirect && server.source === "import") {
577
+ this.importNotice = `Imported from ${sanitizeDisplayText(server.importKind ?? "external")} — will copy to user config on save`;
578
+ }
579
+ }
580
+ this.updateDirty();
581
+ }
582
+ toggleToolDirect(server, tool) {
583
+ tool.isDirect = !tool.isDirect;
584
+ server.directCount += tool.isDirect ? 1 : -1;
585
+ server.directTokens += tool.isDirect ? tool.estimatedTokens : -tool.estimatedTokens;
586
+ }
587
+ handleDiscardInput(data) {
588
+ if (matchesKey(data, "ctrl+c")) {
589
+ this.cleanup();
590
+ this.done({ cancelled: true, changes: new Map() });
591
+ return;
592
+ }
593
+ if (matchesKey(data, "escape") || data === "n" || data === "N") {
594
+ this.confirmingDiscard = false;
595
+ return;
596
+ }
597
+ if (this.keys.selectConfirm(data)) {
598
+ this.cleanup();
599
+ if (this.discardSelected === 0) {
600
+ this.done({ cancelled: true, changes: new Map() });
601
+ }
602
+ else {
603
+ this.done(this.buildResult());
604
+ }
605
+ return;
606
+ }
607
+ if (data === "y" || data === "Y") {
608
+ this.cleanup();
609
+ this.done({ cancelled: true, changes: new Map() });
610
+ return;
611
+ }
612
+ if (matchesKey(data, "left") || matchesKey(data, "right") || matchesKey(data, "tab")) {
613
+ this.discardSelected = this.discardSelected === 0 ? 1 : 0;
614
+ }
615
+ }
616
+ moveCursor(delta) {
617
+ if (this.visibleItems.length === 0)
618
+ return;
619
+ this.cursorIndex = Math.max(0, Math.min(this.visibleItems.length - 1, this.cursorIndex + delta));
620
+ }
621
+ getOtherCurrentCandidates(serverName, definition, currentEntry, toolName) {
622
+ const candidates = new Set();
623
+ for (const [otherServerName, otherDefinition] of Object.entries(this.config.mcpServers)) {
624
+ if (isServerDisabled(otherDefinition))
625
+ continue;
626
+ const cachedEntry = this.cache?.servers?.[otherServerName];
627
+ const entry = otherServerName === serverName
628
+ ? currentEntry
629
+ : cachedEntry && isServerCacheValid(cachedEntry, otherDefinition)
630
+ ? cachedEntry
631
+ : undefined;
632
+ if (!entry)
633
+ continue;
634
+ const otherPrefix = resolveToolPrefix(otherDefinition, this.prefix);
635
+ for (const tool of entry.tools ?? []) {
636
+ if (!isUiToolVisibleToModel(tool.uiVisibility))
637
+ continue;
638
+ for (const candidate of getToolNameCandidates(tool.name, otherServerName, otherPrefix, false))
639
+ candidates.add(candidate);
640
+ }
641
+ if (otherDefinition.exposeResources !== false) {
642
+ for (const resource of entry.resources ?? []) {
643
+ const baseName = `read_${resourceNameToToolName(resource.name)}`;
644
+ for (const candidate of getToolNameCandidates(baseName, otherServerName, otherPrefix, false))
645
+ candidates.add(candidate);
646
+ }
647
+ }
648
+ }
649
+ for (const candidate of getToolNameCandidates(toolName, serverName, resolveToolPrefix(definition, this.prefix), false))
650
+ candidates.delete(candidate);
651
+ return candidates;
652
+ }
653
+ rebuildServerTools(server, entry) {
654
+ const existingState = new Map();
655
+ for (const t of server.tools)
656
+ existingState.set(t.name, t.isDirect);
657
+ const newTools = [];
658
+ for (const tool of entry.tools ?? []) {
659
+ if (!isUiToolVisibleToModel(tool.uiVisibility))
660
+ continue;
661
+ if (!isToolAllowed(tool.name, server.name, this.prefix, server.includeTools, server.excludeTools, this.getOtherCurrentCandidates(server.name, server, entry, tool.name))) {
662
+ continue;
663
+ }
664
+ const prev = existingState.get(tool.name);
665
+ const isDirect = prev !== undefined ? prev : false;
666
+ newTools.push({
667
+ name: tool.name,
668
+ description: tool.description ?? "",
669
+ isDirect,
670
+ wasDirect: prev !== undefined ? (server.tools.find((t) => t.name === tool.name)?.wasDirect ?? false) : false,
671
+ estimatedTokens: estimateTokens(tool),
672
+ });
673
+ }
674
+ if (server.exposeResources) {
675
+ for (const resource of entry.resources ?? []) {
676
+ const baseName = `read_${resourceNameToToolName(resource.name)}`;
677
+ if (!isToolAllowed(baseName, server.name, this.prefix, server.includeTools, server.excludeTools, this.getOtherCurrentCandidates(server.name, server, entry, baseName))) {
678
+ continue;
679
+ }
680
+ const prev = existingState.get(baseName);
681
+ const isDirect = prev !== undefined ? prev : false;
682
+ const ct = {
683
+ name: baseName,
684
+ ...(resource.description !== undefined ? { description: resource.description } : {}),
685
+ };
686
+ newTools.push({
687
+ name: baseName,
688
+ description: resource.description ?? `Read resource: ${resource.uri}`,
689
+ isDirect,
690
+ wasDirect: prev !== undefined ? (server.tools.find((t) => t.name === baseName)?.wasDirect ?? false) : false,
691
+ estimatedTokens: estimateTokens(ct),
692
+ });
693
+ }
694
+ }
695
+ server.tools = newTools;
696
+ server.directCount = 0;
697
+ server.directTokens = 0;
698
+ for (const tool of newTools) {
699
+ if (!tool.isDirect)
700
+ continue;
701
+ server.directCount++;
702
+ server.directTokens += tool.estimatedTokens;
703
+ }
704
+ this.rebuildVisibleItems();
705
+ this.updateDirty();
706
+ }
707
+ render(width) {
708
+ const innerW = width - 2;
709
+ const lines = [];
710
+ const t = this.t;
711
+ const bold = (s) => `\x1b[1m${s}\x1b[22m`;
712
+ const italic = (s) => `\x1b[3m${s}\x1b[23m`;
713
+ const inverse = (s) => `\x1b[7m${s}\x1b[27m`;
714
+ const row = (content) => fg(t.border, "│") + truncateToWidth(` ${sanitizeRowContent(content)}`, innerW, "…", true) + fg(t.border, "│");
715
+ const emptyRow = () => fg(t.border, "│") + " ".repeat(innerW) + fg(t.border, "│");
716
+ const divider = () => fg(t.border, `├${"─".repeat(innerW)}┤`);
717
+ const titleText = this.authOnly ? " MCP OAuth " : " MCP Servers ";
718
+ const borderLen = innerW - visibleWidth(titleText);
719
+ const leftB = Math.floor(borderLen / 2);
720
+ const rightB = borderLen - leftB;
721
+ lines.push(fg(t.border, `╭${"─".repeat(leftB)}`) + fg(t.title, titleText) + fg(t.border, `${"─".repeat(rightB)}╮`));
722
+ lines.push(emptyRow());
723
+ const cursor = fg(t.selected, "│");
724
+ const searchIcon = fg(t.border, "◎");
725
+ if (this.descSearchActive) {
726
+ lines.push(row(`${searchIcon} ${fg(t.needsAuth, "desc:")} ${this.descQuery}${cursor}`));
727
+ }
728
+ else if (this.nameQuery) {
729
+ lines.push(row(`${searchIcon} ${this.nameQuery}${cursor}`));
730
+ }
731
+ else {
732
+ lines.push(row(`${searchIcon} ${fg(t.placeholder, italic("search..."))}`));
733
+ }
734
+ lines.push(emptyRow());
735
+ if (this.noticeLines.length > 0) {
736
+ for (const notice of this.noticeLines) {
737
+ lines.push(row(fg(t.hint, italic(sanitizeDisplayText(notice)))));
738
+ }
739
+ lines.push(emptyRow());
740
+ }
741
+ lines.push(divider());
742
+ if (this.servers.length === 0) {
743
+ lines.push(emptyRow());
744
+ lines.push(row(fg(t.hint, italic(this.authOnly ? "No OAuth-capable MCP servers configured." : "No MCP servers configured."))));
745
+ lines.push(emptyRow());
746
+ }
747
+ else {
748
+ const maxVis = McpPanel.MAX_VISIBLE;
749
+ const total = this.visibleItems.length;
750
+ const startIdx = Math.max(0, Math.min(this.cursorIndex - Math.floor(maxVis / 2), total - maxVis));
751
+ const endIdx = Math.min(startIdx + maxVis, total);
752
+ lines.push(emptyRow());
753
+ for (let i = startIdx; i < endIdx; i++) {
754
+ const item = this.visibleItems[i];
755
+ if (!item)
756
+ continue;
757
+ const isCursor = i === this.cursorIndex;
758
+ const server = this.servers[item.serverIndex];
759
+ if (!server)
760
+ continue;
761
+ if (item.type === "server") {
762
+ lines.push(row(this.renderServerRow(server, isCursor)));
763
+ if (isCursor && server.connectionStatus === "failed" && server.failureMessage) {
764
+ for (const line of this.wrapText(sanitizeDisplayText(server.failureMessage), innerW - 6)) {
765
+ lines.push(row(` ${fg(t.cancel, line)}`));
766
+ }
767
+ }
768
+ }
769
+ else if (item.toolIndex !== undefined) {
770
+ const tool = server.tools[item.toolIndex];
771
+ if (tool)
772
+ lines.push(row(this.renderToolRow(tool, isCursor, innerW)));
773
+ }
774
+ }
775
+ lines.push(emptyRow());
776
+ if (total > maxVis) {
777
+ const prog = Math.round(((this.cursorIndex + 1) / total) * 10);
778
+ lines.push(row(`${rainbowProgress(prog, 10)} ${fg(t.hint, `${this.cursorIndex + 1}/${total}`)}`));
779
+ lines.push(emptyRow());
780
+ }
781
+ if (this.importNotice) {
782
+ lines.push(row(fg(t.needsAuth, italic(sanitizeDisplayText(this.importNotice)))));
783
+ lines.push(emptyRow());
784
+ }
785
+ if (this.authNotice) {
786
+ lines.push(row(fg(t.needsAuth, italic(sanitizeDisplayText(this.authNotice)))));
787
+ lines.push(emptyRow());
788
+ }
789
+ }
790
+ lines.push(divider());
791
+ lines.push(emptyRow());
792
+ if (this.confirmingDiscard) {
793
+ const discardBtn = this.discardSelected === 0 ? inverse(bold(fg(t.cancel, " Discard "))) : fg(t.hint, " Discard ");
794
+ const keepBtn = this.discardSelected === 1
795
+ ? inverse(bold(fg(t.confirm, " Keep & Close ")))
796
+ : fg(t.hint, " Keep & Close ");
797
+ lines.push(row(`Discard unsaved changes? ${discardBtn} ${keepBtn}`));
798
+ }
799
+ else {
800
+ if (this.authOnly) {
801
+ lines.push(row(fg(t.description, "select a server to authenticate")));
802
+ }
803
+ else {
804
+ let directCount = 0;
805
+ let directTokens = 0;
806
+ for (const server of this.servers) {
807
+ directCount += server.directCount;
808
+ directTokens += server.directTokens;
809
+ }
810
+ const stats = directCount > 0 ? `${directCount} direct ~${directTokens.toLocaleString()} tokens` : "no direct tools";
811
+ lines.push(row(fg(t.description, stats + (this.dirty ? fg(t.needsAuth, " (unsaved)") : ""))));
812
+ }
813
+ }
814
+ lines.push(emptyRow());
815
+ const saveLabel = this.keys.saveLabel();
816
+ const hints = this.authOnly
817
+ ? [
818
+ `${italic("↑↓")} navigate`,
819
+ `${italic("⏎")} auth`,
820
+ `${italic("ctrl+a")} auth`,
821
+ `${italic("esc")} clear/close`,
822
+ `${italic("ctrl+c")} quit`,
823
+ ]
824
+ : [
825
+ `${italic("↑↓")} navigate`,
826
+ `${italic("space")} toggle`,
827
+ `${italic("⏎")} expand/auth`,
828
+ `${italic("ctrl+a")} auth`,
829
+ `${italic("ctrl+r")} reconnect`,
830
+ ...(this.selectedServerHasFailureMessage() ? [`${italic("ctrl+y")} copy error`] : []),
831
+ `${italic("?")} desc search`,
832
+ ...(saveLabel ? [`${italic(saveLabel)} save`] : []),
833
+ `${italic("esc")} clear/close`,
834
+ `${italic("ctrl+c")} quit`,
835
+ ];
836
+ const gap = " ";
837
+ const gapW = 2;
838
+ const maxW = innerW - 2;
839
+ let curLine = "";
840
+ let curW = 0;
841
+ for (const hint of hints) {
842
+ const hw = visibleWidth(hint);
843
+ const needed = curW === 0 ? hw : gapW + hw;
844
+ if (curW > 0 && curW + needed > maxW) {
845
+ lines.push(row(fg(t.hint, curLine)));
846
+ curLine = hint;
847
+ curW = hw;
848
+ }
849
+ else {
850
+ curLine += (curW > 0 ? gap : "") + hint;
851
+ curW += needed;
852
+ }
853
+ }
854
+ if (curLine)
855
+ lines.push(row(fg(t.hint, curLine)));
856
+ lines.push(fg(t.border, `╰${"─".repeat(innerW)}╯`));
857
+ return lines;
858
+ }
859
+ renderServerRow(server, isCursor) {
860
+ const t = this.t;
861
+ const bold = (s) => `\x1b[1m${s}\x1b[22m`;
862
+ const expandIcon = server.expanded ? "▾" : "▸";
863
+ const prefix = isCursor ? fg(t.selected, expandIcon) : fg(t.border, server.expanded ? expandIcon : "·");
864
+ const serverName = sanitizeDisplayText(server.name);
865
+ const importKind = sanitizeDisplayText(server.importKind ?? "import");
866
+ const nameStr = isCursor ? bold(fg(t.selected, serverName)) : serverName;
867
+ const importLabel = server.source === "import" ? fg(t.description, ` (${importKind})`) : "";
868
+ const statusLabel = this.renderConnectionStatus(server);
869
+ if (!server.hasCachedData && !this.authOnly) {
870
+ return `${prefix} ${nameStr}${importLabel} ${fg(t.description, "(not cached)")}${statusLabel}`;
871
+ }
872
+ const directCount = server.directCount;
873
+ const totalCount = server.tools.length;
874
+ let toggleIcon = fg(t.description, "○");
875
+ if (directCount === totalCount && totalCount > 0) {
876
+ toggleIcon = fg(t.direct, "●");
877
+ }
878
+ else if (directCount > 0) {
879
+ toggleIcon = fg(t.needsAuth, "◐");
880
+ }
881
+ let toolInfo = "";
882
+ if (totalCount > 0) {
883
+ toolInfo = `${directCount}/${totalCount}`;
884
+ if (directCount > 0) {
885
+ toolInfo += ` ~${server.directTokens.toLocaleString()}`;
886
+ }
887
+ toolInfo = fg(t.description, toolInfo);
888
+ }
889
+ return `${prefix} ${toggleIcon} ${nameStr}${importLabel} ${toolInfo}${statusLabel}`;
890
+ }
891
+ selectedServerHasFailureMessage() {
892
+ const item = this.visibleItems[this.cursorIndex];
893
+ if (!item)
894
+ return false;
895
+ const server = this.servers[item.serverIndex];
896
+ return server?.connectionStatus === "failed" && !!server.failureMessage;
897
+ }
898
+ wrapText(text, width) {
899
+ const max = Math.max(8, width);
900
+ const words = text.split(/\s+/).filter(Boolean);
901
+ const lines = [];
902
+ let current = "";
903
+ const splitLongWord = (word) => {
904
+ let rest = word;
905
+ while (visibleWidth(rest) > max) {
906
+ let take = "";
907
+ let index = 0;
908
+ while (index < rest.length && visibleWidth(take + rest.charAt(index)) <= max) {
909
+ take += rest.charAt(index);
910
+ index++;
911
+ }
912
+ if (!take)
913
+ take = rest.charAt(0);
914
+ lines.push(take);
915
+ rest = rest.slice(take.length);
916
+ }
917
+ return rest;
918
+ };
919
+ for (const word of words) {
920
+ const candidate = current ? `${current} ${word}` : word;
921
+ if (visibleWidth(candidate) <= max) {
922
+ current = candidate;
923
+ }
924
+ else {
925
+ if (current)
926
+ lines.push(current);
927
+ current = splitLongWord(word);
928
+ }
929
+ }
930
+ if (current)
931
+ lines.push(current);
932
+ return lines.length > 0 ? lines : [text];
933
+ }
934
+ renderConnectionStatus(server) {
935
+ const t = this.t;
936
+ if (this.authInFlight === server.name)
937
+ return ` ${fg(t.needsAuth, "authenticating")}`;
938
+ if (server.connectionStatus === "disabled")
939
+ return ` ${fg(t.description, "disabled")}`;
940
+ if (server.connectionStatus === "needs-auth")
941
+ return ` ${fg(t.needsAuth, "needs auth")}`;
942
+ if (server.connectionStatus === "connecting")
943
+ return ` ${fg(t.needsAuth, "connecting")}`;
944
+ if (server.connectionStatus === "failed")
945
+ return ` ${fg(t.cancel, "failed")}`;
946
+ if (this.authOnly && server.connectionStatus === "connected")
947
+ return ` ${fg(t.direct, "connected")}`;
948
+ if (this.authOnly)
949
+ return ` ${fg(t.description, "idle")}`;
950
+ return "";
951
+ }
952
+ renderToolRow(tool, isCursor, innerW) {
953
+ const t = this.t;
954
+ const bold = (s) => `\x1b[1m${s}\x1b[22m`;
955
+ const toggleIcon = tool.isDirect ? fg(t.direct, "●") : fg(t.description, "○");
956
+ const cursor = isCursor ? fg(t.selected, "▸") : " ";
957
+ const toolName = sanitizeDisplayText(tool.name);
958
+ const description = sanitizeDisplayText(tool.description);
959
+ const nameStr = isCursor ? bold(fg(t.selected, toolName)) : toolName;
960
+ const prefixLen = 7 + visibleWidth(toolName);
961
+ const maxDescLen = Math.max(0, innerW - prefixLen - 8);
962
+ const descStr = maxDescLen > 5 && description ? fg(t.description, `— ${truncateToWidth(description, maxDescLen, "…")}`) : "";
963
+ return ` ${cursor} ${toggleIcon} ${nameStr} ${descStr}`;
964
+ }
965
+ invalidate() { }
966
+ dispose() {
967
+ this.cleanup();
968
+ }
969
+ }
970
+ export function createMcpPanel(config, cache, provenance, callbacks, tui, done, options) {
971
+ return new McpPanel(config, cache, provenance, callbacks, tui, done, options ?? {});
972
+ }
973
+ //# sourceMappingURL=mcp-panel.js.map