@herbertgao/pi-extensions 2026.8.5 → 2026.8.7

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 (389) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
  17. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  18. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
  19. package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
  20. package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
  21. package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
  22. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
  23. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
  24. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
  25. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
  26. package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
  27. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
  28. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
  29. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
  30. package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
  31. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
  32. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
  33. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
  34. package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
  35. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
  36. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
  37. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
  38. package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
  39. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
  40. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
  41. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
  42. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
  43. package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
  44. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
  46. package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
  47. package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
  48. package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
  49. package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
  50. package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
  51. package/node_modules/@narumitw/pi-btw/README.md +119 -103
  52. package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
  53. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
  54. package/node_modules/@narumitw/pi-btw/package.json +13 -9
  55. package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
  56. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
  57. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
  58. package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
  59. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  60. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
  61. package/node_modules/pi-lens/CHANGELOG.md +1004 -0
  62. package/node_modules/pi-lens/README.md +11 -3
  63. package/node_modules/pi-lens/config/ruff/core.toml +42 -0
  64. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
  65. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
  66. package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
  67. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
  68. package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
  69. package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
  70. package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
  71. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
  72. package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
  73. package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
  74. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
  75. package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +192 -0
  76. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  77. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  78. package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
  79. package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
  80. package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
  81. package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
  82. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
  83. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  84. package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
  85. package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
  86. package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
  87. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
  88. package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
  89. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
  90. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
  91. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  92. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
  93. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
  94. package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
  95. package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
  96. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
  97. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
  98. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
  99. package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
  100. package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
  101. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
  102. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
  103. package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
  104. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
  105. package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
  106. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
  107. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
  108. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
  109. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
  110. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
  111. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
  112. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
  113. package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
  114. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
  115. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
  116. package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
  117. package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
  118. package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
  119. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
  120. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
  121. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
  122. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
  123. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
  124. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
  125. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
  126. package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
  127. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
  128. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
  129. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  130. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  131. package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
  132. package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
  133. package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
  134. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
  135. package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
  136. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  137. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  138. package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
  139. package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
  140. package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
  141. package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
  142. package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
  143. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  144. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
  145. package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
  146. package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
  147. package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
  148. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
  149. package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
  150. package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
  151. package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
  152. package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
  153. package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
  154. package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
  155. package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
  156. package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
  157. package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
  158. package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
  159. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  160. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
  161. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  162. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  163. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
  164. package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
  165. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
  166. package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
  167. package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
  168. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
  169. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
  170. package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
  171. package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
  172. package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
  173. package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
  174. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -0
  175. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  176. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +61 -3
  177. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
  178. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
  179. package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
  180. package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
  181. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  182. package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
  183. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  184. package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
  185. package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
  186. package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
  187. package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
  188. package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
  189. package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
  190. package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
  191. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
  192. package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
  193. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
  194. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
  195. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  196. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +50 -2
  197. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
  198. package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
  199. package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
  200. package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
  201. package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
  202. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
  204. package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
  205. package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
  206. package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
  207. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  208. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
  209. package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
  210. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
  211. package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
  212. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
  213. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
  214. package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
  215. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  216. package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
  217. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
  218. package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
  219. package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
  220. package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
  221. package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
  222. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  223. package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
  224. package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
  225. package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
  226. package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
  227. package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
  228. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  229. package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
  230. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
  231. package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
  232. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
  233. package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
  234. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  235. package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
  236. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  237. package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
  238. package/node_modules/pi-lens/dist/index.js +64677 -49484
  239. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  240. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  241. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  242. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  243. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
  244. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
  245. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
  246. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  247. package/node_modules/pi-lens/docs/agent-guide.md +44 -14
  248. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
  249. package/node_modules/pi-lens/docs/dispositions.md +21 -0
  250. package/node_modules/pi-lens/docs/features.md +33 -6
  251. package/node_modules/pi-lens/docs/globalconfig.md +14 -0
  252. package/node_modules/pi-lens/docs/language-coverage.md +71 -5
  253. package/node_modules/pi-lens/docs/mcp.md +2 -2
  254. package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
  255. package/node_modules/pi-lens/docs/settings.md +25 -1
  256. package/node_modules/pi-lens/docs/usage.md +24 -5
  257. package/node_modules/pi-lens/docs/word-index.md +35 -0
  258. package/node_modules/pi-lens/package.json +10 -6
  259. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
  260. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
  261. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
  262. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
  263. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
  264. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
  265. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  266. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  267. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-dupe-class-members-test.yml +33 -0
  268. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
  269. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
  270. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
  271. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
  272. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
  273. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
  274. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
  275. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
  276. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
  277. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
  278. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
  279. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
  280. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
  281. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
  282. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  283. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-parameters-test.yml +12 -0
  284. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
  285. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
  286. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
  287. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
  288. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
  289. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
  290. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
  291. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
  292. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
  293. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
  294. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
  295. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
  296. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  297. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  298. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-dupe-class-members.yml +52 -8
  299. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
  300. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
  301. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
  302. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
  303. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
  304. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
  305. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
  306. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
  307. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
  308. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
  309. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
  310. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
  311. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
  312. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
  313. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
  314. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
  315. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
  316. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
  317. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
  318. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
  319. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
  320. package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
  321. package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
  322. package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
  323. package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
  324. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
  325. package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
  326. package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
  327. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -11
  328. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  329. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +87 -116
  330. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
  331. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  332. package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
  333. package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
  334. package/node_modules/pi-mcp-adapter/README.md +51 -6
  335. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  336. package/node_modules/pi-mcp-adapter/cli.js +188 -5
  337. package/node_modules/pi-mcp-adapter/commands.ts +128 -22
  338. package/node_modules/pi-mcp-adapter/config.ts +28 -10
  339. package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
  340. package/node_modules/pi-mcp-adapter/index.ts +219 -32
  341. package/node_modules/pi-mcp-adapter/init.ts +36 -4
  342. package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
  343. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
  344. package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
  345. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
  346. package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
  347. package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
  348. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  349. package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
  350. package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
  351. package/node_modules/pi-mcp-adapter/package.json +4 -1
  352. package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
  353. package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
  354. package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
  355. package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
  356. package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
  357. package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
  358. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  359. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
  360. package/node_modules/pi-mcp-adapter/types.ts +95 -10
  361. package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
  362. package/node_modules/pi-mcp-adapter/utils.ts +70 -0
  363. package/node_modules/pi-web-access/CHANGELOG.md +37 -0
  364. package/node_modules/pi-web-access/README.md +38 -14
  365. package/node_modules/pi-web-access/anysearch.ts +4 -2
  366. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  367. package/node_modules/pi-web-access/brave.ts +16 -4
  368. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  369. package/node_modules/pi-web-access/curator-page.ts +8 -3
  370. package/node_modules/pi-web-access/curator-server.ts +5 -1
  371. package/node_modules/pi-web-access/exa.ts +18 -7
  372. package/node_modules/pi-web-access/extract.ts +134 -37
  373. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  374. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  375. package/node_modules/pi-web-access/gemini-search.ts +33 -7
  376. package/node_modules/pi-web-access/index.ts +160 -104
  377. package/node_modules/pi-web-access/package.json +2 -2
  378. package/node_modules/pi-web-access/page-query.ts +4 -2
  379. package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
  380. package/node_modules/pi-web-access/parallel.ts +2 -2
  381. package/node_modules/pi-web-access/query-rewrite.ts +51 -0
  382. package/node_modules/pi-web-access/serper.ts +211 -0
  383. package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
  384. package/node_modules/pi-web-access/summary-review.ts +51 -23
  385. package/node_modules/pi-web-access/tavily.ts +16 -4
  386. package/node_modules/pi-web-access/utils.ts +87 -0
  387. package/node_modules/pi-web-access/valyu.ts +199 -0
  388. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  389. package/package.json +13 -13
