@f5-sales-demo/xcsh 19.51.2
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.
- package/CHANGELOG.md +7073 -0
- package/README.md +14 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +109 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/todo/index.ts +206 -0
- package/examples/extensions/README.md +143 -0
- package/examples/extensions/api-demo.ts +89 -0
- package/examples/extensions/chalk-logger.ts +25 -0
- package/examples/extensions/hello.ts +32 -0
- package/examples/extensions/pirate.ts +43 -0
- package/examples/extensions/plan-mode.ts +551 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/todo.ts +296 -0
- package/examples/extensions/tools.ts +145 -0
- package/examples/extensions/with-deps/index.ts +36 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +16 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +48 -0
- package/examples/hooks/confirm-destructive.ts +58 -0
- package/examples/hooks/custom-compaction.ts +115 -0
- package/examples/hooks/dirty-repo-guard.ts +51 -0
- package/examples/hooks/file-trigger.ts +40 -0
- package/examples/hooks/git-checkpoint.ts +52 -0
- package/examples/hooks/handoff.ts +149 -0
- package/examples/hooks/permission-gate.ts +33 -0
- package/examples/hooks/protected-paths.ts +29 -0
- package/examples/hooks/qna.ts +118 -0
- package/examples/hooks/status-line.ts +39 -0
- package/examples/sdk/01-minimal.ts +21 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +43 -0
- package/examples/sdk/04-skills.ts +43 -0
- package/examples/sdk/06-extensions.ts +80 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +35 -0
- package/examples/sdk/08-prompt-templates.ts +41 -0
- package/examples/sdk/08-slash-commands.ts +46 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
- package/examples/sdk/11-sessions.ts +47 -0
- package/examples/sdk/README.md +172 -0
- package/package.json +539 -0
- package/scripts/build-info/resolvers.ts +51 -0
- package/scripts/capture-ax-fixture.ts +84 -0
- package/scripts/capture-login-fixture.ts +30 -0
- package/scripts/check-workflow-field-coverage.ts +101 -0
- package/scripts/extension-uat-harness.ts +340 -0
- package/scripts/format-prompts.ts +68 -0
- package/scripts/generate-api-spec-index.ts +580 -0
- package/scripts/generate-branding-index.ts +81 -0
- package/scripts/generate-build-info.ts +106 -0
- package/scripts/generate-console-catalog.ts +88 -0
- package/scripts/generate-console-field-metadata.ts +74 -0
- package/scripts/generate-docs-index.ts +40 -0
- package/scripts/generate-template.ts +32 -0
- package/scripts/generate-terraform-index.ts +81 -0
- package/scripts/mermaid-gallery.ts +132 -0
- package/scripts/uat-matrix-modalities.ts +451 -0
- package/scripts/uat-matrix-report.ts +175 -0
- package/scripts/uat-matrix.ts +353 -0
- package/src/async/index.ts +2 -0
- package/src/async/job-manager.ts +416 -0
- package/src/async/support.ts +5 -0
- package/src/autoresearch/apply-contract-to-state.ts +1 -0
- package/src/autoresearch/command-resume.md +17 -0
- package/src/autoresearch/contract.ts +205 -0
- package/src/autoresearch/dashboard.ts +374 -0
- package/src/autoresearch/git.ts +191 -0
- package/src/autoresearch/helpers.ts +328 -0
- package/src/autoresearch/index.ts +402 -0
- package/src/autoresearch/prompt.md +236 -0
- package/src/autoresearch/resume-message.md +16 -0
- package/src/autoresearch/state.ts +303 -0
- package/src/autoresearch/tools/init-experiment.ts +400 -0
- package/src/autoresearch/tools/log-experiment.ts +793 -0
- package/src/autoresearch/tools/run-experiment.ts +679 -0
- package/src/autoresearch/types.ts +114 -0
- package/src/browser/acquire.ts +249 -0
- package/src/browser/actions.ts +126 -0
- package/src/browser/auth.ts +176 -0
- package/src/browser/ax.ts +134 -0
- package/src/browser/cdp-core.ts +45 -0
- package/src/browser/cdp-page-actions.ts +51 -0
- package/src/browser/chrome-locate.ts +66 -0
- package/src/browser/dom-context.ts +89 -0
- package/src/browser/dt-context.ts +100 -0
- package/src/browser/extension-bridge.ts +182 -0
- package/src/browser/extension-page-actions.ts +397 -0
- package/src/browser/extension-provider.ts +373 -0
- package/src/browser/index.ts +17 -0
- package/src/browser/input-commit.ts +74 -0
- package/src/browser/native-messaging.ts +20 -0
- package/src/browser/page-actions.ts +18 -0
- package/src/browser/provider.ts +155 -0
- package/src/browser/resolver.ts +126 -0
- package/src/browser/selector.ts +40 -0
- package/src/bun-imports.d.ts +22 -0
- package/src/capability/context-file.ts +43 -0
- package/src/capability/extension-module.ts +34 -0
- package/src/capability/extension.ts +47 -0
- package/src/capability/fs.ts +107 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +438 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +74 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule.ts +224 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +50 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/ssh.ts +41 -0
- package/src/capability/system-prompt.ts +34 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +168 -0
- package/src/cli/agents-cli.ts +138 -0
- package/src/cli/args.ts +286 -0
- package/src/cli/chrome-cli.ts +88 -0
- package/src/cli/classify-install-target.ts +50 -0
- package/src/cli/commands/init-xdg.ts +27 -0
- package/src/cli/config-cli.ts +418 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/grep-cli.ts +160 -0
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/jupyter-cli.ts +106 -0
- package/src/cli/list-models.ts +128 -0
- package/src/cli/plugin-cli.ts +964 -0
- package/src/cli/read-cli.ts +68 -0
- package/src/cli/session-picker.ts +52 -0
- package/src/cli/setup-cli.ts +479 -0
- package/src/cli/shell-cli.ts +174 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/stats-cli.ts +182 -0
- package/src/cli/update-cli.ts +446 -0
- package/src/cli/web-search-cli.ts +203 -0
- package/src/cli.ts +123 -0
- package/src/commands/agents.ts +57 -0
- package/src/commands/chrome-host.ts +71 -0
- package/src/commands/chrome.ts +28 -0
- package/src/commands/commit.ts +36 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/grep.ts +48 -0
- package/src/commands/grievances.ts +20 -0
- package/src/commands/jupyter.ts +32 -0
- package/src/commands/launch.ts +144 -0
- package/src/commands/plugin.ts +78 -0
- package/src/commands/read.ts +33 -0
- package/src/commands/setup.ts +42 -0
- package/src/commands/shell.ts +29 -0
- package/src/commands/ssh.ts +60 -0
- package/src/commands/stats.ts +29 -0
- package/src/commands/update.ts +21 -0
- package/src/commands/web-search.ts +46 -0
- package/src/commit/agentic/agent.ts +314 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +354 -0
- package/src/commit/agentic/prompts/analyze-file.md +22 -0
- package/src/commit/agentic/prompts/session-user.md +25 -0
- package/src/commit/agentic/prompts/split-confirm.md +1 -0
- package/src/commit/agentic/prompts/system.md +38 -0
- package/src/commit/agentic/state.ts +60 -0
- package/src/commit/agentic/tools/analyze-file.ts +127 -0
- package/src/commit/agentic/tools/git-file-diff.ts +191 -0
- package/src/commit/agentic/tools/git-hunk.ts +50 -0
- package/src/commit/agentic/tools/git-overview.ts +81 -0
- package/src/commit/agentic/tools/index.ts +54 -0
- package/src/commit/agentic/tools/propose-changelog.ts +139 -0
- package/src/commit/agentic/tools/propose-commit.ts +122 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/schemas.ts +31 -0
- package/src/commit/agentic/tools/split-commit.ts +251 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +183 -0
- package/src/commit/analysis/conventional.ts +122 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +105 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +40 -0
- package/src/commit/changelog/generate.ts +101 -0
- package/src/commit/changelog/index.ts +234 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +85 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/index.ts +69 -0
- package/src/commit/map-reduce/map-phase.ts +193 -0
- package/src/commit/map-reduce/reduce-phase.ts +108 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +66 -0
- package/src/commit/pipeline.ts +242 -0
- package/src/commit/prompts/analysis-system.md +148 -0
- package/src/commit/prompts/analysis-user.md +38 -0
- package/src/commit/prompts/changelog-system.md +50 -0
- package/src/commit/prompts/changelog-user.md +18 -0
- package/src/commit/prompts/file-observer-system.md +24 -0
- package/src/commit/prompts/file-observer-user.md +8 -0
- package/src/commit/prompts/reduce-system.md +50 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +3 -0
- package/src/commit/prompts/summary-system.md +38 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/types.ts +118 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/commit/utils.ts +44 -0
- package/src/config/auto-config.ts +698 -0
- package/src/config/context-schema.json +115 -0
- package/src/config/file-lock.ts +121 -0
- package/src/config/keybindings.ts +493 -0
- package/src/config/mcp-schema.json +230 -0
- package/src/config/model-equivalence.ts +675 -0
- package/src/config/model-registry.ts +2265 -0
- package/src/config/model-resolver.ts +1289 -0
- package/src/config/prompt-templates.ts +271 -0
- package/src/config/resolve-config-value.ts +110 -0
- package/src/config/settings-schema.ts +2155 -0
- package/src/config/settings.ts +730 -0
- package/src/config.ts +418 -0
- package/src/cursor.ts +323 -0
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/index.ts +440 -0
- package/src/debug/log-formatting.ts +58 -0
- package/src/debug/log-viewer.ts +908 -0
- package/src/debug/profiler.ts +158 -0
- package/src/debug/report-bundle.ts +365 -0
- package/src/debug/system-info.ts +107 -0
- package/src/discovery/agents-md.ts +67 -0
- package/src/discovery/agents.ts +219 -0
- package/src/discovery/builtin.ts +851 -0
- package/src/discovery/claude-plugins.ts +297 -0
- package/src/discovery/claude.ts +543 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +535 -0
- package/src/discovery/cursor.ts +220 -0
- package/src/discovery/gemini.ts +396 -0
- package/src/discovery/github.ts +118 -0
- package/src/discovery/helpers.ts +934 -0
- package/src/discovery/index.ts +79 -0
- package/src/discovery/language.ts +46 -0
- package/src/discovery/mcp-json.ts +171 -0
- package/src/discovery/opencode.ts +393 -0
- package/src/discovery/plugin-dir-roots.ts +28 -0
- package/src/discovery/ssh.ts +153 -0
- package/src/discovery/substitute-plugin-root.ts +29 -0
- package/src/discovery/vscode.ts +105 -0
- package/src/discovery/windsurf.ts +147 -0
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +481 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +762 -0
- package/src/edit/modes/hashline.ts +1366 -0
- package/src/edit/modes/patch.ts +1791 -0
- package/src/edit/modes/replace.ts +1115 -0
- package/src/edit/normalize.ts +375 -0
- package/src/edit/renderer.ts +627 -0
- package/src/exa/factory.ts +61 -0
- package/src/exa/index.ts +27 -0
- package/src/exa/mcp-client.ts +293 -0
- package/src/exa/render.ts +232 -0
- package/src/exa/researcher.ts +47 -0
- package/src/exa/search.ts +86 -0
- package/src/exa/types.ts +166 -0
- package/src/exa/websets.ts +247 -0
- package/src/exec/bash-executor.ts +332 -0
- package/src/exec/exec.ts +53 -0
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +48 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/index.ts +161 -0
- package/src/export/html/template.css +1001 -0
- package/src/export/html/template.generated.ts +2 -0
- package/src/export/html/template.html +46 -0
- package/src/export/html/template.js +1950 -0
- package/src/export/html/template.macro.ts +24 -0
- package/src/export/html/vendor/highlight.min.js +1213 -0
- package/src/export/html/vendor/marked.min.js +6 -0
- package/src/export/ttsr.ts +434 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +25 -0
- package/src/extensibility/custom-commands/bundled/review/index.ts +456 -0
- package/src/extensibility/custom-commands/index.ts +2 -0
- package/src/extensibility/custom-commands/loader.ts +236 -0
- package/src/extensibility/custom-commands/types.ts +111 -0
- package/src/extensibility/custom-tools/index.ts +7 -0
- package/src/extensibility/custom-tools/loader.ts +238 -0
- package/src/extensibility/custom-tools/types.ts +251 -0
- package/src/extensibility/custom-tools/wrapper.ts +47 -0
- package/src/extensibility/extensions/index.ts +15 -0
- package/src/extensibility/extensions/loader.ts +572 -0
- package/src/extensibility/extensions/runner.ts +848 -0
- package/src/extensibility/extensions/types.ts +1394 -0
- package/src/extensibility/extensions/wrapper.ts +191 -0
- package/src/extensibility/hooks/index.ts +5 -0
- package/src/extensibility/hooks/loader.ts +255 -0
- package/src/extensibility/hooks/runner.ts +425 -0
- package/src/extensibility/hooks/tool-wrapper.ts +108 -0
- package/src/extensibility/hooks/types.ts +826 -0
- package/src/extensibility/plugins/doctor.ts +66 -0
- package/src/extensibility/plugins/git-url.ts +281 -0
- package/src/extensibility/plugins/index.ts +9 -0
- package/src/extensibility/plugins/installer.ts +192 -0
- package/src/extensibility/plugins/loader.ts +267 -0
- package/src/extensibility/plugins/manager.ts +731 -0
- package/src/extensibility/plugins/marketplace/cache.ts +136 -0
- package/src/extensibility/plugins/marketplace/fetcher.ts +318 -0
- package/src/extensibility/plugins/marketplace/index.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +781 -0
- package/src/extensibility/plugins/marketplace/prerequisites.ts +255 -0
- package/src/extensibility/plugins/marketplace/registry.ts +196 -0
- package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
- package/src/extensibility/plugins/marketplace/types.ts +201 -0
- package/src/extensibility/plugins/parser.ts +105 -0
- package/src/extensibility/plugins/types.ts +194 -0
- package/src/extensibility/skills.ts +269 -0
- package/src/extensibility/slash-commands.ts +246 -0
- package/src/extensibility/tool-proxy.ts +25 -0
- package/src/extensibility/utils.ts +38 -0
- package/src/index.ts +61 -0
- package/src/internal-urls/agent-protocol.ts +136 -0
- package/src/internal-urls/api-catalog-resolve.ts +495 -0
- package/src/internal-urls/api-catalog-types.ts +94 -0
- package/src/internal-urls/api-spec-resolve.ts +1036 -0
- package/src/internal-urls/api-spec-types.ts +239 -0
- package/src/internal-urls/artifact-protocol.ts +97 -0
- package/src/internal-urls/branding-index.generated.ts +88 -0
- package/src/internal-urls/build-info-runtime.ts +302 -0
- package/src/internal-urls/build-info.generated.ts +33 -0
- package/src/internal-urls/computer-profile.ts +692 -0
- package/src/internal-urls/console-catalog-types.ts +21 -0
- package/src/internal-urls/console-catalog.generated.ts +532 -0
- package/src/internal-urls/console-field-metadata-types.ts +59 -0
- package/src/internal-urls/console-field-metadata.generated.ts +4841 -0
- package/src/internal-urls/console-resolve.ts +178 -0
- package/src/internal-urls/docs-index.generated.ts +698 -0
- package/src/internal-urls/extension-api.md +147 -0
- package/src/internal-urls/index.ts +46 -0
- package/src/internal-urls/jobs-protocol.ts +119 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/local-protocol.ts +206 -0
- package/src/internal-urls/mcp-protocol.ts +156 -0
- package/src/internal-urls/memory-protocol.ts +133 -0
- package/src/internal-urls/parse.ts +72 -0
- package/src/internal-urls/profile-collectors.ts +106 -0
- package/src/internal-urls/router.ts +55 -0
- package/src/internal-urls/rule-protocol.ts +55 -0
- package/src/internal-urls/skill-protocol.ts +111 -0
- package/src/internal-urls/terraform-index.generated.ts +1852 -0
- package/src/internal-urls/terraform-resolve.ts +184 -0
- package/src/internal-urls/terraform-types.ts +45 -0
- package/src/internal-urls/types.ts +52 -0
- package/src/internal-urls/user-profile.ts +363 -0
- package/src/internal-urls/xcsh-protocol.ts +644 -0
- package/src/ipy/cancellation.ts +28 -0
- package/src/ipy/executor.ts +1155 -0
- package/src/ipy/gateway-coordinator.ts +424 -0
- package/src/ipy/kernel.ts +1091 -0
- package/src/ipy/modules.ts +144 -0
- package/src/ipy/prelude.py +849 -0
- package/src/ipy/prelude.ts +3 -0
- package/src/ipy/runtime.ts +221 -0
- package/src/locales/ar.json +512 -0
- package/src/locales/de.json +512 -0
- package/src/locales/en.json +549 -0
- package/src/locales/es.json +512 -0
- package/src/locales/fr.json +512 -0
- package/src/locales/hi.json +512 -0
- package/src/locales/index.ts +29 -0
- package/src/locales/it.json +512 -0
- package/src/locales/ja.json +512 -0
- package/src/locales/ko.json +512 -0
- package/src/locales/pt-br.json +512 -0
- package/src/locales/th.json +512 -0
- package/src/locales/zh-cn.json +512 -0
- package/src/locales/zh-tw.json +512 -0
- package/src/lsp/client.ts +941 -0
- package/src/lsp/clients/biome-client.ts +202 -0
- package/src/lsp/clients/index.ts +50 -0
- package/src/lsp/clients/lsp-linter-client.ts +93 -0
- package/src/lsp/clients/swiftlint-client.ts +120 -0
- package/src/lsp/config.ts +418 -0
- package/src/lsp/defaults.json +999 -0
- package/src/lsp/edits.ts +109 -0
- package/src/lsp/index.ts +1746 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/render.ts +692 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +444 -0
- package/src/lsp/utils.ts +691 -0
- package/src/main.ts +944 -0
- package/src/mcp/client.ts +482 -0
- package/src/mcp/config-writer.ts +225 -0
- package/src/mcp/config.ts +365 -0
- package/src/mcp/discoverable-tool-metadata.ts +202 -0
- package/src/mcp/index.ts +29 -0
- package/src/mcp/json-rpc.ts +84 -0
- package/src/mcp/loader.ts +124 -0
- package/src/mcp/manager.ts +1152 -0
- package/src/mcp/oauth-discovery.ts +349 -0
- package/src/mcp/oauth-flow.ts +387 -0
- package/src/mcp/render.ts +157 -0
- package/src/mcp/smithery-auth.ts +104 -0
- package/src/mcp/smithery-connect.ts +145 -0
- package/src/mcp/smithery-registry.ts +477 -0
- package/src/mcp/tool-bridge.ts +417 -0
- package/src/mcp/tool-cache.ts +117 -0
- package/src/mcp/transports/http.ts +475 -0
- package/src/mcp/transports/index.ts +6 -0
- package/src/mcp/transports/stdio.ts +325 -0
- package/src/mcp/types.ts +423 -0
- package/src/memories/index.ts +1115 -0
- package/src/memories/storage.ts +577 -0
- package/src/modes/acp/acp-agent.ts +1361 -0
- package/src/modes/acp/acp-event-mapper.ts +538 -0
- package/src/modes/acp/acp-mode.ts +15 -0
- package/src/modes/acp/index.ts +2 -0
- package/src/modes/components/agent-dashboard.ts +1124 -0
- package/src/modes/components/assistant-message.ts +213 -0
- package/src/modes/components/bash-execution.ts +345 -0
- package/src/modes/components/bordered-loader.ts +41 -0
- package/src/modes/components/branch-summary-message.ts +45 -0
- package/src/modes/components/btw-panel.ts +103 -0
- package/src/modes/components/compaction-summary-message.ts +51 -0
- package/src/modes/components/context-add-wizard.ts +534 -0
- package/src/modes/components/countdown-timer.ts +75 -0
- package/src/modes/components/custom-editor.ts +238 -0
- package/src/modes/components/custom-message.ts +91 -0
- package/src/modes/components/diff.ts +210 -0
- package/src/modes/components/dynamic-border.ts +25 -0
- package/src/modes/components/extensions/extension-dashboard.ts +326 -0
- package/src/modes/components/extensions/extension-list.ts +492 -0
- package/src/modes/components/extensions/index.ts +9 -0
- package/src/modes/components/extensions/inspector-panel.ts +317 -0
- package/src/modes/components/extensions/state-manager.ts +587 -0
- package/src/modes/components/extensions/types.ts +191 -0
- package/src/modes/components/footer.ts +262 -0
- package/src/modes/components/gutter-block.ts +315 -0
- package/src/modes/components/history-search.ts +158 -0
- package/src/modes/components/hook-editor.ts +151 -0
- package/src/modes/components/hook-input.ts +81 -0
- package/src/modes/components/hook-message.ts +100 -0
- package/src/modes/components/hook-selector.ts +192 -0
- package/src/modes/components/index.ts +36 -0
- package/src/modes/components/keybinding-hints.ts +65 -0
- package/src/modes/components/login-dialog.ts +164 -0
- package/src/modes/components/mcp-add-wizard.ts +1314 -0
- package/src/modes/components/model-selector.ts +946 -0
- package/src/modes/components/oauth-selector.ts +211 -0
- package/src/modes/components/plugin-selector.ts +95 -0
- package/src/modes/components/plugin-settings.ts +477 -0
- package/src/modes/components/plugins/index.ts +5 -0
- package/src/modes/components/plugins/plugin-dashboard.ts +523 -0
- package/src/modes/components/plugins/plugin-inspector-pane.ts +84 -0
- package/src/modes/components/plugins/plugin-list-pane.ts +109 -0
- package/src/modes/components/plugins/state-manager.ts +229 -0
- package/src/modes/components/plugins/types.ts +51 -0
- package/src/modes/components/plugins/utils.ts +6 -0
- package/src/modes/components/python-execution.ts +244 -0
- package/src/modes/components/queue-mode-selector.ts +56 -0
- package/src/modes/components/read-tool-group.ts +137 -0
- package/src/modes/components/session-observer-overlay.ts +483 -0
- package/src/modes/components/session-selector.ts +343 -0
- package/src/modes/components/settings-defs.ts +568 -0
- package/src/modes/components/settings-selector.ts +641 -0
- package/src/modes/components/show-images-selector.ts +45 -0
- package/src/modes/components/skill-message.ts +90 -0
- package/src/modes/components/status-line/context-gradient.ts +64 -0
- package/src/modes/components/status-line/git-utils.ts +42 -0
- package/src/modes/components/status-line/hex-ansi.ts +29 -0
- package/src/modes/components/status-line/index.ts +4 -0
- package/src/modes/components/status-line/presets.ts +174 -0
- package/src/modes/components/status-line/segments.ts +494 -0
- package/src/modes/components/status-line/separators.ts +55 -0
- package/src/modes/components/status-line/token-rate.ts +66 -0
- package/src/modes/components/status-line/types.ts +96 -0
- package/src/modes/components/status-line-segment-editor.ts +361 -0
- package/src/modes/components/status-line.ts +729 -0
- package/src/modes/components/theme-selector.ts +63 -0
- package/src/modes/components/thinking-selector.ts +52 -0
- package/src/modes/components/todo-reminder.ts +54 -0
- package/src/modes/components/tool-execution.ts +781 -0
- package/src/modes/components/tree-selector.ts +908 -0
- package/src/modes/components/ttsr-notification.ts +80 -0
- package/src/modes/components/user-message-selector.ts +141 -0
- package/src/modes/components/user-message.ts +100 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome-checks.ts +313 -0
- package/src/modes/components/welcome.ts +270 -0
- package/src/modes/controllers/btw-controller.ts +193 -0
- package/src/modes/controllers/chord-routing.ts +37 -0
- package/src/modes/controllers/command-controller.ts +1293 -0
- package/src/modes/controllers/context-command-controller.ts +61 -0
- package/src/modes/controllers/event-controller.ts +743 -0
- package/src/modes/controllers/extension-ui-controller.ts +983 -0
- package/src/modes/controllers/input-controller.ts +877 -0
- package/src/modes/controllers/mcp-command-controller.ts +1940 -0
- package/src/modes/controllers/selector-controller.ts +1292 -0
- package/src/modes/controllers/ssh-command-controller.ts +421 -0
- package/src/modes/index.ts +34 -0
- package/src/modes/interactive-mode.ts +1781 -0
- package/src/modes/oauth-manual-input.ts +42 -0
- package/src/modes/print-mode.ts +196 -0
- package/src/modes/prompt-action-autocomplete.ts +231 -0
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +762 -0
- package/src/modes/rpc/rpc-mode.ts +877 -0
- package/src/modes/rpc/rpc-types.ts +322 -0
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/shared.ts +30 -0
- package/src/modes/theme/defaults/index.ts +7 -0
- package/src/modes/theme/defaults/xcsh-dark.json +113 -0
- package/src/modes/theme/defaults/xcsh-light.json +115 -0
- package/src/modes/theme/mermaid-cache.ts +151 -0
- package/src/modes/theme/mermaid-palette.ts +39 -0
- package/src/modes/theme/theme-schema.json +553 -0
- package/src/modes/theme/theme.ts +2563 -0
- package/src/modes/types.ts +273 -0
- package/src/modes/utils/hotkeys-markdown.ts +60 -0
- package/src/modes/utils/keybinding-matchers.ts +30 -0
- package/src/modes/utils/read-group-outcome-aggregator.ts +55 -0
- package/src/modes/utils/sanitize-error-message.ts +60 -0
- package/src/modes/utils/tools-markdown.ts +28 -0
- package/src/modes/utils/ui-helpers.ts +734 -0
- package/src/plan-mode/approved-plan.ts +55 -0
- package/src/plan-mode/state.ts +6 -0
- package/src/priority.json +37 -0
- package/src/prompts/agents/designer.md +67 -0
- package/src/prompts/agents/explore.md +61 -0
- package/src/prompts/agents/frontmatter.md +10 -0
- package/src/prompts/agents/init.md +36 -0
- package/src/prompts/agents/librarian.md +121 -0
- package/src/prompts/agents/plan.md +49 -0
- package/src/prompts/agents/reviewer.md +127 -0
- package/src/prompts/agents/task.md +16 -0
- package/src/prompts/ci-green-request.md +35 -0
- package/src/prompts/compaction/branch-summary-context.md +5 -0
- package/src/prompts/compaction/branch-summary-preamble.md +2 -0
- package/src/prompts/compaction/branch-summary.md +30 -0
- package/src/prompts/compaction/compaction-short-summary.md +9 -0
- package/src/prompts/compaction/compaction-summary-context.md +5 -0
- package/src/prompts/compaction/compaction-summary.md +38 -0
- package/src/prompts/compaction/compaction-turn-prefix.md +17 -0
- package/src/prompts/compaction/compaction-update-summary.md +45 -0
- package/src/prompts/memories/consolidation.md +30 -0
- package/src/prompts/memories/read-path.md +11 -0
- package/src/prompts/memories/stage_one_input.md +6 -0
- package/src/prompts/memories/stage_one_system.md +21 -0
- package/src/prompts/review-request.md +70 -0
- package/src/prompts/system/agent-creation-architect.md +65 -0
- package/src/prompts/system/agent-creation-user.md +6 -0
- package/src/prompts/system/auto-handoff-threshold-focus.md +1 -0
- package/src/prompts/system/btw-user.md +8 -0
- package/src/prompts/system/commit-message-system.md +2 -0
- package/src/prompts/system/custom-system-prompt.md +82 -0
- package/src/prompts/system/eager-todo.md +13 -0
- package/src/prompts/system/file-operations.md +10 -0
- package/src/prompts/system/handoff-document.md +48 -0
- package/src/prompts/system/plan-mode-active.md +114 -0
- package/src/prompts/system/plan-mode-approved.md +23 -0
- package/src/prompts/system/plan-mode-reference.md +14 -0
- package/src/prompts/system/plan-mode-subagent.md +34 -0
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
- package/src/prompts/system/subagent-submit-reminder.md +7 -0
- package/src/prompts/system/subagent-system-prompt.md +36 -0
- package/src/prompts/system/subagent-user-prompt.md +12 -0
- package/src/prompts/system/summarization-system.md +3 -0
- package/src/prompts/system/system-prompt.md +698 -0
- package/src/prompts/system/title-system.md +2 -0
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/prompts/system/web-search.md +28 -0
- package/src/prompts/tools/ask.md +28 -0
- package/src/prompts/tools/ast-edit.md +48 -0
- package/src/prompts/tools/ast-grep.md +54 -0
- package/src/prompts/tools/async-result.md +5 -0
- package/src/prompts/tools/bash.md +67 -0
- package/src/prompts/tools/browser.md +26 -0
- package/src/prompts/tools/calculator.md +10 -0
- package/src/prompts/tools/cancel-job.md +5 -0
- package/src/prompts/tools/catalog-workflow-runner.md +13 -0
- package/src/prompts/tools/checkpoint.md +16 -0
- package/src/prompts/tools/chunk-edit.md +279 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/display-image.md +23 -0
- package/src/prompts/tools/exit-plan-mode.md +41 -0
- package/src/prompts/tools/find.md +26 -0
- package/src/prompts/tools/gemini-image.md +7 -0
- package/src/prompts/tools/grep.md +29 -0
- package/src/prompts/tools/hashline.md +131 -0
- package/src/prompts/tools/inspect-image-system.md +20 -0
- package/src/prompts/tools/inspect-image.md +32 -0
- package/src/prompts/tools/lsp.md +33 -0
- package/src/prompts/tools/patch.md +77 -0
- package/src/prompts/tools/poll.md +5 -0
- package/src/prompts/tools/python.md +60 -0
- package/src/prompts/tools/read-chunk.md +35 -0
- package/src/prompts/tools/read.md +63 -0
- package/src/prompts/tools/render-mermaid.md +9 -0
- package/src/prompts/tools/replace.md +36 -0
- package/src/prompts/tools/resolve.md +8 -0
- package/src/prompts/tools/rewind.md +13 -0
- package/src/prompts/tools/search-tool-bm25.md +34 -0
- package/src/prompts/tools/ssh.md +44 -0
- package/src/prompts/tools/task-summary.md +28 -0
- package/src/prompts/tools/task.md +131 -0
- package/src/prompts/tools/todo-write.md +89 -0
- package/src/prompts/tools/vim.md +98 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +14 -0
- package/src/prompts/tools/xcsh-api.md +283 -0
- package/src/resource-management/index.ts +42 -0
- package/src/sdk.ts +2123 -0
- package/src/secrets/index.ts +146 -0
- package/src/secrets/obfuscator.ts +290 -0
- package/src/secrets/regex.ts +21 -0
- package/src/services/context-env.ts +102 -0
- package/src/services/xcsh-api-client.ts +230 -0
- package/src/services/xcsh-context-command.ts +803 -0
- package/src/services/xcsh-context-display.ts +49 -0
- package/src/services/xcsh-context-indicators.ts +17 -0
- package/src/services/xcsh-context-segment.ts +39 -0
- package/src/services/xcsh-context.ts +1410 -0
- package/src/services/xcsh-env.ts +107 -0
- package/src/services/xcsh-knowledge.ts +177 -0
- package/src/services/xcsh-table.ts +164 -0
- package/src/session/agent-session.ts +6690 -0
- package/src/session/agent-storage.ts +451 -0
- package/src/session/artifacts.ts +132 -0
- package/src/session/auth-storage.ts +17 -0
- package/src/session/blob-store.ts +135 -0
- package/src/session/compaction/branch-summarization.ts +322 -0
- package/src/session/compaction/compaction.ts +1388 -0
- package/src/session/compaction/index.ts +7 -0
- package/src/session/compaction/pruning.ts +91 -0
- package/src/session/compaction/utils.ts +184 -0
- package/src/session/history-storage.ts +253 -0
- package/src/session/messages.ts +597 -0
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-manager.ts +2816 -0
- package/src/session/session-storage.ts +370 -0
- package/src/session/streaming-output.ts +770 -0
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +1614 -0
- package/src/slash-commands/export-command.ts +178 -0
- package/src/slash-commands/marketplace-install-parser.ts +99 -0
- package/src/slash-commands/resource-commands.ts +250 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +445 -0
- package/src/ssh/ssh-executor.ts +131 -0
- package/src/ssh/sshfs-mount.ts +137 -0
- package/src/ssh/utils.ts +8 -0
- package/src/stt/downloader.ts +71 -0
- package/src/stt/index.ts +3 -0
- package/src/stt/recorder.ts +351 -0
- package/src/stt/setup.ts +52 -0
- package/src/stt/stt-controller.ts +160 -0
- package/src/stt/transcribe.py +70 -0
- package/src/stt/transcriber.ts +91 -0
- package/src/system-prompt.ts +688 -0
- package/src/task/agents.ts +166 -0
- package/src/task/commands.ts +131 -0
- package/src/task/discovery.ts +126 -0
- package/src/task/executor.ts +1324 -0
- package/src/task/index.ts +1197 -0
- package/src/task/isolation-backend.ts +72 -0
- package/src/task/name-generator.ts +1577 -0
- package/src/task/output-manager.ts +107 -0
- package/src/task/parallel.ts +116 -0
- package/src/task/render.ts +1035 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/template.ts +33 -0
- package/src/task/types.ts +230 -0
- package/src/task/worktree.ts +580 -0
- package/src/task/xcsh-command.ts +26 -0
- package/src/thinking.ts +87 -0
- package/src/tools/action-renderer.ts +198 -0
- package/src/tools/archive-reader.ts +315 -0
- package/src/tools/ask.ts +791 -0
- package/src/tools/ast-edit.ts +492 -0
- package/src/tools/ast-grep.ts +453 -0
- package/src/tools/auto-generated-guard.ts +305 -0
- package/src/tools/bash-interactive.ts +383 -0
- package/src/tools/bash-interceptor.ts +67 -0
- package/src/tools/bash-normalize.ts +107 -0
- package/src/tools/bash-skill-urls.ts +246 -0
- package/src/tools/bash.ts +878 -0
- package/src/tools/browser-renderer.ts +129 -0
- package/src/tools/browser.ts +1366 -0
- package/src/tools/calculator.ts +543 -0
- package/src/tools/cancel-job.ts +100 -0
- package/src/tools/catalog-workflow-runner.ts +718 -0
- package/src/tools/checkpoint.ts +133 -0
- package/src/tools/context.ts +39 -0
- package/src/tools/debug.ts +1004 -0
- package/src/tools/display-image-renderer.ts +96 -0
- package/src/tools/display-image.ts +109 -0
- package/src/tools/exit-plan-mode.ts +101 -0
- package/src/tools/fetch.ts +1462 -0
- package/src/tools/find.ts +437 -0
- package/src/tools/fs-cache-invalidation.ts +33 -0
- package/src/tools/gemini-image.ts +1004 -0
- package/src/tools/grep.ts +705 -0
- package/src/tools/index.ts +442 -0
- package/src/tools/inspect-image-renderer.ts +101 -0
- package/src/tools/inspect-image.ts +176 -0
- package/src/tools/json-tree.ts +266 -0
- package/src/tools/jtd-to-json-schema.ts +199 -0
- package/src/tools/jtd-to-typescript.ts +136 -0
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/list-limit.ts +40 -0
- package/src/tools/mermaid-renderer.ts +127 -0
- package/src/tools/notebook.ts +288 -0
- package/src/tools/output-meta.ts +588 -0
- package/src/tools/path-utils.ts +560 -0
- package/src/tools/plan-mode-guard.ts +41 -0
- package/src/tools/poll-tool.ts +177 -0
- package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
- package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
- package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
- package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
- package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
- package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
- package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
- package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
- package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
- package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
- package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
- package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
- package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
- package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
- package/src/tools/python.ts +1184 -0
- package/src/tools/read.ts +1591 -0
- package/src/tools/render-mermaid.ts +93 -0
- package/src/tools/render-utils.ts +677 -0
- package/src/tools/renderers.ts +82 -0
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +196 -0
- package/src/tools/review.ts +238 -0
- package/src/tools/search-tool-bm25.ts +288 -0
- package/src/tools/sqlite-reader.ts +623 -0
- package/src/tools/ssh.ts +322 -0
- package/src/tools/submit-result.ts +229 -0
- package/src/tools/todo-render.ts +33 -0
- package/src/tools/todo-write.ts +440 -0
- package/src/tools/tool-errors.ts +62 -0
- package/src/tools/tool-result.ts +93 -0
- package/src/tools/tool-timeouts.ts +30 -0
- package/src/tools/vim.ts +981 -0
- package/src/tools/write.ts +671 -0
- package/src/tools/xcsh-api-renderer.ts +402 -0
- package/src/tools/xcsh-api.ts +887 -0
- package/src/tui/code-cell.ts +101 -0
- package/src/tui/file-list.ts +47 -0
- package/src/tui/index.ts +11 -0
- package/src/tui/output-block.ts +175 -0
- package/src/tui/status-line.ts +39 -0
- package/src/tui/tree-list.ts +84 -0
- package/src/tui/types.ts +15 -0
- package/src/tui/utils.ts +103 -0
- package/src/utils/changelog.ts +98 -0
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/command-args.ts +76 -0
- package/src/utils/commit-message-generator.ts +137 -0
- package/src/utils/edit-mode.ts +50 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +65 -0
- package/src/utils/file-display-mode.ts +37 -0
- package/src/utils/file-mentions.ts +376 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/git.ts +1495 -0
- package/src/utils/gitstatus.ts +140 -0
- package/src/utils/image-convert.ts +27 -0
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/image-passthrough.ts +118 -0
- package/src/utils/image-resize.ts +272 -0
- package/src/utils/image-viewer.ts +17 -0
- package/src/utils/lang-from-path.ts +239 -0
- package/src/utils/markit.ts +81 -0
- package/src/utils/open.ts +20 -0
- package/src/utils/session-color.ts +55 -0
- package/src/utils/shell-snapshot.ts +188 -0
- package/src/utils/title-generator.ts +193 -0
- package/src/utils/tool-choice.ts +28 -0
- package/src/utils/tools-manager.ts +356 -0
- package/src/vim/buffer.ts +309 -0
- package/src/vim/commands.ts +382 -0
- package/src/vim/engine.ts +2426 -0
- package/src/vim/parser.ts +151 -0
- package/src/vim/render.ts +252 -0
- package/src/vim/types.ts +197 -0
- package/src/web/kagi.ts +174 -0
- package/src/web/parallel.ts +346 -0
- package/src/web/scrapers/artifacthub.ts +207 -0
- package/src/web/scrapers/arxiv.ts +83 -0
- package/src/web/scrapers/aur.ts +162 -0
- package/src/web/scrapers/biorxiv.ts +133 -0
- package/src/web/scrapers/bluesky.ts +262 -0
- package/src/web/scrapers/brew.ts +172 -0
- package/src/web/scrapers/cheatsh.ts +68 -0
- package/src/web/scrapers/chocolatey.ts +196 -0
- package/src/web/scrapers/choosealicense.ts +95 -0
- package/src/web/scrapers/cisa-kev.ts +87 -0
- package/src/web/scrapers/clojars.ts +154 -0
- package/src/web/scrapers/coingecko.ts +177 -0
- package/src/web/scrapers/crates-io.ts +110 -0
- package/src/web/scrapers/crossref.ts +136 -0
- package/src/web/scrapers/devto.ts +147 -0
- package/src/web/scrapers/discogs.ts +306 -0
- package/src/web/scrapers/discourse.ts +197 -0
- package/src/web/scrapers/dockerhub.ts +138 -0
- package/src/web/scrapers/docs-rs.ts +653 -0
- package/src/web/scrapers/fdroid.ts +134 -0
- package/src/web/scrapers/firefox-addons.ts +191 -0
- package/src/web/scrapers/flathub.ts +223 -0
- package/src/web/scrapers/github-gist.ts +58 -0
- package/src/web/scrapers/github.ts +452 -0
- package/src/web/scrapers/gitlab.ts +401 -0
- package/src/web/scrapers/go-pkg.ts +266 -0
- package/src/web/scrapers/hackage.ts +140 -0
- package/src/web/scrapers/hackernews.ts +189 -0
- package/src/web/scrapers/hex.ts +105 -0
- package/src/web/scrapers/huggingface.ts +321 -0
- package/src/web/scrapers/iacr.ts +89 -0
- package/src/web/scrapers/index.ts +252 -0
- package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
- package/src/web/scrapers/lemmy.ts +203 -0
- package/src/web/scrapers/lobsters.ts +175 -0
- package/src/web/scrapers/mastodon.ts +292 -0
- package/src/web/scrapers/maven.ts +138 -0
- package/src/web/scrapers/mdn.ts +169 -0
- package/src/web/scrapers/metacpan.ts +222 -0
- package/src/web/scrapers/musicbrainz.ts +250 -0
- package/src/web/scrapers/npm.ts +98 -0
- package/src/web/scrapers/nuget.ts +183 -0
- package/src/web/scrapers/nvd.ts +222 -0
- package/src/web/scrapers/ollama.ts +239 -0
- package/src/web/scrapers/open-vsx.ts +106 -0
- package/src/web/scrapers/opencorporates.ts +292 -0
- package/src/web/scrapers/openlibrary.ts +336 -0
- package/src/web/scrapers/orcid.ts +286 -0
- package/src/web/scrapers/osv.ts +176 -0
- package/src/web/scrapers/packagist.ts +160 -0
- package/src/web/scrapers/pub-dev.ts +143 -0
- package/src/web/scrapers/pubmed.ts +211 -0
- package/src/web/scrapers/pypi.ts +112 -0
- package/src/web/scrapers/rawg.ts +110 -0
- package/src/web/scrapers/readthedocs.ts +120 -0
- package/src/web/scrapers/reddit.ts +95 -0
- package/src/web/scrapers/repology.ts +251 -0
- package/src/web/scrapers/rfc.ts +201 -0
- package/src/web/scrapers/rubygems.ts +103 -0
- package/src/web/scrapers/searchcode.ts +190 -0
- package/src/web/scrapers/sec-edgar.ts +261 -0
- package/src/web/scrapers/semantic-scholar.ts +171 -0
- package/src/web/scrapers/snapcraft.ts +187 -0
- package/src/web/scrapers/sourcegraph.ts +336 -0
- package/src/web/scrapers/spdx.ts +108 -0
- package/src/web/scrapers/spotify.ts +198 -0
- package/src/web/scrapers/stackoverflow.ts +120 -0
- package/src/web/scrapers/terraform.ts +277 -0
- package/src/web/scrapers/tldr.ts +47 -0
- package/src/web/scrapers/twitter.ts +93 -0
- package/src/web/scrapers/types.ts +267 -0
- package/src/web/scrapers/utils.ts +109 -0
- package/src/web/scrapers/vimeo.ts +133 -0
- package/src/web/scrapers/vscode-marketplace.ts +187 -0
- package/src/web/scrapers/w3c.ts +156 -0
- package/src/web/scrapers/wikidata.ts +344 -0
- package/src/web/scrapers/wikipedia.ts +84 -0
- package/src/web/scrapers/youtube.ts +313 -0
- package/src/web/search/errors.ts +120 -0
- package/src/web/search/index.ts +346 -0
- package/src/web/search/params.ts +129 -0
- package/src/web/search/provider.ts +97 -0
- package/src/web/search/providers/anthropic.ts +339 -0
- package/src/web/search/providers/base.ts +35 -0
- package/src/web/search/providers/brave.ts +150 -0
- package/src/web/search/providers/codex.ts +402 -0
- package/src/web/search/providers/exa.ts +264 -0
- package/src/web/search/providers/firecrawl.ts +115 -0
- package/src/web/search/providers/gemini.ts +566 -0
- package/src/web/search/providers/jina.ts +99 -0
- package/src/web/search/providers/kagi.ts +70 -0
- package/src/web/search/providers/kimi.ts +157 -0
- package/src/web/search/providers/parallel.ts +63 -0
- package/src/web/search/providers/perplexity.ts +546 -0
- package/src/web/search/providers/synthetic.ts +113 -0
- package/src/web/search/providers/tavily.ts +162 -0
- package/src/web/search/providers/utils.ts +36 -0
- package/src/web/search/providers/zai.ts +311 -0
- package/src/web/search/render.ts +348 -0
- package/src/web/search/types.ts +438 -0
- package/src/web/search/utils.ts +17 -0
|
@@ -0,0 +1,1152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Manager.
|
|
3
|
+
*
|
|
4
|
+
* Discovers, connects to, and manages MCP servers.
|
|
5
|
+
* Handles tool loading and lifecycle.
|
|
6
|
+
*/
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import * as url from "node:url";
|
|
9
|
+
import { logger } from "@f5xc-salesdemos/pi-utils";
|
|
10
|
+
import type { TSchema } from "@sinclair/typebox";
|
|
11
|
+
import type { SourceMeta } from "../capability/types";
|
|
12
|
+
import { resolveConfigValue } from "../config/resolve-config-value";
|
|
13
|
+
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
14
|
+
import type { AuthStorage } from "../session/auth-storage";
|
|
15
|
+
import {
|
|
16
|
+
connectToServer,
|
|
17
|
+
disconnectServer,
|
|
18
|
+
getPrompt,
|
|
19
|
+
listPrompts,
|
|
20
|
+
listResources,
|
|
21
|
+
listResourceTemplates,
|
|
22
|
+
listTools,
|
|
23
|
+
readResource,
|
|
24
|
+
serverSupportsPrompts,
|
|
25
|
+
serverSupportsResources,
|
|
26
|
+
subscribeToResources,
|
|
27
|
+
unsubscribeFromResources,
|
|
28
|
+
} from "./client";
|
|
29
|
+
import { loadAllMCPConfigs, validateServerConfig } from "./config";
|
|
30
|
+
import { refreshMCPOAuthToken } from "./oauth-flow";
|
|
31
|
+
import type { MCPToolDetails } from "./tool-bridge";
|
|
32
|
+
import { DeferredMCPTool, MCPTool } from "./tool-bridge";
|
|
33
|
+
import type { MCPToolCache } from "./tool-cache";
|
|
34
|
+
import { HttpTransport } from "./transports/http";
|
|
35
|
+
import type {
|
|
36
|
+
MCPGetPromptResult,
|
|
37
|
+
MCPPrompt,
|
|
38
|
+
MCPRequestOptions,
|
|
39
|
+
MCPResource,
|
|
40
|
+
MCPResourceReadResult,
|
|
41
|
+
MCPResourceTemplate,
|
|
42
|
+
MCPServerConfig,
|
|
43
|
+
MCPServerConnection,
|
|
44
|
+
MCPToolDefinition,
|
|
45
|
+
} from "./types";
|
|
46
|
+
import { MCPNotificationMethods } from "./types";
|
|
47
|
+
|
|
48
|
+
type ToolLoadResult = {
|
|
49
|
+
connection: MCPServerConnection;
|
|
50
|
+
serverTools: MCPToolDefinition[];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type TrackedPromise<T> = {
|
|
54
|
+
promise: Promise<T>;
|
|
55
|
+
status: "pending" | "fulfilled" | "rejected";
|
|
56
|
+
value?: T;
|
|
57
|
+
reason?: unknown;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const STARTUP_TIMEOUT_MS = 250;
|
|
61
|
+
|
|
62
|
+
function trackPromise<T>(promise: Promise<T>): TrackedPromise<T> {
|
|
63
|
+
const tracked: TrackedPromise<T> = { promise, status: "pending" };
|
|
64
|
+
promise.then(
|
|
65
|
+
value => {
|
|
66
|
+
tracked.status = "fulfilled";
|
|
67
|
+
tracked.value = value;
|
|
68
|
+
},
|
|
69
|
+
reason => {
|
|
70
|
+
tracked.status = "rejected";
|
|
71
|
+
tracked.reason = reason;
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
return tracked;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function delay(ms: number): Promise<void> {
|
|
78
|
+
return Bun.sleep(ms);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function resolveSubscriptionPostAction(
|
|
82
|
+
notificationsEnabled: boolean,
|
|
83
|
+
currentEpoch: number,
|
|
84
|
+
subscriptionEpoch: number,
|
|
85
|
+
): "rollback" | "ignore" | "apply" {
|
|
86
|
+
if (!notificationsEnabled) return "rollback";
|
|
87
|
+
if (currentEpoch !== subscriptionEpoch) return "ignore";
|
|
88
|
+
return "apply";
|
|
89
|
+
}
|
|
90
|
+
/** Result of loading MCP tools */
|
|
91
|
+
export interface MCPLoadResult {
|
|
92
|
+
/** Loaded tools as CustomTool instances */
|
|
93
|
+
tools: CustomTool<TSchema, MCPToolDetails>[];
|
|
94
|
+
/** Connection errors by server name */
|
|
95
|
+
errors: Map<string, string>;
|
|
96
|
+
/** Connected server names */
|
|
97
|
+
connectedServers: string[];
|
|
98
|
+
/** Extracted Exa API keys from filtered MCP servers */
|
|
99
|
+
exaApiKeys: string[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Options for discovering and connecting to MCP servers */
|
|
103
|
+
export interface MCPDiscoverOptions {
|
|
104
|
+
/** Whether to load project-level config (default: true) */
|
|
105
|
+
enableProjectConfig?: boolean;
|
|
106
|
+
/** Whether to filter out Exa MCP servers (default: true) */
|
|
107
|
+
filterExa?: boolean;
|
|
108
|
+
/** Whether to filter out browser MCP servers when builtin browser tool is enabled (default: false) */
|
|
109
|
+
filterBrowser?: boolean;
|
|
110
|
+
/** Called when starting to connect to servers */
|
|
111
|
+
onConnecting?: (serverNames: string[]) => void;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* MCP Server Manager.
|
|
116
|
+
*
|
|
117
|
+
* Manages connections to MCP servers and provides tools to the agent.
|
|
118
|
+
*/
|
|
119
|
+
export class MCPManager {
|
|
120
|
+
#connections = new Map<string, MCPServerConnection>();
|
|
121
|
+
#tools: CustomTool<TSchema, MCPToolDetails>[] = [];
|
|
122
|
+
#pendingConnections = new Map<string, Promise<MCPServerConnection>>();
|
|
123
|
+
#pendingToolLoads = new Map<string, Promise<ToolLoadResult>>();
|
|
124
|
+
#sources = new Map<string, SourceMeta>();
|
|
125
|
+
#authStorage: AuthStorage | null = null;
|
|
126
|
+
#onNotification?: (serverName: string, method: string, params: unknown) => void;
|
|
127
|
+
#onToolsChanged?: (tools: CustomTool<TSchema, MCPToolDetails>[]) => void;
|
|
128
|
+
#onResourcesChanged?: (serverName: string, uri: string) => void;
|
|
129
|
+
#onPromptsChanged?: (serverName: string) => void;
|
|
130
|
+
#notificationsEnabled = false;
|
|
131
|
+
#notificationsEpoch = 0;
|
|
132
|
+
#subscribedResources = new Map<string, Set<string>>();
|
|
133
|
+
#pendingResourceRefresh = new Map<string, { connection: MCPServerConnection; promise: Promise<void> }>();
|
|
134
|
+
#pendingReconnections = new Map<string, Promise<MCPServerConnection | null>>();
|
|
135
|
+
/** Preserved configs for reconnection after connection loss. */
|
|
136
|
+
#serverConfigs = new Map<string, MCPServerConfig>();
|
|
137
|
+
/** Monotonic epoch incremented on disconnectAll to invalidate stale reconnections. */
|
|
138
|
+
#epoch = 0;
|
|
139
|
+
|
|
140
|
+
constructor(
|
|
141
|
+
private cwd: string,
|
|
142
|
+
private toolCache: MCPToolCache | null = null,
|
|
143
|
+
) {}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Set a callback to receive all server notifications.
|
|
147
|
+
*/
|
|
148
|
+
setOnNotification(handler: (serverName: string, method: string, params: unknown) => void): void {
|
|
149
|
+
this.#onNotification = handler;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Set a callback to fire when any server's tools change.
|
|
154
|
+
*/
|
|
155
|
+
setOnToolsChanged(handler: (tools: CustomTool<TSchema, MCPToolDetails>[]) => void): void {
|
|
156
|
+
this.#onToolsChanged = handler;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Set a callback to fire when any server's resources change.
|
|
161
|
+
*/
|
|
162
|
+
setOnResourcesChanged(handler: (serverName: string, uri: string) => void): void {
|
|
163
|
+
this.#onResourcesChanged = handler;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Set a callback to fire when any server's prompts change.
|
|
168
|
+
*/
|
|
169
|
+
setOnPromptsChanged(handler: (serverName: string) => void): void {
|
|
170
|
+
this.#onPromptsChanged = handler;
|
|
171
|
+
// Fire immediately for servers that already have prompts loaded
|
|
172
|
+
for (const [name, connection] of this.#connections) {
|
|
173
|
+
if (connection.prompts?.length) {
|
|
174
|
+
handler(name);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
setNotificationsEnabled(enabled: boolean): void {
|
|
180
|
+
const wasEnabled = this.#notificationsEnabled;
|
|
181
|
+
this.#notificationsEnabled = enabled;
|
|
182
|
+
if (enabled === wasEnabled) return;
|
|
183
|
+
|
|
184
|
+
this.#notificationsEpoch += 1;
|
|
185
|
+
const notificationEpoch = this.#notificationsEpoch;
|
|
186
|
+
|
|
187
|
+
if (enabled) {
|
|
188
|
+
// Subscribe to all connected servers that support it
|
|
189
|
+
for (const [name, connection] of this.#connections) {
|
|
190
|
+
if (connection.capabilities.resources?.subscribe && connection.resources) {
|
|
191
|
+
const uris = connection.resources.map(r => r.uri);
|
|
192
|
+
void subscribeToResources(connection, uris)
|
|
193
|
+
.then(() => {
|
|
194
|
+
const action = resolveSubscriptionPostAction(
|
|
195
|
+
this.#notificationsEnabled,
|
|
196
|
+
this.#notificationsEpoch,
|
|
197
|
+
notificationEpoch,
|
|
198
|
+
);
|
|
199
|
+
if (action === "rollback") {
|
|
200
|
+
void unsubscribeFromResources(connection, uris).catch(error => {
|
|
201
|
+
logger.debug("Failed to rollback stale MCP resource subscription", {
|
|
202
|
+
path: `mcp:${name}`,
|
|
203
|
+
error,
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (action === "ignore") {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
this.#subscribedResources.set(name, new Set(uris));
|
|
212
|
+
})
|
|
213
|
+
.catch(error => {
|
|
214
|
+
logger.debug("Failed to subscribe to MCP resources", { path: `mcp:${name}`, error });
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Unsubscribe from all servers
|
|
222
|
+
for (const [name, connection] of this.#connections) {
|
|
223
|
+
const uris = this.#subscribedResources.get(name);
|
|
224
|
+
if (uris && uris.size > 0) {
|
|
225
|
+
void unsubscribeFromResources(connection, Array.from(uris)).catch(error => {
|
|
226
|
+
logger.debug("Failed to unsubscribe MCP resources", { path: `mcp:${name}`, error });
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
this.#subscribedResources.clear();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Set the auth storage for resolving OAuth credentials.
|
|
235
|
+
*/
|
|
236
|
+
setAuthStorage(authStorage: AuthStorage): void {
|
|
237
|
+
this.#authStorage = authStorage;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Discover and connect to all MCP servers from .mcp.json files.
|
|
242
|
+
* Returns tools and any connection errors.
|
|
243
|
+
*/
|
|
244
|
+
async discoverAndConnect(options?: MCPDiscoverOptions): Promise<MCPLoadResult> {
|
|
245
|
+
const { configs, exaApiKeys, sources } = await loadAllMCPConfigs(this.cwd, {
|
|
246
|
+
enableProjectConfig: options?.enableProjectConfig,
|
|
247
|
+
filterExa: options?.filterExa,
|
|
248
|
+
filterBrowser: options?.filterBrowser,
|
|
249
|
+
});
|
|
250
|
+
const result = await this.connectServers(configs, sources, options?.onConnecting);
|
|
251
|
+
result.exaApiKeys = exaApiKeys;
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Connect to specific MCP servers.
|
|
257
|
+
* Connections are made in parallel for faster startup.
|
|
258
|
+
*/
|
|
259
|
+
async connectServers(
|
|
260
|
+
configs: Record<string, MCPServerConfig>,
|
|
261
|
+
sources: Record<string, SourceMeta>,
|
|
262
|
+
onConnecting?: (serverNames: string[]) => void,
|
|
263
|
+
): Promise<MCPLoadResult> {
|
|
264
|
+
type ConnectionTask = {
|
|
265
|
+
name: string;
|
|
266
|
+
config: MCPServerConfig;
|
|
267
|
+
tracked: TrackedPromise<ToolLoadResult>;
|
|
268
|
+
toolsPromise: Promise<ToolLoadResult>;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const errors = new Map<string, string>();
|
|
272
|
+
const connectedServers = new Set<string>();
|
|
273
|
+
const allTools: CustomTool<TSchema, MCPToolDetails>[] = [];
|
|
274
|
+
const reportedErrors = new Set<string>();
|
|
275
|
+
let allowBackgroundLogging = false;
|
|
276
|
+
|
|
277
|
+
// Prepare connection tasks
|
|
278
|
+
const connectionTasks: ConnectionTask[] = [];
|
|
279
|
+
|
|
280
|
+
for (const [name, config] of Object.entries(configs)) {
|
|
281
|
+
if (sources[name]) {
|
|
282
|
+
this.#sources.set(name, sources[name]);
|
|
283
|
+
const existing = this.#connections.get(name);
|
|
284
|
+
if (existing) {
|
|
285
|
+
existing._source = sources[name];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Skip if already connected
|
|
290
|
+
if (this.#connections.has(name)) {
|
|
291
|
+
connectedServers.add(name);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (
|
|
296
|
+
this.#pendingConnections.has(name) ||
|
|
297
|
+
this.#pendingToolLoads.has(name) ||
|
|
298
|
+
this.#pendingReconnections.has(name)
|
|
299
|
+
) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Validate config
|
|
304
|
+
const validationErrors = validateServerConfig(name, config);
|
|
305
|
+
if (validationErrors.length > 0) {
|
|
306
|
+
errors.set(name, validationErrors.join("; "));
|
|
307
|
+
reportedErrors.add(name);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Save config early so reconnection works even if the initial connect times out
|
|
312
|
+
// and falls back to cached/deferred tools.
|
|
313
|
+
this.#serverConfigs.set(name, config);
|
|
314
|
+
|
|
315
|
+
// Resolve auth config before connecting, but do so per-server in parallel.
|
|
316
|
+
const connectionPromise = (async () => {
|
|
317
|
+
const resolvedConfig = await this.#resolveAuthConfig(config);
|
|
318
|
+
return connectToServer(name, resolvedConfig, {
|
|
319
|
+
onNotification: (method, params) => {
|
|
320
|
+
this.#handleServerNotification(name, method, params);
|
|
321
|
+
},
|
|
322
|
+
onRequest: (method, params) => {
|
|
323
|
+
return this.#handleServerRequest(method, params);
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
})().then(
|
|
327
|
+
connection => {
|
|
328
|
+
// Store original config (without resolved tokens) to keep
|
|
329
|
+
// cache keys stable and avoid leaking rotating credentials.
|
|
330
|
+
connection.config = config;
|
|
331
|
+
this.#serverConfigs.set(name, config);
|
|
332
|
+
if (sources[name]) {
|
|
333
|
+
connection._source = sources[name];
|
|
334
|
+
}
|
|
335
|
+
if (this.#pendingConnections.get(name) === connectionPromise) {
|
|
336
|
+
this.#pendingConnections.delete(name);
|
|
337
|
+
this.#connections.set(name, connection);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Wire auth refresh for HTTP transports so 401s trigger token refresh.
|
|
341
|
+
if (connection.transport instanceof HttpTransport && config.auth?.type === "oauth") {
|
|
342
|
+
connection.transport.onAuthError = async () => {
|
|
343
|
+
const refreshed = await this.#resolveAuthConfig(config, true);
|
|
344
|
+
if (refreshed.type === "http" || refreshed.type === "sse") {
|
|
345
|
+
return refreshed.headers ?? null;
|
|
346
|
+
}
|
|
347
|
+
return null;
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Re-establish connection if the transport closes (server restart,
|
|
352
|
+
// network interruption).
|
|
353
|
+
connection.transport.onClose = () => {
|
|
354
|
+
logger.debug("MCP transport lost, triggering reconnect", { path: `mcp:${name}` });
|
|
355
|
+
void this.reconnectServer(name);
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
return connection;
|
|
359
|
+
},
|
|
360
|
+
error => {
|
|
361
|
+
if (this.#pendingConnections.get(name) === connectionPromise) {
|
|
362
|
+
this.#pendingConnections.delete(name);
|
|
363
|
+
}
|
|
364
|
+
throw error;
|
|
365
|
+
},
|
|
366
|
+
);
|
|
367
|
+
this.#pendingConnections.set(name, connectionPromise);
|
|
368
|
+
|
|
369
|
+
const toolsPromise = connectionPromise.then(async connection => {
|
|
370
|
+
const serverTools = await listTools(connection);
|
|
371
|
+
return { connection, serverTools };
|
|
372
|
+
});
|
|
373
|
+
this.#pendingToolLoads.set(name, toolsPromise);
|
|
374
|
+
|
|
375
|
+
const tracked = trackPromise(toolsPromise);
|
|
376
|
+
connectionTasks.push({ name, config, tracked, toolsPromise });
|
|
377
|
+
|
|
378
|
+
void toolsPromise
|
|
379
|
+
.then(async ({ connection, serverTools }) => {
|
|
380
|
+
if (this.#pendingToolLoads.get(name) !== toolsPromise) return;
|
|
381
|
+
this.#pendingToolLoads.delete(name);
|
|
382
|
+
const reconnect = () => this.reconnectServer(name);
|
|
383
|
+
const customTools = MCPTool.fromTools(connection, serverTools, reconnect);
|
|
384
|
+
this.#replaceServerTools(name, customTools);
|
|
385
|
+
this.#onToolsChanged?.(this.#tools);
|
|
386
|
+
void this.toolCache?.set(name, config, serverTools);
|
|
387
|
+
|
|
388
|
+
await this.#loadServerResourcesAndPrompts(name, connection);
|
|
389
|
+
})
|
|
390
|
+
.catch(error => {
|
|
391
|
+
if (this.#pendingToolLoads.get(name) !== toolsPromise) return;
|
|
392
|
+
this.#pendingToolLoads.delete(name);
|
|
393
|
+
if (!allowBackgroundLogging || reportedErrors.has(name)) return;
|
|
394
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
395
|
+
logger.error("MCP tool load failed", { path: `mcp:${name}`, error: message });
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Notify about servers we're connecting to
|
|
400
|
+
if (connectionTasks.length > 0 && onConnecting) {
|
|
401
|
+
onConnecting(connectionTasks.map(task => task.name));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (connectionTasks.length > 0) {
|
|
405
|
+
await Promise.race([
|
|
406
|
+
Promise.allSettled(connectionTasks.map(task => task.tracked.promise)),
|
|
407
|
+
delay(STARTUP_TIMEOUT_MS),
|
|
408
|
+
]);
|
|
409
|
+
|
|
410
|
+
const cachedTools = new Map<string, MCPToolDefinition[]>();
|
|
411
|
+
const pendingTasks = connectionTasks.filter(task => task.tracked.status === "pending");
|
|
412
|
+
|
|
413
|
+
if (pendingTasks.length > 0) {
|
|
414
|
+
if (this.toolCache) {
|
|
415
|
+
await Promise.all(
|
|
416
|
+
pendingTasks.map(async task => {
|
|
417
|
+
const cached = await this.toolCache?.get(task.name, task.config);
|
|
418
|
+
if (cached) {
|
|
419
|
+
cachedTools.set(task.name, cached);
|
|
420
|
+
}
|
|
421
|
+
}),
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const pendingWithoutCache = pendingTasks.filter(task => !cachedTools.has(task.name));
|
|
426
|
+
if (pendingWithoutCache.length > 0) {
|
|
427
|
+
await Promise.allSettled(pendingWithoutCache.map(task => task.tracked.promise));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
for (const task of connectionTasks) {
|
|
432
|
+
const { name } = task;
|
|
433
|
+
if (task.tracked.status === "fulfilled") {
|
|
434
|
+
const value = task.tracked.value;
|
|
435
|
+
if (!value) continue;
|
|
436
|
+
const { connection, serverTools } = value;
|
|
437
|
+
connectedServers.add(name);
|
|
438
|
+
const reconnect = () => this.reconnectServer(name);
|
|
439
|
+
allTools.push(...MCPTool.fromTools(connection, serverTools, reconnect));
|
|
440
|
+
} else if (task.tracked.status === "rejected") {
|
|
441
|
+
const message =
|
|
442
|
+
task.tracked.reason instanceof Error ? task.tracked.reason.message : String(task.tracked.reason);
|
|
443
|
+
errors.set(name, message);
|
|
444
|
+
reportedErrors.add(name);
|
|
445
|
+
} else {
|
|
446
|
+
const cached = cachedTools.get(name);
|
|
447
|
+
if (cached) {
|
|
448
|
+
const source = this.#sources.get(name);
|
|
449
|
+
const reconnect = () => this.reconnectServer(name);
|
|
450
|
+
allTools.push(
|
|
451
|
+
...DeferredMCPTool.fromTools(name, cached, () => this.waitForConnection(name), source, reconnect),
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Update cached tools
|
|
459
|
+
this.#tools = allTools;
|
|
460
|
+
allowBackgroundLogging = true;
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
tools: allTools,
|
|
464
|
+
errors,
|
|
465
|
+
connectedServers: Array.from(connectedServers),
|
|
466
|
+
exaApiKeys: [], // Will be populated by discoverAndConnect
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
#replaceServerTools(name: string, tools: CustomTool<TSchema, MCPToolDetails>[]): void {
|
|
471
|
+
this.#tools = this.#tools.filter(t => !t.name.startsWith(`mcp_${name}_`));
|
|
472
|
+
this.#tools.push(...tools);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
#triggerNotificationRefresh(serverName: string, kind: "tools" | "resources" | "prompts"): void {
|
|
476
|
+
const refresh = (() => {
|
|
477
|
+
switch (kind) {
|
|
478
|
+
case "tools":
|
|
479
|
+
return this.refreshServerTools(serverName);
|
|
480
|
+
case "resources":
|
|
481
|
+
return this.refreshServerResources(serverName);
|
|
482
|
+
case "prompts":
|
|
483
|
+
return this.refreshServerPrompts(serverName);
|
|
484
|
+
}
|
|
485
|
+
})();
|
|
486
|
+
void refresh.catch(error => {
|
|
487
|
+
logger.debug("Failed MCP notification refresh", { path: `mcp:${serverName}`, kind, error });
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
#handleServerNotification(serverName: string, method: string, params: unknown): void {
|
|
491
|
+
logger.debug("MCP notification received", { path: `mcp:${serverName}`, method });
|
|
492
|
+
|
|
493
|
+
switch (method) {
|
|
494
|
+
case MCPNotificationMethods.TOOLS_LIST_CHANGED:
|
|
495
|
+
this.#triggerNotificationRefresh(serverName, "tools");
|
|
496
|
+
break;
|
|
497
|
+
case MCPNotificationMethods.RESOURCES_LIST_CHANGED:
|
|
498
|
+
this.#triggerNotificationRefresh(serverName, "resources");
|
|
499
|
+
break;
|
|
500
|
+
case MCPNotificationMethods.RESOURCES_UPDATED: {
|
|
501
|
+
const uri = (params as { uri?: string })?.uri;
|
|
502
|
+
const subscribed = this.#subscribedResources.get(serverName);
|
|
503
|
+
if (uri && subscribed?.has(uri)) {
|
|
504
|
+
this.#onResourcesChanged?.(serverName, uri);
|
|
505
|
+
}
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
case MCPNotificationMethods.PROMPTS_LIST_CHANGED:
|
|
509
|
+
this.#triggerNotificationRefresh(serverName, "prompts");
|
|
510
|
+
break;
|
|
511
|
+
default:
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
this.#onNotification?.(serverName, method, params);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/** Handle server-to-client JSON-RPC requests (e.g. ping, roots/list). */
|
|
519
|
+
async #handleServerRequest(method: string, _params: unknown): Promise<unknown> {
|
|
520
|
+
switch (method) {
|
|
521
|
+
case "ping":
|
|
522
|
+
return {};
|
|
523
|
+
case "roots/list":
|
|
524
|
+
return this.#getRoots();
|
|
525
|
+
default:
|
|
526
|
+
throw Object.assign(new Error(`Unsupported server request: ${method}`), { code: -32601 });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
#getRoots(): { roots: Array<{ uri: string; name: string }> } {
|
|
531
|
+
return {
|
|
532
|
+
roots: [
|
|
533
|
+
{
|
|
534
|
+
uri: url.pathToFileURL(this.cwd).href,
|
|
535
|
+
name: path.basename(this.cwd),
|
|
536
|
+
},
|
|
537
|
+
],
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Get all loaded tools.
|
|
543
|
+
*/
|
|
544
|
+
getTools(): CustomTool<TSchema, MCPToolDetails>[] {
|
|
545
|
+
return this.#tools;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Get a specific connection.
|
|
550
|
+
*/
|
|
551
|
+
getConnection(name: string): MCPServerConnection | undefined {
|
|
552
|
+
return this.#connections.get(name);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Get current connection status for a server.
|
|
557
|
+
*/
|
|
558
|
+
getConnectionStatus(name: string): "connected" | "connecting" | "disconnected" {
|
|
559
|
+
if (this.#connections.has(name)) return "connected";
|
|
560
|
+
if (
|
|
561
|
+
this.#pendingConnections.has(name) ||
|
|
562
|
+
this.#pendingToolLoads.has(name) ||
|
|
563
|
+
this.#pendingReconnections.has(name)
|
|
564
|
+
)
|
|
565
|
+
return "connecting";
|
|
566
|
+
return "disconnected";
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Get the source metadata for a server.
|
|
571
|
+
*/
|
|
572
|
+
getSource(name: string): SourceMeta | undefined {
|
|
573
|
+
return this.#sources.get(name) ?? this.#connections.get(name)?._source;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Wait for a connection to complete (or fail).
|
|
578
|
+
*/
|
|
579
|
+
async waitForConnection(name: string): Promise<MCPServerConnection> {
|
|
580
|
+
const connection = this.#connections.get(name);
|
|
581
|
+
if (connection) return connection;
|
|
582
|
+
const pending = this.#pendingConnections.get(name);
|
|
583
|
+
if (pending) return pending;
|
|
584
|
+
// If a reconnection is in flight, wait for it to complete
|
|
585
|
+
const reconnecting = this.#pendingReconnections.get(name);
|
|
586
|
+
if (reconnecting) {
|
|
587
|
+
const result = await reconnecting;
|
|
588
|
+
if (result) return result;
|
|
589
|
+
}
|
|
590
|
+
throw new Error(`MCP server not connected: ${name}`);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Resolve auth and shell-command substitutions in config before connecting.
|
|
595
|
+
*/
|
|
596
|
+
async prepareConfig(config: MCPServerConfig): Promise<MCPServerConfig> {
|
|
597
|
+
return this.#resolveAuthConfig(config);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Get all connected server names.
|
|
602
|
+
*/
|
|
603
|
+
getConnectedServers(): string[] {
|
|
604
|
+
return Array.from(this.#connections.keys());
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Get all known server names (connected, connecting, or discovered).
|
|
609
|
+
*/
|
|
610
|
+
getAllServerNames(): string[] {
|
|
611
|
+
return Array.from(
|
|
612
|
+
new Set([...this.#sources.keys(), ...this.#connections.keys(), ...this.#pendingConnections.keys()]),
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Disconnect from a specific server.
|
|
618
|
+
*/
|
|
619
|
+
async disconnectServer(name: string): Promise<void> {
|
|
620
|
+
this.#pendingConnections.delete(name);
|
|
621
|
+
this.#pendingToolLoads.delete(name);
|
|
622
|
+
this.#pendingReconnections.delete(name);
|
|
623
|
+
this.#sources.delete(name);
|
|
624
|
+
this.#serverConfigs.delete(name);
|
|
625
|
+
this.#pendingResourceRefresh.delete(name);
|
|
626
|
+
|
|
627
|
+
const connection = this.#connections.get(name);
|
|
628
|
+
|
|
629
|
+
const subscribedUris = this.#subscribedResources.get(name);
|
|
630
|
+
if (subscribedUris && subscribedUris.size > 0 && connection) {
|
|
631
|
+
void unsubscribeFromResources(connection, Array.from(subscribedUris)).catch(() => {});
|
|
632
|
+
}
|
|
633
|
+
this.#subscribedResources.delete(name);
|
|
634
|
+
|
|
635
|
+
if (connection) {
|
|
636
|
+
// Detach onClose to prevent spurious reconnect from close()
|
|
637
|
+
connection.transport.onClose = undefined;
|
|
638
|
+
await disconnectServer(connection);
|
|
639
|
+
this.#connections.delete(name);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Remove tools from this server and notify consumers
|
|
643
|
+
const hadTools = this.#tools.some(t => t.name.startsWith(`mcp_${name}_`));
|
|
644
|
+
this.#tools = this.#tools.filter(t => !t.name.startsWith(`mcp_${name}_`));
|
|
645
|
+
if (hadTools) this.#onToolsChanged?.(this.#tools);
|
|
646
|
+
|
|
647
|
+
// Notify prompt consumers so stale commands are cleared
|
|
648
|
+
if (connection?.prompts?.length) this.#onPromptsChanged?.(name);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Disconnect from all servers.
|
|
653
|
+
*/
|
|
654
|
+
async disconnectAll(): Promise<void> {
|
|
655
|
+
// Invalidate any in-flight reconnection attempts that outlive this call.
|
|
656
|
+
// They captured the old epoch; after increment they'll detect staleness.
|
|
657
|
+
this.#epoch++;
|
|
658
|
+
// Detach onClose before closing to prevent spurious reconnect attempts
|
|
659
|
+
for (const conn of this.#connections.values()) {
|
|
660
|
+
conn.transport.onClose = undefined;
|
|
661
|
+
}
|
|
662
|
+
const promises = Array.from(this.#connections.values()).map(conn => disconnectServer(conn));
|
|
663
|
+
await Promise.allSettled(promises);
|
|
664
|
+
|
|
665
|
+
this.#pendingConnections.clear();
|
|
666
|
+
this.#pendingToolLoads.clear();
|
|
667
|
+
this.#pendingReconnections.clear();
|
|
668
|
+
this.#pendingResourceRefresh.clear();
|
|
669
|
+
this.#sources.clear();
|
|
670
|
+
this.#serverConfigs.clear();
|
|
671
|
+
this.#connections.clear();
|
|
672
|
+
this.#tools = [];
|
|
673
|
+
this.#subscribedResources.clear();
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Reconnect to a server after a connection failure.
|
|
678
|
+
* Tears down the stale connection, re-resolves auth, establishes a new
|
|
679
|
+
* connection, reloads tools, and notifies consumers.
|
|
680
|
+
* Concurrent calls for the same server share one reconnection attempt.
|
|
681
|
+
* Returns the new connection, or null if reconnection failed.
|
|
682
|
+
*/
|
|
683
|
+
async reconnectServer(name: string): Promise<MCPServerConnection | null> {
|
|
684
|
+
const pending = this.#pendingReconnections.get(name);
|
|
685
|
+
if (pending) return pending;
|
|
686
|
+
|
|
687
|
+
const attempt = this.#doReconnect(name);
|
|
688
|
+
this.#pendingReconnections.set(name, attempt);
|
|
689
|
+
return attempt.finally(() => this.#pendingReconnections.delete(name));
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
async #doReconnect(name: string): Promise<MCPServerConnection | null> {
|
|
693
|
+
const oldConnection = this.#connections.get(name);
|
|
694
|
+
const config = oldConnection?.config ?? this.#serverConfigs.get(name);
|
|
695
|
+
const source = this.#sources.get(name) ?? oldConnection?._source;
|
|
696
|
+
if (!config) return null;
|
|
697
|
+
|
|
698
|
+
logger.debug("MCP reconnecting", { path: `mcp:${name}` });
|
|
699
|
+
|
|
700
|
+
// Close the old transport without removing tools or notifying consumers.
|
|
701
|
+
// Tools stay available (stale) while we establish the new connection.
|
|
702
|
+
// Fire-and-forget: don't await the close — HttpTransport.close() sends a
|
|
703
|
+
// DELETE with config.timeout (30s default), and blocking here delays the
|
|
704
|
+
// reconnect loop by that amount on every server restart.
|
|
705
|
+
const reconnectEpoch = this.#epoch;
|
|
706
|
+
if (oldConnection) {
|
|
707
|
+
// Detach onClose to prevent re-entrant reconnect from the close itself
|
|
708
|
+
oldConnection.transport.onClose = undefined;
|
|
709
|
+
void oldConnection.transport.close().catch(() => {});
|
|
710
|
+
this.#connections.delete(name);
|
|
711
|
+
}
|
|
712
|
+
this.#pendingConnections.delete(name);
|
|
713
|
+
this.#pendingToolLoads.delete(name);
|
|
714
|
+
|
|
715
|
+
// Retry with backoff — the server may still be starting up.
|
|
716
|
+
const delays = [500, 1000, 2000, 4000];
|
|
717
|
+
for (let attempt = 0; attempt <= delays.length; attempt++) {
|
|
718
|
+
if (this.#epoch !== reconnectEpoch) {
|
|
719
|
+
logger.debug("MCP reconnect aborted before attempt after configuration changed", {
|
|
720
|
+
path: `mcp:${name}`,
|
|
721
|
+
storedEpoch: reconnectEpoch,
|
|
722
|
+
currentEpoch: this.#epoch,
|
|
723
|
+
});
|
|
724
|
+
return null;
|
|
725
|
+
}
|
|
726
|
+
try {
|
|
727
|
+
const connection = await this.#connectAndWireServer(name, config, source, reconnectEpoch);
|
|
728
|
+
logger.debug("MCP reconnected", { path: `mcp:${name}`, tools: connection.tools?.length ?? 0 });
|
|
729
|
+
return connection;
|
|
730
|
+
} catch (error) {
|
|
731
|
+
if (this.#epoch !== reconnectEpoch) {
|
|
732
|
+
logger.debug("MCP reconnect aborted after configuration changed", {
|
|
733
|
+
path: `mcp:${name}`,
|
|
734
|
+
storedEpoch: reconnectEpoch,
|
|
735
|
+
currentEpoch: this.#epoch,
|
|
736
|
+
});
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
741
|
+
if (attempt < delays.length) {
|
|
742
|
+
logger.debug("MCP reconnect attempt failed, retrying", {
|
|
743
|
+
path: `mcp:${name}`,
|
|
744
|
+
attempt: attempt + 1,
|
|
745
|
+
error: msg,
|
|
746
|
+
});
|
|
747
|
+
await Bun.sleep(delays[attempt]);
|
|
748
|
+
} else {
|
|
749
|
+
logger.error("MCP reconnect failed after retries", { path: `mcp:${name}`, error: msg });
|
|
750
|
+
// Don't remove stale tools — keep them in the registry so they
|
|
751
|
+
// remain selected. Calls will fail with MCP errors, which
|
|
752
|
+
// triggers the tool-level reconnect, or the user can run
|
|
753
|
+
// /mcp reconnect <name> manually.
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/** Establish a new connection to a server, wire handlers, load tools. */
|
|
761
|
+
async #connectAndWireServer(
|
|
762
|
+
name: string,
|
|
763
|
+
config: MCPServerConfig,
|
|
764
|
+
source: SourceMeta | undefined,
|
|
765
|
+
reconnectEpoch: number,
|
|
766
|
+
): Promise<MCPServerConnection> {
|
|
767
|
+
const resolvedConfig = await this.#resolveAuthConfig(config);
|
|
768
|
+
const connection = await connectToServer(name, resolvedConfig, {
|
|
769
|
+
onNotification: (method, params) => {
|
|
770
|
+
this.#handleServerNotification(name, method, params);
|
|
771
|
+
},
|
|
772
|
+
onRequest: (method, params) => {
|
|
773
|
+
return this.#handleServerRequest(method, params);
|
|
774
|
+
},
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
connection.config = config;
|
|
778
|
+
if (source) connection._source = source;
|
|
779
|
+
|
|
780
|
+
// Bail out if the server was disconnected or the manager was reset
|
|
781
|
+
// while we were connecting (e.g. /mcp reload called disconnectAll).
|
|
782
|
+
if (!this.#serverConfigs.has(name) || this.#epoch !== reconnectEpoch) {
|
|
783
|
+
await connection.transport.close().catch(() => {});
|
|
784
|
+
throw new Error(`Server "${name}" was disconnected during reconnection`);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
this.#connections.set(name, connection);
|
|
788
|
+
|
|
789
|
+
// Wire auth refresh for HTTP transports, and reconnect for any transport.
|
|
790
|
+
if (connection.transport instanceof HttpTransport && config.auth?.type === "oauth") {
|
|
791
|
+
connection.transport.onAuthError = async () => {
|
|
792
|
+
const refreshed = await this.#resolveAuthConfig(config, true);
|
|
793
|
+
if (refreshed.type === "http" || refreshed.type === "sse") {
|
|
794
|
+
return refreshed.headers ?? null;
|
|
795
|
+
}
|
|
796
|
+
return null;
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
connection.transport.onClose = () => {
|
|
800
|
+
logger.debug("MCP transport lost, triggering reconnect", { path: `mcp:${name}` });
|
|
801
|
+
void this.reconnectServer(name);
|
|
802
|
+
};
|
|
803
|
+
try {
|
|
804
|
+
const serverTools = await listTools(connection);
|
|
805
|
+
const reconnect = () => this.reconnectServer(name);
|
|
806
|
+
const customTools = MCPTool.fromTools(connection, serverTools, reconnect);
|
|
807
|
+
void this.toolCache?.set(name, config, serverTools);
|
|
808
|
+
this.#replaceServerTools(name, customTools);
|
|
809
|
+
this.#onToolsChanged?.(this.#tools);
|
|
810
|
+
void this.#loadServerResourcesAndPrompts(name, connection);
|
|
811
|
+
return connection;
|
|
812
|
+
} catch (error) {
|
|
813
|
+
// Clean up the connection to avoid zombie transports
|
|
814
|
+
connection.transport.onClose = undefined;
|
|
815
|
+
await connection.transport.close().catch(() => {});
|
|
816
|
+
this.#connections.delete(name);
|
|
817
|
+
throw error;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Best-effort loading of resources, resource subscriptions, and prompts.
|
|
823
|
+
* Shared between initial connection and reconnection.
|
|
824
|
+
*/
|
|
825
|
+
async #loadServerResourcesAndPrompts(name: string, connection: MCPServerConnection): Promise<void> {
|
|
826
|
+
if (serverSupportsResources(connection.capabilities)) {
|
|
827
|
+
try {
|
|
828
|
+
const [resources] = await Promise.all([listResources(connection), listResourceTemplates(connection)]);
|
|
829
|
+
|
|
830
|
+
if (this.#notificationsEnabled && connection.capabilities.resources?.subscribe) {
|
|
831
|
+
const uris = resources.map(r => r.uri);
|
|
832
|
+
const notificationEpoch = this.#notificationsEpoch;
|
|
833
|
+
void subscribeToResources(connection, uris)
|
|
834
|
+
.then(() => {
|
|
835
|
+
const action = resolveSubscriptionPostAction(
|
|
836
|
+
this.#notificationsEnabled,
|
|
837
|
+
this.#notificationsEpoch,
|
|
838
|
+
notificationEpoch,
|
|
839
|
+
);
|
|
840
|
+
if (action === "rollback") {
|
|
841
|
+
void unsubscribeFromResources(connection, uris).catch(error => {
|
|
842
|
+
logger.debug("Failed to rollback stale MCP resource subscription", {
|
|
843
|
+
path: `mcp:${name}`,
|
|
844
|
+
error,
|
|
845
|
+
});
|
|
846
|
+
});
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
if (action === "ignore") {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
this.#subscribedResources.set(name, new Set(uris));
|
|
853
|
+
})
|
|
854
|
+
.catch(error => {
|
|
855
|
+
logger.debug("Failed to subscribe to MCP resources", { path: `mcp:${name}`, error });
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
} catch (error) {
|
|
859
|
+
logger.debug("Failed to load MCP resources", { path: `mcp:${name}`, error });
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (serverSupportsPrompts(connection.capabilities)) {
|
|
864
|
+
try {
|
|
865
|
+
await listPrompts(connection);
|
|
866
|
+
this.#onPromptsChanged?.(name);
|
|
867
|
+
} catch (error) {
|
|
868
|
+
logger.debug("Failed to load MCP prompts", { path: `mcp:${name}`, error });
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Refresh tools from a specific server.
|
|
875
|
+
*/
|
|
876
|
+
async refreshServerTools(name: string): Promise<void> {
|
|
877
|
+
const connection = this.#connections.get(name);
|
|
878
|
+
if (!connection) return;
|
|
879
|
+
|
|
880
|
+
// Clear cached tools
|
|
881
|
+
connection.tools = undefined;
|
|
882
|
+
|
|
883
|
+
// Reload tools
|
|
884
|
+
const serverTools = await listTools(connection);
|
|
885
|
+
const reconnect = () => this.reconnectServer(name);
|
|
886
|
+
const customTools = MCPTool.fromTools(connection, serverTools, reconnect);
|
|
887
|
+
void this.toolCache?.set(name, connection.config, serverTools);
|
|
888
|
+
|
|
889
|
+
// Replace tools from this server
|
|
890
|
+
this.#replaceServerTools(name, customTools);
|
|
891
|
+
this.#onToolsChanged?.(this.#tools);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Refresh tools from all servers.
|
|
896
|
+
*/
|
|
897
|
+
async refreshAllTools(): Promise<void> {
|
|
898
|
+
const promises = Array.from(this.#connections.keys()).map(name => this.refreshServerTools(name));
|
|
899
|
+
await Promise.allSettled(promises);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Refresh resources from a specific server.
|
|
904
|
+
*/
|
|
905
|
+
async refreshServerResources(name: string): Promise<void> {
|
|
906
|
+
const connection = this.#connections.get(name);
|
|
907
|
+
if (!connection || !serverSupportsResources(connection.capabilities)) return;
|
|
908
|
+
|
|
909
|
+
const existing = this.#pendingResourceRefresh.get(name);
|
|
910
|
+
if (existing && existing.connection === connection) return existing.promise;
|
|
911
|
+
|
|
912
|
+
const doRefresh = async (): Promise<void> => {
|
|
913
|
+
// Clear cached resources
|
|
914
|
+
connection.resources = undefined;
|
|
915
|
+
connection.resourceTemplates = undefined;
|
|
916
|
+
|
|
917
|
+
// Reload
|
|
918
|
+
const [resources] = await Promise.all([listResources(connection), listResourceTemplates(connection)]);
|
|
919
|
+
if (this.#notificationsEnabled && connection.capabilities.resources?.subscribe) {
|
|
920
|
+
const newUris = new Set(resources.map(r => r.uri));
|
|
921
|
+
const oldUris = this.#subscribedResources.get(name);
|
|
922
|
+
const notificationEpoch = this.#notificationsEpoch;
|
|
923
|
+
|
|
924
|
+
// Unsubscribe URIs that were removed
|
|
925
|
+
if (oldUris) {
|
|
926
|
+
const removed = [...oldUris].filter(uri => !newUris.has(uri));
|
|
927
|
+
if (removed.length > 0) {
|
|
928
|
+
try {
|
|
929
|
+
await unsubscribeFromResources(connection, removed);
|
|
930
|
+
} catch (error) {
|
|
931
|
+
logger.debug("Failed to unsubscribe stale MCP resources", { path: `mcp:${name}`, error });
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// Subscribe to the current set and update tracking atomically
|
|
937
|
+
try {
|
|
938
|
+
const allUris = [...newUris];
|
|
939
|
+
await subscribeToResources(connection, allUris);
|
|
940
|
+
const action = resolveSubscriptionPostAction(
|
|
941
|
+
this.#notificationsEnabled,
|
|
942
|
+
this.#notificationsEpoch,
|
|
943
|
+
notificationEpoch,
|
|
944
|
+
);
|
|
945
|
+
if (action === "rollback") {
|
|
946
|
+
await unsubscribeFromResources(connection, allUris).catch(error => {
|
|
947
|
+
logger.debug("Failed to rollback stale MCP resource subscription", { path: `mcp:${name}`, error });
|
|
948
|
+
});
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if (action === "ignore") {
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
this.#subscribedResources.set(name, newUris);
|
|
955
|
+
} catch (error) {
|
|
956
|
+
logger.debug("Failed to re-subscribe to MCP resources", { path: `mcp:${name}`, error });
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
const promise = doRefresh().finally(() => {
|
|
962
|
+
const pending = this.#pendingResourceRefresh.get(name);
|
|
963
|
+
if (pending?.promise === promise) {
|
|
964
|
+
this.#pendingResourceRefresh.delete(name);
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
this.#pendingResourceRefresh.set(name, { connection, promise });
|
|
968
|
+
return promise;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Refresh prompts from a specific server.
|
|
973
|
+
*/
|
|
974
|
+
async refreshServerPrompts(name: string): Promise<void> {
|
|
975
|
+
const connection = this.#connections.get(name);
|
|
976
|
+
if (!connection || !serverSupportsPrompts(connection.capabilities)) return;
|
|
977
|
+
|
|
978
|
+
connection.prompts = undefined;
|
|
979
|
+
await listPrompts(connection);
|
|
980
|
+
|
|
981
|
+
this.#onPromptsChanged?.(name);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* Get resources and templates for a specific server.
|
|
986
|
+
*/
|
|
987
|
+
getServerResources(name: string): { resources: MCPResource[]; templates: MCPResourceTemplate[] } | undefined {
|
|
988
|
+
const connection = this.#connections.get(name);
|
|
989
|
+
if (!connection) return undefined;
|
|
990
|
+
return {
|
|
991
|
+
resources: connection.resources ?? [],
|
|
992
|
+
templates: connection.resourceTemplates ?? [],
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Read a specific resource from a server.
|
|
998
|
+
*/
|
|
999
|
+
async readServerResource(
|
|
1000
|
+
name: string,
|
|
1001
|
+
uri: string,
|
|
1002
|
+
options?: MCPRequestOptions,
|
|
1003
|
+
): Promise<MCPResourceReadResult | undefined> {
|
|
1004
|
+
const connection = this.#connections.get(name);
|
|
1005
|
+
if (!connection) return undefined;
|
|
1006
|
+
return readResource(connection, uri, options);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Get prompts for a specific server.
|
|
1011
|
+
*/
|
|
1012
|
+
getServerPrompts(name: string): MCPPrompt[] | undefined {
|
|
1013
|
+
const connection = this.#connections.get(name);
|
|
1014
|
+
if (!connection) return undefined;
|
|
1015
|
+
return connection.prompts ?? [];
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* Get a specific prompt from a server.
|
|
1020
|
+
*/
|
|
1021
|
+
async executePrompt(
|
|
1022
|
+
name: string,
|
|
1023
|
+
promptName: string,
|
|
1024
|
+
args?: Record<string, string>,
|
|
1025
|
+
options?: MCPRequestOptions,
|
|
1026
|
+
): Promise<MCPGetPromptResult | undefined> {
|
|
1027
|
+
const connection = this.#connections.get(name);
|
|
1028
|
+
if (!connection) return undefined;
|
|
1029
|
+
return getPrompt(connection, promptName, args, options);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Get all server instructions (for system prompt injection).
|
|
1034
|
+
*/
|
|
1035
|
+
getServerInstructions(): Map<string, string> {
|
|
1036
|
+
const instructions = new Map<string, string>();
|
|
1037
|
+
for (const [name, connection] of this.#connections) {
|
|
1038
|
+
if (connection.instructions) {
|
|
1039
|
+
instructions.set(name, connection.instructions);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return instructions;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* Get notification state for display.
|
|
1047
|
+
*/
|
|
1048
|
+
getNotificationState(): { enabled: boolean; subscriptions: Map<string, ReadonlySet<string>> } {
|
|
1049
|
+
return {
|
|
1050
|
+
enabled: this.#notificationsEnabled,
|
|
1051
|
+
subscriptions: this.#subscribedResources as Map<string, ReadonlySet<string>>,
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Resolve OAuth credentials and shell commands in config.
|
|
1057
|
+
*/
|
|
1058
|
+
async #resolveAuthConfig(config: MCPServerConfig, forceRefresh = false): Promise<MCPServerConfig> {
|
|
1059
|
+
let resolved: MCPServerConfig = { ...config };
|
|
1060
|
+
|
|
1061
|
+
const auth = config.auth;
|
|
1062
|
+
if (auth?.type === "oauth" && auth.credentialId && this.#authStorage) {
|
|
1063
|
+
const credentialId = auth.credentialId;
|
|
1064
|
+
try {
|
|
1065
|
+
let credential = this.#authStorage.get(credentialId);
|
|
1066
|
+
if (credential?.type === "oauth") {
|
|
1067
|
+
// Proactive refresh: 5-minute buffer before expiry
|
|
1068
|
+
// Force refresh: on 401/403 auth errors (revoked tokens, clock skew, missing expires)
|
|
1069
|
+
const REFRESH_BUFFER_MS = 5 * 60_000;
|
|
1070
|
+
const shouldRefresh =
|
|
1071
|
+
forceRefresh || (credential.expires && Date.now() >= credential.expires - REFRESH_BUFFER_MS);
|
|
1072
|
+
if (shouldRefresh && credential.refresh && auth.tokenUrl) {
|
|
1073
|
+
try {
|
|
1074
|
+
const refreshed = await refreshMCPOAuthToken(
|
|
1075
|
+
auth.tokenUrl,
|
|
1076
|
+
credential.refresh,
|
|
1077
|
+
auth.clientId,
|
|
1078
|
+
auth.clientSecret,
|
|
1079
|
+
);
|
|
1080
|
+
const refreshedCredential = { type: "oauth" as const, ...refreshed };
|
|
1081
|
+
await this.#authStorage.set(credentialId, refreshedCredential);
|
|
1082
|
+
credential = refreshedCredential;
|
|
1083
|
+
} catch (refreshError) {
|
|
1084
|
+
logger.warn("MCP OAuth refresh failed, using existing token", {
|
|
1085
|
+
credentialId,
|
|
1086
|
+
error: refreshError,
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
if (resolved.type === "http" || resolved.type === "sse") {
|
|
1092
|
+
resolved = {
|
|
1093
|
+
...resolved,
|
|
1094
|
+
headers: {
|
|
1095
|
+
...resolved.headers,
|
|
1096
|
+
Authorization: `Bearer ${credential.access}`,
|
|
1097
|
+
},
|
|
1098
|
+
};
|
|
1099
|
+
} else {
|
|
1100
|
+
resolved = {
|
|
1101
|
+
...resolved,
|
|
1102
|
+
env: {
|
|
1103
|
+
...resolved.env,
|
|
1104
|
+
OAUTH_ACCESS_TOKEN: credential.access,
|
|
1105
|
+
},
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
logger.warn("Failed to resolve OAuth credential", { credentialId, error });
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
if (resolved.type !== "http" && resolved.type !== "sse") {
|
|
1115
|
+
if (resolved.env) {
|
|
1116
|
+
const nextEnv: Record<string, string> = {};
|
|
1117
|
+
for (const [key, value] of Object.entries(resolved.env)) {
|
|
1118
|
+
const resolvedValue = await resolveConfigValue(value);
|
|
1119
|
+
if (resolvedValue) nextEnv[key] = resolvedValue;
|
|
1120
|
+
}
|
|
1121
|
+
resolved = { ...resolved, env: nextEnv };
|
|
1122
|
+
}
|
|
1123
|
+
} else {
|
|
1124
|
+
if (resolved.headers) {
|
|
1125
|
+
const nextHeaders: Record<string, string> = {};
|
|
1126
|
+
for (const [key, value] of Object.entries(resolved.headers)) {
|
|
1127
|
+
const resolvedValue = await resolveConfigValue(value);
|
|
1128
|
+
if (resolvedValue) nextHeaders[key] = resolvedValue;
|
|
1129
|
+
}
|
|
1130
|
+
resolved = { ...resolved, headers: nextHeaders };
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
return resolved;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Create an MCP manager and discover servers.
|
|
1140
|
+
* Convenience function for quick setup.
|
|
1141
|
+
*/
|
|
1142
|
+
export async function createMCPManager(
|
|
1143
|
+
cwd: string,
|
|
1144
|
+
options?: MCPDiscoverOptions,
|
|
1145
|
+
): Promise<{
|
|
1146
|
+
manager: MCPManager;
|
|
1147
|
+
result: MCPLoadResult;
|
|
1148
|
+
}> {
|
|
1149
|
+
const manager = new MCPManager(cwd);
|
|
1150
|
+
const result = await manager.discoverAndConnect(options);
|
|
1151
|
+
return { manager, result };
|
|
1152
|
+
}
|