@@ -4,8 +4,9 @@ import { homedir } from "node:os";
4
4
  import { dirname, join, resolve } from "node:path";
5
5
  import { parse as parseToml } from "smol-toml";
6
6
  import stripJsonComments from "strip-json-comments";
7
- import { getAgentPath } from "./agent-dir.ts";
7
+ import { getAgentPath, getConfigDirName } from "./agent-dir.ts";
8
8
  import { getAgentPluginSummaries, loadAgentPluginConfigs, type AgentPluginSummary } from "./agent-plugin-loader.ts";
9
+ import { loadPackageMcpConfigs } from "./package-mcp-loader.ts";
9
10
  import { isServerDisabled, type HostConfigDiscovery, type McpConfig, type ServerEntry, type McpSettings, type ImportKind, type ServerProvenance } from "./types.ts";
10
11
  import { toStringRecord } from "./utils.ts";
11
12
 
@@ -15,7 +16,7 @@ const AGENTS_GLOBAL_CONFIG_PATHS = [
15
16
  join(homedir(), ".agents", "mcp", "mcp.json"),
16
17
  ] as const;
17
18
  const PROJECT_CONFIG_NAME = ".mcp.json";
18
- const PROJECT_PI_CONFIG_NAME = ".pi/mcp.json";
19
+ const PROJECT_PI_CONFIG_NAME = "mcp.json";
19
20
  const REPOPROMPT_BINARY_CANDIDATES = [
20
21
  join(homedir(), "RepoPrompt", "repoprompt_cli"),
21
22
  "/Applications/Repo Prompt.app/Contents/MacOS/repoprompt-mcp",
@@ -177,7 +178,7 @@ export function getProjectConfigPath(cwd = process.cwd()): string {
177
178
  }
178
179
 
179
180
  export function getProjectPiConfigPath(cwd = process.cwd()): string {
180
- return resolve(cwd, PROJECT_PI_CONFIG_NAME);
181
+ return resolve(cwd, getConfigDirName(), PROJECT_PI_CONFIG_NAME);
181
182
  }
182
183
 
183
184
  export function getConfigDiscoveryPaths(overridePath?: string, cwd = process.cwd()): ConfigDiscoveryPath[] {
@@ -306,8 +307,12 @@ export function loadMcpConfig(overridePath?: string, cwd = process.cwd()): McpCo
306
307
  config = mergeConfigs(config, expandImports(loaded, cwd));
307
308
  }
308
309
 
310
+ const packageConfig = loadPackageMcpConfigs(cwd);
309
311
  const pluginConfig = loadAgentPluginConfigs(config.settings?.agentPluginPaths, cwd);
310
- return mergeConfigs(pluginConfig, config);
312
+ const packageServers = Object.fromEntries(
313
+ Object.entries(packageConfig.mcpServers).filter(([name]) => !Object.hasOwn(pluginConfig.mcpServers, name)),
314
+ );
315
+ return mergeConfigs({ mcpServers: packageServers }, mergeConfigs(pluginConfig, config));
311
316
  }
312
317
 
313
318
  function getMergedSettings(overridePath?: string, cwd = process.cwd()): McpSettings | undefined {
@@ -471,7 +476,7 @@ function mergeConfigs(base: McpConfig, next: McpConfig): McpConfig {
471
476
  // different url, these MUST NOT be inherited from the lower-precedence entry —
472
477
  // otherwise the original endpoint's credentials would be shipped to the new
473
478
  // url. See the SECURITY note in mergeServerMaps.
474
- const URL_BOUND_AUTH_FIELDS = ["headers", "bearerToken", "bearerTokenEnv"] as const;
479
+ const URL_BOUND_AUTH_FIELDS = ["headers", "bearerToken", "bearerTokenEnv", "bearerTokenStore", "requestHeadersCommand"] as const;
475
480
 
476
481
  function mergeServerMaps(
477
482
  base: Record<string, ServerEntry>,
@@ -491,17 +496,30 @@ function mergeServerMaps(
491
496
  // applies (it is spread last). Behaviour is unchanged when the url is
492
497
  // identical or the override omits `url` (partial overrides still inherit).
493
498
  let baseEntry: ServerEntry = existing ?? {};
494
- if (existing && typeof definition.socket === "string") {
499
+ if (existing && typeof definition.command === "string") {
495
500
  baseEntry = { ...existing };
496
501
  for (const field of [
497
- "command", "args", "env", "cwd", "url", "headers", "auth",
498
- "bearerToken", "bearerTokenEnv", "oauth",
502
+ "url", "headers", "requestHeadersCommand", "auth", "bearerToken",
503
+ "bearerTokenEnv", "oauth", "httpTransport", "socket",
499
504
  ] as const) {
500
505
  delete baseEntry[field];
501
506
  }
502
- } else if (existing?.socket && (typeof definition.command === "string" || typeof definition.url === "string")) {
507
+ } else if (existing && typeof definition.url === "string") {
503
508
  baseEntry = { ...existing };
504
- delete baseEntry.socket;
509
+ for (const field of [
510
+ "command", "args", "env", "cwd", "pluginDataDir", "literalEnv", "socket",
511
+ ] as const) {
512
+ delete baseEntry[field];
513
+ }
514
+ } else if (existing && typeof definition.socket === "string") {
515
+ baseEntry = { ...existing };
516
+ for (const field of [
517
+ "command", "args", "env", "cwd", "pluginDataDir", "literalEnv", "url",
518
+ "headers", "requestHeadersCommand", "auth", "bearerToken", "bearerTokenEnv",
519
+ "oauth", "httpTransport",
520
+ ] as const) {
521
+ delete baseEntry[field];
522
+ }
505
523
  }
506
524
  if (existing && typeof definition.url === "string" && definition.url !== existing.url) {
507
525
  if (baseEntry === existing) baseEntry = { ...existing };
@@ -11,11 +11,11 @@ import { formatSchema } from "./tool-metadata.ts";
11
11
  import { resolveMcpResultContent, transformMcpContent, transformMcpResourceContents } from "./tool-registrar.ts";
12
12
  import { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions } from "./mcp-output-guard.ts";
13
13
  import { maybeStartUiSession, summarizeUiSessionResult, type UiSessionRuntime } from "./ui-session.ts";
14
- import { formatToolName, getToolNameCandidates, isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.ts";
14
+ import { createToolSelectorCandidateIndex, formatToolName, getToolNameCandidates, isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.ts";
15
15
  import { isUiToolVisibleToModel } from "./ui-tool-visibility.ts";
16
16
  import { resourceNameToToolName } from "./resource-tools.ts";
17
17
  import { authenticate, supportsOAuth } from "./mcp-auth-flow.ts";
18
- import { formatAuthRequiredMessage, resolveServerUrl, truncateAtWord } from "./utils.ts";
18
+ import { formatAuthRequiredMessage, normalizeToolArguments, resolveServerUrl, truncateAtWord } from "./utils.ts";
19
19
  import { SessionRecoveryAuthRequiredError, withSessionRecovery } from "./session-recovery.ts";
20
20
  import { combineAbortSignals, isAbortError } from "./runtime-owner.ts";
21
21
  import { ensureToolCallApproved } from "./tool-approval.ts";
@@ -150,7 +150,10 @@ export function resolveDirectTools(
150
150
  if (!toolFilter) continue;
151
151
 
152
152
  const effectivePrefix = resolveToolPrefix(definition, prefix);
153
- const getOtherCurrentCandidates = (toolName: string): Set<string> => {
153
+ const hasToolFilters =
154
+ (Array.isArray(definition.includeTools) && definition.includeTools.length > 0) ||
155
+ (Array.isArray(definition.excludeTools) && definition.excludeTools.length > 0);
156
+ const selectorCandidateIndex = hasToolFilters ? (() => {
154
157
  const candidates = new Set<string>();
155
158
  for (const [otherServerName, otherDefinition] of Object.entries(config.mcpServers)) {
156
159
  const otherCache = cache.servers[otherServerName];
@@ -167,14 +170,13 @@ export function resolveDirectTools(
167
170
  }
168
171
  }
169
172
  }
170
- for (const candidate of getToolNameCandidates(toolName, serverName, effectivePrefix, false)) candidates.delete(candidate);
171
- return candidates;
172
- };
173
+ return createToolSelectorCandidateIndex(candidates);
174
+ })() : undefined;
173
175
 
174
176
  for (const tool of serverCache.tools ?? []) {
175
177
  if (!isUiToolVisibleToModel(tool.uiVisibility)) continue;
176
178
  if (toolFilter !== true && !toolFilter.includes(tool.name)) continue;
177
- if (!isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, getOtherCurrentCandidates(tool.name))) continue;
179
+ if (!isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex)) continue;
178
180
  const prefixedName = formatToolName(tool.name, serverName, effectivePrefix);
179
181
  if (BUILTIN_NAMES.has(prefixedName)) {
180
182
  console.warn(`MCP: skipping direct tool "${prefixedName}" (collides with builtin)`);
@@ -200,7 +202,7 @@ export function resolveDirectTools(
200
202
  for (const resource of serverCache.resources ?? []) {
201
203
  const baseName = `read_${resourceNameToToolName(resource.name)}`;
202
204
  if (toolFilter !== true && !toolFilter.includes(baseName)) continue;
203
- if (!isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, getOtherCurrentCandidates(baseName))) continue;
205
+ if (!isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex)) continue;
204
206
  const prefixedName = formatToolName(baseName, serverName, effectivePrefix);
205
207
  if (BUILTIN_NAMES.has(prefixedName)) {
206
208
  console.warn(`MCP: skipping direct resource tool "${prefixedName}" (collides with builtin)`);
@@ -222,7 +224,7 @@ export function resolveDirectTools(
222
224
  }
223
225
  }
224
226
 
225
- if (specs.length >= DIRECT_TOOLS_ADVISORY_THRESHOLD) {
227
+ if (config.settings?.warnOnLargeDirectTools !== false && specs.length >= DIRECT_TOOLS_ADVISORY_THRESHOLD) {
226
228
  console.warn(`MCP: ${specs.length} direct tools resolved. Each direct tool adds prompt context; README guidance recommends targeted sets of 5-20 tools and using the proxy or an explicit string[] when 75+ direct tools would be registered.`);
227
229
  }
228
230
 
@@ -255,8 +257,10 @@ export function buildProxyDescription(
255
257
  const cachedEntry = cache?.servers?.[serverName];
256
258
  const entry = cachedEntry && isServerCacheValid(cachedEntry, definition) ? cachedEntry : undefined;
257
259
  const effectivePrefix = resolveToolPrefix(definition, prefix);
258
- const getOtherCurrentCandidates = (toolName: string): Set<string> | undefined => {
259
- if (!cache) return undefined;
260
+ const hasToolFilters =
261
+ (Array.isArray(definition.includeTools) && definition.includeTools.length > 0) ||
262
+ (Array.isArray(definition.excludeTools) && definition.excludeTools.length > 0);
263
+ const selectorCandidateIndex = hasToolFilters && cache ? (() => {
260
264
  const candidates = new Set<string>();
261
265
  for (const [otherServerName, otherDefinition] of Object.entries(config.mcpServers)) {
262
266
  const otherEntry = cache.servers[otherServerName];
@@ -273,17 +277,16 @@ export function buildProxyDescription(
273
277
  }
274
278
  }
275
279
  }
276
- for (const candidate of getToolNameCandidates(toolName, serverName, effectivePrefix, false)) candidates.delete(candidate);
277
- return candidates;
278
- };
280
+ return createToolSelectorCandidateIndex(candidates);
281
+ })() : undefined;
279
282
  const toolCount = (entry?.tools ?? []).filter(
280
283
  (tool) => isUiToolVisibleToModel(tool.uiVisibility)
281
- && isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, getOtherCurrentCandidates(tool.name)),
284
+ && isToolAllowed(tool.name, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex),
282
285
  ).length;
283
286
  const resourceCount = definition?.exposeResources !== false
284
287
  ? (entry?.resources ?? []).filter((resource) => {
285
288
  const baseName = `read_${resourceNameToToolName(resource.name)}`;
286
- return isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, getOtherCurrentCandidates(baseName));
289
+ return isToolAllowed(baseName, serverName, effectivePrefix, definition.includeTools, definition.excludeTools, selectorCandidateIndex);
287
290
  }).length
288
291
  : 0;
289
292
  const totalItems = toolCount + resourceCount;
@@ -426,6 +429,7 @@ export function createDirectToolExecutor(
426
429
  };
427
430
  }
428
431
 
432
+ const normalizedParams = spec.resourceUri ? params : normalizeToolArguments(params);
429
433
  const approval = await ensureToolCallApproved(state, spec.serverName, {
430
434
  name: spec.prefixedName,
431
435
  originalName: spec.originalName,
@@ -434,7 +438,7 @@ export function createDirectToolExecutor(
434
438
  ...(spec.resourceUri !== undefined ? { resourceUri: spec.resourceUri } : {}),
435
439
  ...(spec.uiResourceUri !== undefined ? { uiResourceUri: spec.uiResourceUri } : {}),
436
440
  ...(spec.uiStreamMode !== undefined ? { uiStreamMode: spec.uiStreamMode } : {}),
437
- }, params, ownedSignal, spec.resourceUri ? "resource" : "direct");
441
+ }, normalizedParams, ownedSignal, spec.resourceUri ? "resource" : "direct");
438
442
  if (approval.ok === false) {
439
443
  const denied = approval.reason === "denied";
440
444
  const message = denied
@@ -506,7 +510,7 @@ export function createDirectToolExecutor(
506
510
  ? await maybeStartUiSession(state, {
507
511
  serverName: spec.serverName,
508
512
  toolName: spec.originalName,
509
- toolArgs: params ?? {},
513
+ toolArgs: normalizedParams,
510
514
  uiResourceUri: spec.uiResourceUri!,
511
515
  ...(spec.uiStreamMode !== undefined ? { streamMode: spec.uiStreamMode } : {}),
512
516
  ...(signal ? { signal } : {}),
@@ -524,7 +528,7 @@ export function createDirectToolExecutor(
524
528
  spec.serverName,
525
529
  (conn) => abortable(conn.client.callTool({
526
530
  name: spec.originalName,
527
- arguments: params ?? {},
531
+ arguments: normalizedParams,
528
532
  _meta: uiSession?.requestMeta,
529
533
  }, requestOptions), ownedSignal),
530
534
  );
@@ -1,10 +1,10 @@
1
1
  import type { AgentToolUpdateCallback, ExtensionAPI, ExtensionContext, ToolInfo } from "@earendil-works/pi-coding-agent";
2
2
  import type { McpExtensionState } from "./state.ts";
3
- import type { DirectToolSpec, McpAdapterOptions, McpConfig, PromptMetadata } from "./types.ts";
3
+ import type { DirectToolSpec, McpAdapterOptions, McpConfig, PromptMetadata, ServerEntry } from "./types.ts";
4
4
  import type { McpOAuthRuntime } from "./mcp-auth-flow.ts";
5
5
  import { Type } from "typebox";
6
6
  import type { TSchema } from "typebox";
7
- import { showStatus, showTools, showPrompts, reconnectServer, reconnectServers, authenticateServer, logoutServer, openMcpAuthPanel, openMcpPanel, openMcpSetup } from "./commands.ts";
7
+ import { showStatus, showTools, showPrompts, reconnectServer, reconnectServers, authenticateServer, logoutServer, manageBearerToken, openMcpAuthPanel, openMcpPanel, openMcpSetup } from "./commands.ts";
8
8
  import { cloneMcpConfig, loadMcpConfig, writeProjectServerDisabledOverride } from "./config.ts";
9
9
  import { buildProxyDescription, createDirectToolExecutor, getMissingConfiguredDirectToolServers, resolveDirectTools } from "./direct-tools.ts";
10
10
  import { flushMetadataCache, initializeMcp, updateStatusBar } from "./init.ts";
@@ -22,6 +22,7 @@ import { runMcpScript } from "./mcp-code.ts";
22
22
  import { cleanupMaterializedBinaryResources } from "./tool-registrar.ts";
23
23
 
24
24
  export type { McpAdapterOptions } from "./types.ts";
25
+ export type { ServerEntry } from "./types.ts";
25
26
  export {
26
27
  MCP_STATUS_EVENT,
27
28
  MCP_STATUS_SNAPSHOT_VERSION,
@@ -38,6 +39,14 @@ export {
38
39
  const INIT_WAIT_TIMEOUT_MS = 30_000;
39
40
  const INIT_WAIT_TIMED_OUT: unique symbol = Symbol("init-wait-timed-out");
40
41
 
42
+ export interface McpServerRegistration {
43
+ dispose(): Promise<void>;
44
+ }
45
+
46
+ // Routes runtime registrations to the adapter installed for a specific Pi
47
+ // instance, so a process with several adapters cannot cross-register.
48
+ const runtimeRegistrars = new WeakMap<ExtensionAPI, (name: string, definition: ServerEntry) => McpServerRegistration>();
49
+
41
50
  async function awaitWithTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | typeof INIT_WAIT_TIMED_OUT> {
42
51
  let timer: ReturnType<typeof setTimeout> | undefined;
43
52
  try {
@@ -130,6 +139,19 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
130
139
  let proxyToolRegistered = false;
131
140
  let proxyToolDescription: string | null = null;
132
141
  let directToolsFrozen = false;
142
+ // Session/runtime scoped server registrations from other extensions. They
143
+ // survive session restarts within this install and die with the process.
144
+ const runtimeServers = new Map<string, ServerEntry>();
145
+
146
+ // Mirrors init's per-server lifecycle registration so runtime servers get
147
+ // idle cleanup and keep-alive health recovery like configured servers.
148
+ function attachRuntimeServerLifecycle(targetState: McpExtensionState, name: string, definition: ServerEntry): void {
149
+ const lifecycleMode = definition.lifecycle ?? "lazy";
150
+ const persistsAfterFirstSpawn = lifecycleMode === "eager" || lifecycleMode === "lazy-keep-alive";
151
+ const idleOverride = definition.idleTimeout ?? (persistsAfterFirstSpawn ? 0 : undefined);
152
+ targetState.lifecycle.registerServer(name, definition, idleOverride !== undefined ? { idleTimeout: idleOverride } : undefined);
153
+ if (lifecycleMode === "keep-alive") targetState.lifecycle.markKeepAlive(name, definition);
154
+ }
133
155
 
134
156
  // OMP remaps `typebox` to a host shim that historically lacked Type.Unsafe.
135
157
  // Prefer Unsafe when present (real TypeBox / fixed OMP shim); otherwise pass
@@ -282,6 +304,45 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
282
304
 
283
305
  registerPromptCommands(resolveCachedPrompts(earlyConfig));
284
306
 
307
+ runtimeRegistrars.set(pi, (name: string, definition: ServerEntry): McpServerRegistration => {
308
+ if (typeof name !== "string" || name.trim() === "") {
309
+ throw new Error("MCP server name must be a non-empty string");
310
+ }
311
+ if (typeof definition !== "object" || definition === null || Array.isArray(definition)) {
312
+ throw new Error(`MCP server definition for "${name}" must be an object`);
313
+ }
314
+ const effective = state?.config ?? earlyConfig;
315
+ if (runtimeServers.has(name) || Object.hasOwn(effective.mcpServers, name)) {
316
+ throw new Error(`MCP server "${name}" is already registered`);
317
+ }
318
+ // Runtime-registered servers are proxy-tool-only: direct tools are frozen
319
+ // at startup and must not be rebuilt for late registrations.
320
+ const entry: ServerEntry = { ...structuredClone(definition), directTools: false };
321
+ runtimeServers.set(name, entry);
322
+ const registeredState = state;
323
+ if (registeredState) {
324
+ registeredState.config.mcpServers[name] = entry;
325
+ attachRuntimeServerLifecycle(registeredState, name, entry);
326
+ syncToolSurface();
327
+ updateStatusBar(registeredState);
328
+ }
329
+ let disposed = false;
330
+ return {
331
+ dispose: async (): Promise<void> => {
332
+ if (disposed) return;
333
+ disposed = true;
334
+ runtimeServers.delete(name);
335
+ const currentState = state;
336
+ if (!currentState || currentState.config.mcpServers[name] !== entry) return;
337
+ delete currentState.config.mcpServers[name];
338
+ currentState.lifecycle.unregisterServer(name);
339
+ await currentState.manager.close(name);
340
+ syncToolSurface();
341
+ updateStatusBar(currentState);
342
+ },
343
+ };
344
+ });
345
+
285
346
  const getPiTools = (): ToolInfo[] => pi.getAllTools();
286
347
 
287
348
  pi.registerFlag("mcp-config", {
@@ -299,7 +360,6 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
299
360
  }
300
361
  : {}),
301
362
  oauthRuntime,
302
- statusEvents: pi.events,
303
363
  });
304
364
  initPromise = promise;
305
365
 
@@ -314,6 +374,14 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
314
374
  }
315
375
 
316
376
  state = nextState;
377
+ for (const [name, definition] of runtimeServers) {
378
+ if (Object.hasOwn(nextState.config.mcpServers, name)) {
379
+ console.error(`MCP: runtime-registered server "${name}" now collides with a configured server; keeping the configured server`);
380
+ continue;
381
+ }
382
+ nextState.config.mcpServers[name] = definition;
383
+ attachRuntimeServerLifecycle(nextState, name, definition);
384
+ }
317
385
  nextState.onToolMetadataUpdated = (_serverName, _reason) => {
318
386
  if (state !== nextState || !owner.isActive()) return;
319
387
  syncPromptCommands();
@@ -325,6 +393,9 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
325
393
  };
326
394
  syncPromptCommands();
327
395
  syncToolSurface(ctx);
396
+ // A connected snapshot is readiness-like external state. Publish it only
397
+ // after Pi's model-facing direct-tool surface reflects live metadata.
398
+ nextState.statusEvents = pi.events;
328
399
  updateStatusBar(nextState);
329
400
  initPromise = null;
330
401
  if (earlyConfig.settings?.freezeDirectTools === true) {
@@ -417,6 +488,29 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
417
488
  }
418
489
  });
419
490
 
491
+ pi.on("input", async () => {
492
+ const inputOwner = currentOwner;
493
+ if (!inputOwner?.isActive()) return;
494
+
495
+ if (!state && initPromise) {
496
+ try {
497
+ await awaitWithTimeout(initPromise, INIT_WAIT_TIMEOUT_MS);
498
+ } catch {
499
+ return;
500
+ }
501
+ }
502
+
503
+ const inputState = state;
504
+ if (!inputState || !inputOwner.isActive()) return;
505
+ try {
506
+ await inputState.lifecycle.ensureConverged(inputOwner.signal);
507
+ } catch (error) {
508
+ if (!isAbortError(error, inputOwner.signal)) {
509
+ logger.debug(`MCP: keep-alive convergence failed before input: ${formatTerminalError(error)}`);
510
+ }
511
+ }
512
+ });
513
+
420
514
  pi.on("session_shutdown", async () => {
421
515
  ++lifecycleGeneration;
422
516
  const currentState = state;
@@ -444,7 +538,7 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
444
538
  // Re-flag returned MCP tool failures so pi registers them as errors (see toolErrorOverride).
445
539
  pi.on("tool_result", (event) => toolErrorOverride(event.details));
446
540
 
447
- pi.registerCommand("mcp", {
541
+ const registerMcpCommand = (commandName: string) => pi.registerCommand(commandName, {
448
542
  description: "Show MCP server status",
449
543
  getArgumentCompletions: (prefix: string) => {
450
544
  const normalized = prefix.trimStart();
@@ -456,6 +550,7 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
456
550
  { value: "prompts", label: "prompts — List all MCP prompts" },
457
551
  { value: "setup", label: "setup — Configure MCP servers" },
458
552
  { value: "logout", label: "logout — Clear server credentials" },
553
+ { value: "token", label: "token — Manage stored bearer tokens" },
459
554
  { value: "disable", label: "disable — Disable a server" },
460
555
  { value: "enable", label: "enable — Enable a server" },
461
556
  { value: "status", label: "status — Show server status" },
@@ -465,11 +560,27 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
465
560
 
466
561
  const [, subcommand, argumentPrefix] = argumentMatch;
467
562
  if (
468
- (subcommand !== "reconnect" && subcommand !== "logout" && subcommand !== "disable" && subcommand !== "enable")
563
+ (subcommand !== "reconnect" && subcommand !== "logout" && subcommand !== "disable" && subcommand !== "enable" && subcommand !== "token")
469
564
  || argumentPrefix === undefined
470
565
  || !state
471
566
  ) return null;
472
567
 
568
+ if (subcommand === "token") {
569
+ const tokenMatch = argumentPrefix.trimStart().match(/^(set|remove|status)\s+(.*)$/);
570
+ if (!tokenMatch) {
571
+ const actions = ["set", "remove", "status"]
572
+ .filter(action => action.startsWith(argumentPrefix.trimStart()))
573
+ .map(action => ({ value: `token ${action} `, label: `${action} — Bearer token ${action}` }));
574
+ return actions.length > 0 ? actions : null;
575
+ }
576
+ const action = tokenMatch[1] ?? "";
577
+ const serverPrefix = tokenMatch[2] ?? "";
578
+ const servers = Object.keys(state.config.mcpServers)
579
+ .filter(serverName => serverName.startsWith(serverPrefix.trimStart()))
580
+ .map(serverName => ({ value: `token ${action} ${serverName}`, label: serverName }));
581
+ return servers.length > 0 ? servers : null;
582
+ }
583
+
473
584
  const servers = Object.keys(state.config.mcpServers)
474
585
  .filter((serverName) => serverName.startsWith(argumentPrefix.trimStart()))
475
586
  .map((serverName) => ({ value: `${subcommand} ${serverName}`, label: serverName }));
@@ -545,6 +656,21 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
545
656
  await logoutServer(serverName, state, commandCtx);
546
657
  break;
547
658
  }
659
+ case "token": {
660
+ const action = parts[1];
661
+ const serverName = parts.slice(2).join(" ");
662
+ if (action !== "set" && action !== "remove" && action !== "status") {
663
+ if (commandCtx.hasUI) commandCtx.ui?.notify("Usage: /mcp token set|remove|status <server>", "error");
664
+ return;
665
+ }
666
+ if (!serverName) {
667
+ if (commandCtx.hasUI) commandCtx.ui?.notify("Usage: /mcp token set|remove|status <server>", "error");
668
+ return;
669
+ }
670
+ commandOwner?.throwIfInactive();
671
+ await manageBearerToken(action, serverName, state, commandCtx);
672
+ break;
673
+ }
548
674
  case "disable":
549
675
  case "enable": {
550
676
  const serverName = rest;
@@ -595,6 +721,8 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
595
721
  }
596
722
  },
597
723
  });
724
+ registerMcpCommand("mcp");
725
+ registerMcpCommand("pi-mcp");
598
726
 
599
727
  pi.registerCommand("mcp-auth", {
600
728
  description: "Authenticate with an MCP server (OAuth)",
@@ -737,12 +865,12 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
737
865
  action?: string;
738
866
  }, signal: AbortSignal | undefined, _onUpdate: AgentToolUpdateCallback<Record<string, unknown>> | undefined, _ctx: ExtensionContext) {
739
867
  const executeOwner = currentOwner;
740
- let parsedArgs: Record<string, unknown> | undefined;
741
- if (params.args !== undefined && params.args !== "") {
868
+ const parseArgs = (value: string | Record<string, unknown> | undefined): Record<string, unknown> | undefined => {
869
+ if (value === undefined || value === "") return undefined;
742
870
  let args: unknown;
743
- if (typeof params.args === "string") {
871
+ if (typeof value === "string") {
744
872
  try {
745
- args = JSON.parse(params.args);
873
+ args = JSON.parse(value);
746
874
  } catch (error) {
747
875
  if (error instanceof SyntaxError) {
748
876
  throw new Error(`Invalid args JSON: ${error.message}`, { cause: error });
@@ -750,14 +878,57 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
750
878
  throw error;
751
879
  }
752
880
  } else {
753
- args = params.args;
881
+ args = value;
754
882
  }
755
883
 
756
884
  if (typeof args !== "object" || args === null || Array.isArray(args)) {
757
885
  const gotType = Array.isArray(args) ? "array" : args === null ? "null" : typeof args;
758
886
  throw new Error(`Invalid args: expected a JSON object, got ${gotType}`);
759
887
  }
760
- parsedArgs = args as Record<string, unknown>;
888
+ return args as Record<string, unknown>;
889
+ };
890
+ const validateNestedGatewayParams = (value: Record<string, unknown>): typeof params => {
891
+ for (const key of ["tool", "connect", "describe", "instructions", "search", "server", "action"] as const) {
892
+ if (value[key] !== undefined && typeof value[key] !== "string") {
893
+ throw new Error(`Invalid nested gateway param \`${key}\`: expected string, got ${Array.isArray(value[key]) ? "array" : typeof value[key]}`);
894
+ }
895
+ }
896
+ for (const key of ["regex", "includeSchemas"] as const) {
897
+ if (value[key] !== undefined && typeof value[key] !== "boolean") {
898
+ throw new Error(`Invalid nested gateway param \`${key}\`: expected boolean, got ${Array.isArray(value[key]) ? "array" : typeof value[key]}`);
899
+ }
900
+ }
901
+ for (const key of ["limit", "offset"] as const) {
902
+ if (value[key] !== undefined && typeof value[key] !== "number") {
903
+ throw new Error(`Invalid nested gateway param \`${key}\`: expected number, got ${Array.isArray(value[key]) ? "array" : typeof value[key]}`);
904
+ }
905
+ }
906
+ const args = value.args;
907
+ if (args !== undefined && typeof args !== "string" && (typeof args !== "object" || args === null || Array.isArray(args))) {
908
+ throw new Error(`Invalid nested gateway param \`args\`: expected JSON object or JSON string, got ${Array.isArray(args) ? "array" : args === null ? "null" : typeof args}`);
909
+ }
910
+ return value as typeof params;
911
+ };
912
+ let parsedArgs = parseArgs(params.args);
913
+ let dispatchParams = params;
914
+ const hasGatewayMode = (value: typeof params): boolean =>
915
+ value.tool !== undefined
916
+ || value.connect !== undefined
917
+ || value.describe !== undefined
918
+ || value.instructions !== undefined
919
+ || value.search !== undefined
920
+ || value.server !== undefined
921
+ || value.action !== undefined;
922
+ if (!hasGatewayMode(params) && parsedArgs) {
923
+ const nestedParams = validateNestedGatewayParams(parsedArgs);
924
+ if (hasGatewayMode(nestedParams)) {
925
+ dispatchParams = nestedParams;
926
+ parsedArgs = parseArgs(nestedParams.args);
927
+ } else {
928
+ throw new Error("Gateway params were nested inside `args`; pass them top-level (for example, mcp({ search: \"...\" }) or mcp({ tool: \"...\", args: {} })).");
929
+ }
930
+ } else if (!hasGatewayMode(params) && params.args !== undefined) {
931
+ throw new Error("Gateway params were nested inside `args`; pass them top-level (for example, mcp({ search: \"...\" }) or mcp({ tool: \"...\", args: {} })).");
761
932
  }
762
933
 
763
934
  if (!state && initPromise) {
@@ -788,22 +959,22 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
788
959
  }
789
960
  executeOwner?.throwIfInactive();
790
961
 
791
- if (params.action === "ui-messages") {
962
+ if (dispatchParams.action === "ui-messages") {
792
963
  return executeUiMessages(state);
793
964
  }
794
- if (params.action === "auth-start") {
795
- if (!params.server) {
965
+ if (dispatchParams.action === "auth-start") {
966
+ if (!dispatchParams.server) {
796
967
  return {
797
968
  content: [{ type: "text" as const, text: "auth-start requires `server`. Example: mcp({ action: \"auth-start\", server: \"linear-server\" })" }],
798
969
  details: { mode: "auth-start", error: "missing_server" },
799
970
  };
800
971
  }
801
972
  return signal
802
- ? executeAuthStart(state, params.server, signal)
803
- : executeAuthStart(state, params.server);
973
+ ? executeAuthStart(state, dispatchParams.server, signal)
974
+ : executeAuthStart(state, dispatchParams.server);
804
975
  }
805
- if (params.action === "auth-complete") {
806
- if (!params.server) {
976
+ if (dispatchParams.action === "auth-complete") {
977
+ if (!dispatchParams.server) {
807
978
  return {
808
979
  content: [{ type: "text" as const, text: "auth-complete requires `server`." }],
809
980
  details: { mode: "auth-complete", error: "missing_server" },
@@ -817,28 +988,28 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
817
988
  };
818
989
  }
819
990
  return signal
820
- ? executeAuthComplete(state, params.server, input, signal)
821
- : executeAuthComplete(state, params.server, input);
991
+ ? executeAuthComplete(state, dispatchParams.server, input, signal)
992
+ : executeAuthComplete(state, dispatchParams.server, input);
822
993
  }
823
- if (params.tool) {
824
- return executeCall(state, params.tool, parsedArgs, params.server, getPiTools, signal);
994
+ if (dispatchParams.tool) {
995
+ return executeCall(state, dispatchParams.tool, parsedArgs, dispatchParams.server, getPiTools, signal);
825
996
  }
826
- if (params.connect) {
827
- const result = await executeConnect(state, params.connect, signal);
997
+ if (dispatchParams.connect) {
998
+ const result = await executeConnect(state, dispatchParams.connect, signal);
828
999
  syncToolSurface(_ctx as ExtensionContext);
829
1000
  return result;
830
1001
  }
831
- if (params.describe) {
832
- return executeDescribe(state, params.describe);
1002
+ if (dispatchParams.describe) {
1003
+ return executeDescribe(state, dispatchParams.describe);
833
1004
  }
834
- if (params.instructions) {
835
- return executeInstructions(state, params.instructions);
1005
+ if (dispatchParams.instructions) {
1006
+ return executeInstructions(state, dispatchParams.instructions);
836
1007
  }
837
- if (params.search !== undefined) {
838
- return executeSearch(state, params.search, params.regex, params.server, params.includeSchemas, params.limit, params.offset);
1008
+ if (dispatchParams.search !== undefined) {
1009
+ return executeSearch(state, dispatchParams.search, dispatchParams.regex, dispatchParams.server, dispatchParams.includeSchemas, dispatchParams.limit, dispatchParams.offset);
839
1010
  }
840
- if (params.server) {
841
- return executeList(state, params.server);
1011
+ if (dispatchParams.server) {
1012
+ return executeList(state, dispatchParams.server);
842
1013
  }
843
1014
  return executeStatus(state);
844
1015
  },
@@ -895,4 +1066,20 @@ export function createMcpAdapter(options: McpAdapterOptions = {}) {
895
1066
  };
896
1067
  }
897
1068
 
1069
+ /**
1070
+ * Register an MCP server with the adapter installed for this Pi instance.
1071
+ * Registrations are session/runtime scoped and never persisted. Duplicate
1072
+ * names fail closed. Registered servers are proxy-tool-only; their tools
1073
+ * become visible at the next tool sync. To change a definition, dispose the
1074
+ * registration and register again.
1075
+ */
1076
+ export function registerMcpServer(options: { pi: ExtensionAPI; name: string; definition: ServerEntry }): McpServerRegistration {
1077
+ const { pi, name, definition } = options;
1078
+ const register = runtimeRegistrars.get(pi);
1079
+ if (!register) {
1080
+ throw new Error("pi-mcp-adapter is not installed for this Pi instance");
1081
+ }
1082
+ return register(name, definition);
1083
+ }
1084
+
898
1085
  export default createMcpAdapter();