@prestyj/cli 5.9.0 → 5.11.0
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/README.md +4 -5
- package/assets/skills/evidence-led-ui/SKILL.md +1 -1
- package/dist/app-sidecar-auth-broadcast.test.d.ts +2 -0
- package/dist/app-sidecar-auth-broadcast.test.d.ts.map +1 -0
- package/dist/app-sidecar-auth-broadcast.test.js +282 -0
- package/dist/app-sidecar-auth-broadcast.test.js.map +1 -0
- package/dist/app-sidecar-sessions.d.ts +5 -2
- package/dist/app-sidecar-sessions.d.ts.map +1 -1
- package/dist/app-sidecar-sessions.js +34 -8
- package/dist/app-sidecar-sessions.js.map +1 -1
- package/dist/app-sidecar-sessions.test.js +95 -0
- package/dist/app-sidecar-sessions.test.js.map +1 -1
- package/dist/app-sidecar.js +871 -142
- package/dist/app-sidecar.js.map +1 -1
- package/dist/chat-agents/general.test.js +5 -3
- package/dist/chat-agents/general.test.js.map +1 -1
- package/dist/chat-agents/memory.d.ts.map +1 -1
- package/dist/chat-agents/memory.js +14 -5
- package/dist/chat-agents/memory.js.map +1 -1
- package/dist/chat-agents/memory.test.js +11 -1
- package/dist/chat-agents/memory.test.js.map +1 -1
- package/dist/chat-agents/shared.js +1 -1
- package/dist/chat-agents/shared.js.map +1 -1
- package/dist/chat-agents/specialists.test.js +3 -2
- package/dist/chat-agents/specialists.test.js.map +1 -1
- package/dist/cli/command-routing.d.ts +1 -1
- package/dist/cli/command-routing.d.ts.map +1 -1
- package/dist/cli/command-routing.js +1 -0
- package/dist/cli/command-routing.js.map +1 -1
- package/dist/cli/command-routing.test.js +1 -0
- package/dist/cli/command-routing.test.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +183 -51
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +51 -2
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +71 -2
- package/dist/config.test.js.map +1 -1
- package/dist/core/agent-mcp-passthrough.test.d.ts +2 -0
- package/dist/core/agent-mcp-passthrough.test.d.ts.map +1 -0
- package/dist/core/agent-mcp-passthrough.test.js +61 -0
- package/dist/core/agent-mcp-passthrough.test.js.map +1 -0
- package/dist/core/agent-notifications.d.ts +62 -0
- package/dist/core/agent-notifications.d.ts.map +1 -0
- package/dist/core/agent-notifications.js +92 -0
- package/dist/core/agent-notifications.js.map +1 -0
- package/dist/core/agent-notifications.test.d.ts +2 -0
- package/dist/core/agent-notifications.test.d.ts.map +1 -0
- package/dist/core/agent-notifications.test.js +97 -0
- package/dist/core/agent-notifications.test.js.map +1 -0
- package/dist/core/agent-session-add-dir.test.d.ts +2 -0
- package/dist/core/agent-session-add-dir.test.d.ts.map +1 -0
- package/dist/core/agent-session-add-dir.test.js +110 -0
- package/dist/core/agent-session-add-dir.test.js.map +1 -0
- package/dist/core/agent-session-checkpoint-flush.test.d.ts +2 -0
- package/dist/core/agent-session-checkpoint-flush.test.d.ts.map +1 -0
- package/dist/core/agent-session-checkpoint-flush.test.js +201 -0
- package/dist/core/agent-session-checkpoint-flush.test.js.map +1 -0
- package/dist/core/agent-session-compaction.test.js +327 -13
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session-crash-durability.test.d.ts +2 -0
- package/dist/core/agent-session-crash-durability.test.d.ts.map +1 -0
- package/dist/core/agent-session-crash-durability.test.js +102 -0
- package/dist/core/agent-session-crash-durability.test.js.map +1 -0
- package/dist/core/agent-session-marker-anchors.test.d.ts +2 -0
- package/dist/core/agent-session-marker-anchors.test.d.ts.map +1 -0
- package/dist/core/agent-session-marker-anchors.test.js +326 -0
- package/dist/core/agent-session-marker-anchors.test.js.map +1 -0
- package/dist/core/agent-session-memory-tail.test.js +4 -7
- package/dist/core/agent-session-memory-tail.test.js.map +1 -1
- package/dist/core/agent-session-model-switch.test.d.ts +2 -0
- package/dist/core/agent-session-model-switch.test.d.ts.map +1 -0
- package/dist/core/agent-session-model-switch.test.js +161 -0
- package/dist/core/agent-session-model-switch.test.js.map +1 -0
- package/dist/core/agent-session-notifications.test.d.ts +2 -0
- package/dist/core/agent-session-notifications.test.d.ts.map +1 -0
- package/dist/core/agent-session-notifications.test.js +114 -0
- package/dist/core/agent-session-notifications.test.js.map +1 -0
- package/dist/core/agent-session-process-gate.test.d.ts +2 -0
- package/dist/core/agent-session-process-gate.test.d.ts.map +1 -0
- package/dist/core/agent-session-process-gate.test.js +92 -0
- package/dist/core/agent-session-process-gate.test.js.map +1 -0
- package/dist/core/agent-session-queue.test.js +69 -7
- package/dist/core/agent-session-queue.test.js.map +1 -1
- package/dist/core/agent-session-review-coverage.test.js +80 -23
- package/dist/core/agent-session-review-coverage.test.js.map +1 -1
- package/dist/core/agent-session-slash-restore.test.d.ts +2 -0
- package/dist/core/agent-session-slash-restore.test.d.ts.map +1 -0
- package/dist/core/agent-session-slash-restore.test.js +121 -0
- package/dist/core/agent-session-slash-restore.test.js.map +1 -0
- package/dist/core/agent-session-subagent-policy.test.js +7 -3
- package/dist/core/agent-session-subagent-policy.test.js.map +1 -1
- package/dist/core/agent-session.d.ts +256 -10
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1071 -219
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agent-tools-allowlist.test.js +5 -3
- package/dist/core/agent-tools-allowlist.test.js.map +1 -1
- package/dist/core/agents.d.ts +11 -0
- package/dist/core/agents.d.ts.map +1 -1
- package/dist/core/agents.js +21 -0
- package/dist/core/agents.js.map +1 -1
- package/dist/core/agents.test.d.ts +2 -0
- package/dist/core/agents.test.d.ts.map +1 -0
- package/dist/core/agents.test.js +67 -0
- package/dist/core/agents.test.js.map +1 -0
- package/dist/core/auth-providers.js +1 -1
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/compaction/active-context.test.js +21 -0
- package/dist/core/compaction/active-context.test.js.map +1 -1
- package/dist/core/compaction/compactor.d.ts +108 -8
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +423 -119
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +361 -9
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/compaction/policy.d.ts +17 -0
- package/dist/core/compaction/policy.d.ts.map +1 -0
- package/dist/core/compaction/policy.js +22 -0
- package/dist/core/compaction/policy.js.map +1 -0
- package/dist/core/compaction/policy.test.d.ts +2 -0
- package/dist/core/compaction/policy.test.d.ts.map +1 -0
- package/dist/core/compaction/policy.test.js +35 -0
- package/dist/core/compaction/policy.test.js.map +1 -0
- package/dist/core/compaction/query-aware-selector.d.ts +33 -0
- package/dist/core/compaction/query-aware-selector.d.ts.map +1 -0
- package/dist/core/compaction/query-aware-selector.js +202 -0
- package/dist/core/compaction/query-aware-selector.js.map +1 -0
- package/dist/core/compaction/query-aware-selector.test.d.ts +2 -0
- package/dist/core/compaction/query-aware-selector.test.d.ts.map +1 -0
- package/dist/core/compaction/query-aware-selector.test.js +96 -0
- package/dist/core/compaction/query-aware-selector.test.js.map +1 -0
- package/dist/core/compaction/token-estimator.test.js +1 -1
- package/dist/core/compaction/token-estimator.test.js.map +1 -1
- package/dist/core/compaction/tool-result-pruner.d.ts +24 -11
- package/dist/core/compaction/tool-result-pruner.d.ts.map +1 -1
- package/dist/core/compaction/tool-result-pruner.js +98 -26
- package/dist/core/compaction/tool-result-pruner.js.map +1 -1
- package/dist/core/compaction/tool-result-pruner.test.js +178 -6
- package/dist/core/compaction/tool-result-pruner.test.js.map +1 -1
- package/dist/core/encode-cwd.d.ts +17 -0
- package/dist/core/encode-cwd.d.ts.map +1 -1
- package/dist/core/encode-cwd.js +20 -7
- package/dist/core/encode-cwd.js.map +1 -1
- package/dist/core/encode-cwd.test.js +20 -1
- package/dist/core/encode-cwd.test.js.map +1 -1
- package/dist/core/event-bus.d.ts +23 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +10 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +28 -8
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/plugin-bundles.d.ts +23 -0
- package/dist/core/extensions/plugin-bundles.d.ts.map +1 -0
- package/dist/core/extensions/plugin-bundles.js +228 -0
- package/dist/core/extensions/plugin-bundles.js.map +1 -0
- package/dist/core/extensions/plugin-bundles.test.d.ts +2 -0
- package/dist/core/extensions/plugin-bundles.test.d.ts.map +1 -0
- package/dist/core/extensions/plugin-bundles.test.js +110 -0
- package/dist/core/extensions/plugin-bundles.test.js.map +1 -0
- package/dist/core/foreign-session-import.d.ts +120 -0
- package/dist/core/foreign-session-import.d.ts.map +1 -0
- package/dist/core/foreign-session-import.js +598 -0
- package/dist/core/foreign-session-import.js.map +1 -0
- package/dist/core/foreign-session-import.test.d.ts +2 -0
- package/dist/core/foreign-session-import.test.d.ts.map +1 -0
- package/dist/core/foreign-session-import.test.js +220 -0
- package/dist/core/foreign-session-import.test.js.map +1 -0
- package/dist/core/foreign-transcript-blocks.d.ts +51 -0
- package/dist/core/foreign-transcript-blocks.d.ts.map +1 -0
- package/dist/core/foreign-transcript-blocks.js +151 -0
- package/dist/core/foreign-transcript-blocks.js.map +1 -0
- package/dist/core/ideal-review.d.ts +20 -1
- package/dist/core/ideal-review.d.ts.map +1 -1
- package/dist/core/ideal-review.js +43 -3
- package/dist/core/ideal-review.js.map +1 -1
- package/dist/core/ideal-review.test.js +106 -5
- package/dist/core/ideal-review.test.js.map +1 -1
- package/dist/core/local-endpoint-store.d.ts +50 -0
- package/dist/core/local-endpoint-store.d.ts.map +1 -0
- package/dist/core/local-endpoint-store.js +201 -0
- package/dist/core/local-endpoint-store.js.map +1 -0
- package/dist/core/local-endpoint-store.test.d.ts +2 -0
- package/dist/core/local-endpoint-store.test.d.ts.map +1 -0
- package/dist/core/local-endpoint-store.test.js +131 -0
- package/dist/core/local-endpoint-store.test.js.map +1 -0
- package/dist/core/loop-breaker.d.ts.map +1 -1
- package/dist/core/loop-breaker.js +1 -0
- package/dist/core/loop-breaker.js.map +1 -1
- package/dist/core/lsp/client.d.ts +65 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +197 -14
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/lsp/jsonrpc.d.ts +38 -1
- package/dist/core/lsp/jsonrpc.d.ts.map +1 -1
- package/dist/core/lsp/jsonrpc.js +13 -1
- package/dist/core/lsp/jsonrpc.js.map +1 -1
- package/dist/core/lsp/manager.d.ts +47 -9
- package/dist/core/lsp/manager.d.ts.map +1 -1
- package/dist/core/lsp/manager.js +107 -59
- package/dist/core/lsp/manager.js.map +1 -1
- package/dist/core/lsp/manager.test.js +50 -1
- package/dist/core/lsp/manager.test.js.map +1 -1
- package/dist/core/lsp/pool.d.ts +113 -0
- package/dist/core/lsp/pool.d.ts.map +1 -0
- package/dist/core/lsp/pool.js +255 -0
- package/dist/core/lsp/pool.js.map +1 -0
- package/dist/core/lsp/pool.test.d.ts +2 -0
- package/dist/core/lsp/pool.test.d.ts.map +1 -0
- package/dist/core/lsp/pool.test.js +286 -0
- package/dist/core/lsp/pool.test.js.map +1 -0
- package/dist/core/lsp/servers.d.ts.map +1 -1
- package/dist/core/lsp/servers.js +43 -3
- package/dist/core/lsp/servers.js.map +1 -1
- package/dist/core/lsp/servers.test.d.ts +2 -0
- package/dist/core/lsp/servers.test.d.ts.map +1 -0
- package/dist/core/lsp/servers.test.js +67 -0
- package/dist/core/lsp/servers.test.js.map +1 -0
- package/dist/core/lsp/test-support.d.ts +18 -0
- package/dist/core/lsp/test-support.d.ts.map +1 -0
- package/dist/core/lsp/test-support.js +27 -0
- package/dist/core/lsp/test-support.js.map +1 -0
- package/dist/core/lsp/uri.test.d.ts +2 -0
- package/dist/core/lsp/uri.test.d.ts.map +1 -0
- package/dist/core/lsp/uri.test.js +56 -0
- package/dist/core/lsp/uri.test.js.map +1 -0
- package/dist/core/lsp/windows.test.d.ts +2 -0
- package/dist/core/lsp/windows.test.d.ts.map +1 -0
- package/dist/core/lsp/windows.test.js +239 -0
- package/dist/core/lsp/windows.test.js.map +1 -0
- package/dist/core/mcp/catalog-cache.d.ts +63 -0
- package/dist/core/mcp/catalog-cache.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.js +163 -0
- package/dist/core/mcp/catalog-cache.js.map +1 -0
- package/dist/core/mcp/catalog-cache.test.d.ts +2 -0
- package/dist/core/mcp/catalog-cache.test.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.test.js +99 -0
- package/dist/core/mcp/catalog-cache.test.js.map +1 -0
- package/dist/core/mcp/client-session-recovery.test.d.ts +2 -0
- package/dist/core/mcp/client-session-recovery.test.d.ts.map +1 -0
- package/dist/core/mcp/client-session-recovery.test.js +231 -0
- package/dist/core/mcp/client-session-recovery.test.js.map +1 -0
- package/dist/core/mcp/client.d.ts +158 -0
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +476 -35
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/elicitation-bridge.d.ts +41 -0
- package/dist/core/mcp/elicitation-bridge.d.ts.map +1 -0
- package/dist/core/mcp/elicitation-bridge.js +52 -0
- package/dist/core/mcp/elicitation-bridge.js.map +1 -0
- package/dist/core/mcp/elicitation.test.d.ts +2 -0
- package/dist/core/mcp/elicitation.test.d.ts.map +1 -0
- package/dist/core/mcp/elicitation.test.js +158 -0
- package/dist/core/mcp/elicitation.test.js.map +1 -0
- package/dist/core/mcp/index.d.ts +5 -1
- package/dist/core/mcp/index.d.ts.map +1 -1
- package/dist/core/mcp/index.js +2 -0
- package/dist/core/mcp/index.js.map +1 -1
- package/dist/core/mcp/legacy-negotiation.test.d.ts +2 -0
- package/dist/core/mcp/legacy-negotiation.test.d.ts.map +1 -0
- package/dist/core/mcp/legacy-negotiation.test.js +80 -0
- package/dist/core/mcp/legacy-negotiation.test.js.map +1 -0
- package/dist/core/mcp/oauth-provider.d.ts +1 -2
- package/dist/core/mcp/oauth-provider.d.ts.map +1 -1
- package/dist/core/mcp/oauth-provider.js +6 -2
- package/dist/core/mcp/oauth-provider.js.map +1 -1
- package/dist/core/mcp/oauth-store.d.ts +1 -1
- package/dist/core/mcp/oauth-store.d.ts.map +1 -1
- package/dist/core/mcp/oauth-store.js.map +1 -1
- package/dist/core/mcp/resolve-stdio.d.ts +35 -0
- package/dist/core/mcp/resolve-stdio.d.ts.map +1 -1
- package/dist/core/mcp/resolve-stdio.js +80 -1
- package/dist/core/mcp/resolve-stdio.js.map +1 -1
- package/dist/core/mcp/resolve-stdio.test.js +99 -4
- package/dist/core/mcp/resolve-stdio.test.js.map +1 -1
- package/dist/core/mcp/session-elicitation-wiring.test.d.ts +2 -0
- package/dist/core/mcp/session-elicitation-wiring.test.d.ts.map +1 -0
- package/dist/core/mcp/session-elicitation-wiring.test.js +138 -0
- package/dist/core/mcp/session-elicitation-wiring.test.js.map +1 -0
- package/dist/core/mcp/shared-pool.d.ts +158 -0
- package/dist/core/mcp/shared-pool.d.ts.map +1 -0
- package/dist/core/mcp/shared-pool.js +235 -0
- package/dist/core/mcp/shared-pool.js.map +1 -0
- package/dist/core/mcp/shared-pool.test.d.ts +2 -0
- package/dist/core/mcp/shared-pool.test.d.ts.map +1 -0
- package/dist/core/mcp/shared-pool.test.js +425 -0
- package/dist/core/mcp/shared-pool.test.js.map +1 -0
- package/dist/core/mcp/types.d.ts +12 -0
- package/dist/core/mcp/types.d.ts.map +1 -1
- package/dist/core/mcp/when-connected.test.d.ts +2 -0
- package/dist/core/mcp/when-connected.test.d.ts.map +1 -0
- package/dist/core/mcp/when-connected.test.js +46 -0
- package/dist/core/mcp/when-connected.test.js.map +1 -0
- package/dist/core/network-guard.d.ts +47 -0
- package/dist/core/network-guard.d.ts.map +1 -0
- package/dist/core/network-guard.js +208 -0
- package/dist/core/network-guard.js.map +1 -0
- package/dist/core/network-guard.test.d.ts +2 -0
- package/dist/core/network-guard.test.d.ts.map +1 -0
- package/dist/core/network-guard.test.js +72 -0
- package/dist/core/network-guard.test.js.map +1 -0
- package/dist/core/nolan-context.d.ts.map +1 -1
- package/dist/core/nolan-context.js +8 -0
- package/dist/core/nolan-context.js.map +1 -1
- package/dist/core/nolan-context.test.js +40 -0
- package/dist/core/nolan-context.test.js.map +1 -1
- package/dist/core/nolan-prompt.js +3 -0
- package/dist/core/nolan-prompt.js.map +1 -1
- package/dist/core/nolan-prompt.test.js +5 -0
- package/dist/core/nolan-prompt.test.js.map +1 -1
- package/dist/core/persistent-shell.d.ts +4 -1
- package/dist/core/persistent-shell.d.ts.map +1 -1
- package/dist/core/persistent-shell.js +21 -4
- package/dist/core/persistent-shell.js.map +1 -1
- package/dist/core/process-gate.d.ts +45 -0
- package/dist/core/process-gate.d.ts.map +1 -0
- package/dist/core/process-gate.js +79 -0
- package/dist/core/process-gate.js.map +1 -0
- package/dist/core/process-gate.test.d.ts +2 -0
- package/dist/core/process-gate.test.d.ts.map +1 -0
- package/dist/core/process-gate.test.js +87 -0
- package/dist/core/process-gate.test.js.map +1 -0
- package/dist/core/process-manager-dev-server-repro.test.js +34 -12
- package/dist/core/process-manager-dev-server-repro.test.js.map +1 -1
- package/dist/core/process-manager-notifications.test.d.ts +2 -0
- package/dist/core/process-manager-notifications.test.d.ts.map +1 -0
- package/dist/core/process-manager-notifications.test.js +217 -0
- package/dist/core/process-manager-notifications.test.js.map +1 -0
- package/dist/core/process-manager.d.ts +51 -2
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +212 -15
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/project-discovery.d.ts +38 -6
- package/dist/core/project-discovery.d.ts.map +1 -1
- package/dist/core/project-discovery.js +327 -40
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/project-discovery.test.js +226 -1
- package/dist/core/project-discovery.test.js.map +1 -1
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +89 -156
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/prompt-commands.test.js +4 -10
- package/dist/core/prompt-commands.test.js.map +1 -1
- package/dist/core/radio.test.js +4 -1
- package/dist/core/radio.test.js.map +1 -1
- package/dist/core/regrounding.d.ts.map +1 -1
- package/dist/core/regrounding.js +1 -0
- package/dist/core/regrounding.js.map +1 -1
- package/dist/core/run-claim.d.ts +37 -0
- package/dist/core/run-claim.d.ts.map +1 -0
- package/dist/core/run-claim.js +46 -0
- package/dist/core/run-claim.js.map +1 -0
- package/dist/core/run-claim.test.d.ts +2 -0
- package/dist/core/run-claim.test.d.ts.map +1 -0
- package/dist/core/run-claim.test.js +107 -0
- package/dist/core/run-claim.test.js.map +1 -0
- package/dist/core/run-journal.test.d.ts +2 -0
- package/dist/core/run-journal.test.d.ts.map +1 -0
- package/dist/core/run-journal.test.js +195 -0
- package/dist/core/run-journal.test.js.map +1 -0
- package/dist/core/run-lifecycle.d.ts +22 -3
- package/dist/core/run-lifecycle.d.ts.map +1 -1
- package/dist/core/run-lifecycle.js +12 -3
- package/dist/core/run-lifecycle.js.map +1 -1
- package/dist/core/runtime-mode.d.ts +2 -0
- package/dist/core/runtime-mode.d.ts.map +1 -1
- package/dist/core/runtime-mode.js +4 -0
- package/dist/core/runtime-mode.js.map +1 -1
- package/dist/core/sandbox-domains.d.ts +14 -0
- package/dist/core/sandbox-domains.d.ts.map +1 -0
- package/dist/core/sandbox-domains.js +76 -0
- package/dist/core/sandbox-domains.js.map +1 -0
- package/dist/core/sandbox-feedback.d.ts +22 -0
- package/dist/core/sandbox-feedback.d.ts.map +1 -0
- package/dist/core/sandbox-feedback.js +69 -0
- package/dist/core/sandbox-feedback.js.map +1 -0
- package/dist/core/sandbox-feedback.test.d.ts +2 -0
- package/dist/core/sandbox-feedback.test.d.ts.map +1 -0
- package/dist/core/sandbox-feedback.test.js +32 -0
- package/dist/core/sandbox-feedback.test.js.map +1 -0
- package/dist/core/sandbox.d.ts +91 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandbox.js +254 -0
- package/dist/core/sandbox.js.map +1 -0
- package/dist/core/sandbox.test.d.ts +2 -0
- package/dist/core/sandbox.test.d.ts.map +1 -0
- package/dist/core/sandbox.test.js +157 -0
- package/dist/core/sandbox.test.js.map +1 -0
- package/dist/core/session-compaction.d.ts +6 -0
- package/dist/core/session-compaction.d.ts.map +1 -1
- package/dist/core/session-compaction.js +11 -1
- package/dist/core/session-compaction.js.map +1 -1
- package/dist/core/session-compaction.test.js +18 -1
- package/dist/core/session-compaction.test.js.map +1 -1
- package/dist/core/session-export.d.ts +98 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +364 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-export.test.d.ts +2 -0
- package/dist/core/session-export.test.d.ts.map +1 -0
- package/dist/core/session-export.test.js +353 -0
- package/dist/core/session-export.test.js.map +1 -0
- package/dist/core/session-history.d.ts +95 -6
- package/dist/core/session-history.d.ts.map +1 -1
- package/dist/core/session-history.js +261 -17
- package/dist/core/session-history.js.map +1 -1
- package/dist/core/session-history.test.js +173 -2
- package/dist/core/session-history.test.js.map +1 -1
- package/dist/core/session-manager.d.ts +223 -6
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +515 -26
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.js +147 -3
- package/dist/core/session-manager.test.js.map +1 -1
- package/dist/core/session-notification-visibility.test.d.ts +2 -0
- package/dist/core/session-notification-visibility.test.d.ts.map +1 -0
- package/dist/core/session-notification-visibility.test.js +113 -0
- package/dist/core/session-notification-visibility.test.js.map +1 -0
- package/dist/core/session-preview.d.ts +5 -4
- package/dist/core/session-preview.d.ts.map +1 -1
- package/dist/core/session-preview.js +12 -5
- package/dist/core/session-preview.js.map +1 -1
- package/dist/core/session-restore-display.test.js +25 -0
- package/dist/core/session-restore-display.test.js.map +1 -1
- package/dist/core/session-storage.d.ts.map +1 -1
- package/dist/core/session-storage.js +64 -4
- package/dist/core/session-storage.js.map +1 -1
- package/dist/core/session-storage.test.js +10 -2
- package/dist/core/session-storage.test.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +36 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/setup-history.d.ts +0 -6
- package/dist/core/setup-history.d.ts.map +1 -1
- package/dist/core/setup-history.js +2 -18
- package/dist/core/setup-history.js.map +1 -1
- package/dist/core/setup-history.test.js +11 -26
- package/dist/core/setup-history.test.js.map +1 -1
- package/dist/core/skills-routing.test.js +3 -1
- package/dist/core/skills-routing.test.js.map +1 -1
- package/dist/core/slash-commands.d.ts +18 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +42 -10
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/slash-commands.test.d.ts +2 -0
- package/dist/core/slash-commands.test.d.ts.map +1 -0
- package/dist/core/slash-commands.test.js +59 -0
- package/dist/core/slash-commands.test.js.map +1 -0
- package/dist/core/speed-benchmark.test.js +9 -3
- package/dist/core/speed-benchmark.test.js.map +1 -1
- package/dist/core/steering.d.ts +12 -0
- package/dist/core/steering.d.ts.map +1 -1
- package/dist/core/steering.js +16 -0
- package/dist/core/steering.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +14 -1
- package/dist/core/subagent-manager.d.ts.map +1 -1
- package/dist/core/subagent-manager.js +48 -1
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-manager.test.js +43 -7
- package/dist/core/subagent-manager.test.js.map +1 -1
- package/dist/core/subagent-store.test.js +14 -2
- package/dist/core/subagent-store.test.js.map +1 -1
- package/dist/core/verification-evidence.d.ts +17 -0
- package/dist/core/verification-evidence.d.ts.map +1 -0
- package/dist/core/verification-evidence.js +283 -0
- package/dist/core/verification-evidence.js.map +1 -0
- package/dist/core/verification-evidence.test.d.ts +2 -0
- package/dist/core/verification-evidence.test.d.ts.map +1 -0
- package/dist/core/verification-evidence.test.js +103 -0
- package/dist/core/verification-evidence.test.js.map +1 -0
- package/dist/core/workspace-guard.d.ts +2 -0
- package/dist/core/workspace-guard.d.ts.map +1 -1
- package/dist/core/workspace-guard.js +4 -1
- package/dist/core/workspace-guard.js.map +1 -1
- package/dist/core/workspace-guard.test.js +21 -0
- package/dist/core/workspace-guard.test.js.map +1 -1
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +17 -3
- package/dist/interactive.js.map +1 -1
- package/dist/modes/acp-mode.d.ts +122 -0
- package/dist/modes/acp-mode.d.ts.map +1 -0
- package/dist/modes/acp-mode.js +1366 -0
- package/dist/modes/acp-mode.js.map +1 -0
- package/dist/modes/acp-mode.test.d.ts +2 -0
- package/dist/modes/acp-mode.test.d.ts.map +1 -0
- package/dist/modes/acp-mode.test.js +1065 -0
- package/dist/modes/acp-mode.test.js.map +1 -0
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +1 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/json-mode.d.ts +35 -0
- package/dist/modes/json-mode.d.ts.map +1 -1
- package/dist/modes/json-mode.js +38 -0
- package/dist/modes/json-mode.js.map +1 -1
- package/dist/modes/json-mode.test.d.ts +2 -0
- package/dist/modes/json-mode.test.d.ts.map +1 -0
- package/dist/modes/json-mode.test.js +84 -0
- package/dist/modes/json-mode.test.js.map +1 -0
- package/dist/modes/subagent-worker-mode.d.ts +5 -0
- package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
- package/dist/modes/subagent-worker-mode.js +91 -11
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.test.d.ts +2 -0
- package/dist/modes/subagent-worker-mode.test.d.ts.map +1 -0
- package/dist/modes/subagent-worker-mode.test.js +52 -0
- package/dist/modes/subagent-worker-mode.test.js.map +1 -0
- package/dist/session.d.ts +29 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +41 -0
- package/dist/session.js.map +1 -1
- package/dist/system-prompt.d.ts +11 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +39 -18
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.test.js +33 -17
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/test-support/fake-home.d.ts +20 -0
- package/dist/test-support/fake-home.d.ts.map +1 -0
- package/dist/test-support/fake-home.js +37 -0
- package/dist/test-support/fake-home.js.map +1 -0
- package/dist/tools/bash.d.ts +3 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +70 -14
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/bash.test.js +122 -7
- package/dist/tools/bash.test.js.map +1 -1
- package/dist/tools/edit-robustness.test.js +3 -1
- package/dist/tools/edit-robustness.test.js.map +1 -1
- package/dist/tools/find.d.ts.map +1 -1
- package/dist/tools/find.js +4 -1
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/goals.d.ts +2 -2
- package/dist/tools/grep.d.ts +3 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +137 -12
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/grep.test.js +53 -0
- package/dist/tools/grep.test.js.map +1 -1
- package/dist/tools/index.d.ts +19 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +22 -19
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/index.test.d.ts +2 -0
- package/dist/tools/index.test.d.ts.map +1 -0
- package/dist/tools/index.test.js +55 -0
- package/dist/tools/index.test.js.map +1 -0
- package/dist/tools/overflow.test.js +4 -7
- package/dist/tools/overflow.test.js.map +1 -1
- package/dist/tools/path-utils.d.ts +15 -0
- package/dist/tools/path-utils.d.ts.map +1 -1
- package/dist/tools/path-utils.js +17 -0
- package/dist/tools/path-utils.js.map +1 -1
- package/dist/tools/path-utils.test.js +7 -2
- package/dist/tools/path-utils.test.js.map +1 -1
- package/dist/tools/prompt-hints.js +1 -1
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/read-only-bash.d.ts +3 -0
- package/dist/tools/read-only-bash.d.ts.map +1 -1
- package/dist/tools/read-only-bash.js +7 -9
- package/dist/tools/read-only-bash.js.map +1 -1
- package/dist/tools/search-code.d.ts.map +1 -1
- package/dist/tools/search-code.js +6 -4
- package/dist/tools/search-code.js.map +1 -1
- package/dist/tools/subagent-control.d.ts.map +1 -1
- package/dist/tools/subagent-control.js +6 -2
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/subagent-shared.d.ts +6 -0
- package/dist/tools/subagent-shared.d.ts.map +1 -1
- package/dist/tools/subagent-shared.js +6 -0
- package/dist/tools/subagent-shared.js.map +1 -1
- package/dist/tools/subagent.d.ts +1 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +51 -8
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/subagent.test.js +65 -3
- package/dist/tools/subagent.test.js.map +1 -1
- package/dist/tools/task-output.d.ts.map +1 -1
- package/dist/tools/task-output.js +3 -1
- package/dist/tools/task-output.js.map +1 -1
- package/dist/tools/tool-search.d.ts +12 -1
- package/dist/tools/tool-search.d.ts.map +1 -1
- package/dist/tools/tool-search.js +22 -3
- package/dist/tools/tool-search.js.map +1 -1
- package/dist/tools/tool-search.test.d.ts +2 -0
- package/dist/tools/tool-search.test.d.ts.map +1 -0
- package/dist/tools/tool-search.test.js +69 -0
- package/dist/tools/tool-search.test.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +3 -2
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +22 -8
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-fetch.test.js +34 -0
- package/dist/tools/web-fetch.test.js.map +1 -1
- package/dist/tools/web-search.d.ts +2 -1
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +21 -5
- package/dist/tools/web-search.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +32 -81
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-items.d.ts +1 -12
- package/dist/ui/app-items.d.ts.map +1 -1
- package/dist/ui/app-items.js.map +1 -1
- package/dist/ui/components/Footer.d.ts +1 -1
- package/dist/ui/components/Footer.js +2 -2
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +18 -4
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/usePixelFixFlow.d.ts +1 -2
- package/dist/ui/hooks/usePixelFixFlow.d.ts.map +1 -1
- package/dist/ui/hooks/usePixelFixFlow.js +2 -5
- package/dist/ui/hooks/usePixelFixFlow.js.map +1 -1
- package/dist/ui/login.js +1 -1
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/terminal-history-status-renderers.d.ts +1 -2
- package/dist/ui/terminal-history-status-renderers.d.ts.map +1 -1
- package/dist/ui/terminal-history-status-renderers.js +2 -15
- package/dist/ui/terminal-history-status-renderers.js.map +1 -1
- package/dist/ui/terminal-history.d.ts.map +1 -1
- package/dist/ui/terminal-history.js +2 -4
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/thinking-level-cycle.test.js +8 -8
- package/dist/ui/thinking-level-cycle.test.js.map +1 -1
- package/dist/ui/transcript/MiscRows.d.ts +1 -4
- package/dist/ui/transcript/MiscRows.d.ts.map +1 -1
- package/dist/ui/transcript/MiscRows.js +2 -7
- package/dist/ui/transcript/MiscRows.js.map +1 -1
- package/dist/ui/transcript/TranscriptRenderer.d.ts.map +1 -1
- package/dist/ui/transcript/TranscriptRenderer.js +1 -3
- package/dist/ui/transcript/TranscriptRenderer.js.map +1 -1
- package/dist/ui/transcript/presentation.d.ts +1 -9
- package/dist/ui/transcript/presentation.d.ts.map +1 -1
- package/dist/ui/transcript/presentation.js +0 -9
- package/dist/ui/transcript/presentation.js.map +1 -1
- package/dist/ui/transcript/spacing.d.ts +2 -2
- package/dist/ui/transcript/spacing.d.ts.map +1 -1
- package/dist/ui/transcript/spacing.js +0 -1
- package/dist/ui/transcript/spacing.js.map +1 -1
- package/dist/ui/transcript/spacing.test.js +1 -3
- package/dist/ui/transcript/spacing.test.js.map +1 -1
- package/dist/ui/tui-history-parity.test.js +2 -9
- package/dist/ui/tui-history-parity.test.js.map +1 -1
- package/dist/utils/github.d.ts +20 -0
- package/dist/utils/github.d.ts.map +1 -0
- package/dist/utils/github.js +65 -0
- package/dist/utils/github.js.map +1 -0
- package/dist/utils/github.test.d.ts +2 -0
- package/dist/utils/github.test.d.ts.map +1 -0
- package/dist/utils/github.test.js +23 -0
- package/dist/utils/github.test.js.map +1 -0
- package/dist/utils/image.d.ts +26 -3
- package/dist/utils/image.d.ts.map +1 -1
- package/dist/utils/image.js +83 -14
- package/dist/utils/image.js.map +1 -1
- package/dist/utils/image.test.d.ts +2 -0
- package/dist/utils/image.test.d.ts.map +1 -0
- package/dist/utils/image.test.js +139 -0
- package/dist/utils/image.test.js.map +1 -0
- package/dist/utils/process.d.ts +26 -3
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +68 -10
- package/dist/utils/process.js.map +1 -1
- package/dist/utils/process.test.d.ts +2 -0
- package/dist/utils/process.test.d.ts.map +1 -0
- package/dist/utils/process.test.js +42 -0
- package/dist/utils/process.test.js.map +1 -0
- package/package.json +6 -5
package/dist/core/mcp/client.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/
|
|
3
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
|
-
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
1
|
+
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
2
|
+
import { Client, OAuthError, OAuthErrorCode, ProtocolError, SdkError, SdkErrorCode, SdkHttpError, StreamableHTTPClientTransport, SSEClientTransport, UnauthorizedError, } from "@modelcontextprotocol/client";
|
|
6
3
|
import { z } from "zod";
|
|
7
4
|
import http from "node:http";
|
|
8
5
|
import os from "node:os";
|
|
@@ -11,8 +8,220 @@ import { McpOAuthProvider, MCP_OAUTH_CALLBACK_PORT, MCP_OAUTH_CALLBACK_PATH, } f
|
|
|
11
8
|
import { McpOAuthStore } from "./oauth-store.js";
|
|
12
9
|
import { isLocalhost, alternateLoopback, isNetworkError } from "./loopback.js";
|
|
13
10
|
import { resolveStdioCommand } from "./resolve-stdio.js";
|
|
11
|
+
import { McpCatalogCache } from "./catalog-cache.js";
|
|
12
|
+
import { isShareableServer, sharedMcpPool, } from "./shared-pool.js";
|
|
14
13
|
export class MCPClientManager {
|
|
15
14
|
servers = [];
|
|
15
|
+
/**
|
|
16
|
+
* Per-server connection settlement, so a caller holding a cached-only tool
|
|
17
|
+
* can wait for the live client instead of failing or hanging forever.
|
|
18
|
+
*/
|
|
19
|
+
connections = new Map();
|
|
20
|
+
/**
|
|
21
|
+
* In-flight session rebuilds, keyed by server name. N tool calls to the same
|
|
22
|
+
* server all 404 at once when its session expires; without coalescing each
|
|
23
|
+
* would spawn its own reconnect and they would race to replace each other in
|
|
24
|
+
* `this.servers`. Sharing one promise means exactly one reconnect per outage.
|
|
25
|
+
*/
|
|
26
|
+
reconnecting = new Map();
|
|
27
|
+
/**
|
|
28
|
+
* Claims on process-wide shared connections, keyed by server name. Held so
|
|
29
|
+
* `dispose` can hand them back: the pooled child only exits once the LAST
|
|
30
|
+
* manager holding it releases, so leaking a claim here would pin the process
|
|
31
|
+
* for the daemon's lifetime — the exact bug sharing is meant to fix.
|
|
32
|
+
*/
|
|
33
|
+
sharedHandles = new Map();
|
|
34
|
+
catalogCache;
|
|
35
|
+
/**
|
|
36
|
+
* Opt into the 2026-07-28 revision. Off by default: `mode: "auto"` probes with
|
|
37
|
+
* `server/discover` before falling back to `initialize`, and a legacy stdio
|
|
38
|
+
* server that ignores pre-`initialize` requests pays the full probe timeout.
|
|
39
|
+
*/
|
|
40
|
+
modernProtocol;
|
|
41
|
+
/**
|
|
42
|
+
* Host callback for server-initiated `elicitation/create`. Absent means we
|
|
43
|
+
* declare no elicitation capability at all — which is protocol-legal and is
|
|
44
|
+
* how the CLI runs, since it has nowhere to render the form.
|
|
45
|
+
*/
|
|
46
|
+
onElicit;
|
|
47
|
+
/**
|
|
48
|
+
* Pool backing `shared: true` servers. Defaults to the daemon-wide singleton;
|
|
49
|
+
* injectable so tests can exercise sharing without touching global state.
|
|
50
|
+
*/
|
|
51
|
+
pool;
|
|
52
|
+
/**
|
|
53
|
+
* Fired when a connected server's transport closes on its own — a crashed or
|
|
54
|
+
* exited child, not our own `dispose`. The pool uses it to drop a dead
|
|
55
|
+
* connection so the next session rebuilds instead of inheriting a corpse.
|
|
56
|
+
*/
|
|
57
|
+
onServerClosed;
|
|
58
|
+
/** Set while `dispose` runs, so our own teardown is not reported as a death. */
|
|
59
|
+
disposing = false;
|
|
60
|
+
constructor(opts = {}) {
|
|
61
|
+
this.catalogCache = opts.catalogCache ?? new McpCatalogCache();
|
|
62
|
+
this.modernProtocol = opts.modernProtocol ?? false;
|
|
63
|
+
this.onElicit = opts.onElicit;
|
|
64
|
+
this.pool = opts.sharedPool ?? sharedMcpPool;
|
|
65
|
+
this.onServerClosed = opts.onServerClosed;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build a `Client` for one server, declaring elicitation support and wiring
|
|
69
|
+
* the handler when the host can actually prompt the user.
|
|
70
|
+
*
|
|
71
|
+
* Form mode only. URL mode would have us open a browser tab in the middle of
|
|
72
|
+
* a tool call with no user gesture behind it — the SDK rejects URL-mode
|
|
73
|
+
* requests for us when the `url` capability is undeclared.
|
|
74
|
+
*/
|
|
75
|
+
createClient(serverName, negotiate) {
|
|
76
|
+
const handler = this.onElicit;
|
|
77
|
+
if (!handler)
|
|
78
|
+
return new Client({ name: "ezcoder", version: "1.0.0" }, negotiate);
|
|
79
|
+
const client = new Client({ name: "ezcoder", version: "1.0.0" }, { ...negotiate, capabilities: { elicitation: { form: {} } } });
|
|
80
|
+
client.setRequestHandler("elicitation/create", async (request) => {
|
|
81
|
+
const params = request.params;
|
|
82
|
+
if (params.mode === "url") {
|
|
83
|
+
// Unreachable while `url` stays undeclared, but a server that ignores
|
|
84
|
+
// capabilities must get a clean decline rather than a thrown handler.
|
|
85
|
+
return { action: "decline" };
|
|
86
|
+
}
|
|
87
|
+
return handler({
|
|
88
|
+
server: serverName,
|
|
89
|
+
message: params.message,
|
|
90
|
+
requestedSchema: params.requestedSchema,
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
return client;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Version-negotiation options for a real (non-probe) connect. `probe()` is
|
|
97
|
+
* spawn-per-invocation and deliberately never negotiates: a legacy stdio
|
|
98
|
+
* server that ignores the discovery request would stall it for the whole
|
|
99
|
+
* probe timeout, turning "validate this server" into a 30s hang.
|
|
100
|
+
*
|
|
101
|
+
* The probe timeout is transport-aware because the SDK's timeout VERDICT is:
|
|
102
|
+
*
|
|
103
|
+
* - **stdio** — silence on a local pipe means a legacy server, and the SDK
|
|
104
|
+
* falls back to `initialize`. The probe runs on a disposable sibling
|
|
105
|
+
* process, so the fallback is clean. Waiting the full connect timeout to
|
|
106
|
+
* reach a conclusion we can draw in seconds is pure dead time, so cap it
|
|
107
|
+
* short.
|
|
108
|
+
* - **HTTP** — silence means an outage, and the SDK REJECTS the connect
|
|
109
|
+
* rather than falling back. A short timeout would therefore turn a slow
|
|
110
|
+
* cold start into a hard connection failure, so inherit the full connect
|
|
111
|
+
* timeout and let the normal timeout handling deal with a real outage.
|
|
112
|
+
*/
|
|
113
|
+
negotiationOptions(transport) {
|
|
114
|
+
if (!this.modernProtocol)
|
|
115
|
+
return {};
|
|
116
|
+
return {
|
|
117
|
+
versionNegotiation: {
|
|
118
|
+
mode: "auto",
|
|
119
|
+
...(transport === "stdio" ? { probe: { timeoutMs: STDIO_PROBE_TIMEOUT_MS } } : {}),
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Claim the pooled connection for a shared server and adopt its tools.
|
|
125
|
+
*
|
|
126
|
+
* Shaped like `connectServer` (resolve tools / throw on failure) so the
|
|
127
|
+
* caller's `allSettled` handling is identical for both kinds. The pool
|
|
128
|
+
* reports failure in-band, so it is rethrown here with the message the pool
|
|
129
|
+
* already formatted.
|
|
130
|
+
*/
|
|
131
|
+
async connectShared(config) {
|
|
132
|
+
const handle = await this.pool.acquire(config,
|
|
133
|
+
// The pooled connection is just another manager with sharing switched
|
|
134
|
+
// off, so it takes the normal connect path instead of recursing into the
|
|
135
|
+
// pool. Its elicit handler is the pool's dispatcher, NOT this session's:
|
|
136
|
+
// the connection is shared, so each request has to be routed to whichever
|
|
137
|
+
// session actually provoked it.
|
|
138
|
+
(opts) => {
|
|
139
|
+
const manager = new MCPClientManager({
|
|
140
|
+
catalogCache: opts.catalogCache,
|
|
141
|
+
modernProtocol: opts.modernProtocol,
|
|
142
|
+
onElicit: opts.onElicit,
|
|
143
|
+
sharedPool: this.pool,
|
|
144
|
+
// Let the pool retire this connection if its server exits.
|
|
145
|
+
onServerClosed: opts.onClosed,
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
connect: async (target) => {
|
|
149
|
+
const [result] = await manager.connectAllDetailed([{ ...target, shared: false }]);
|
|
150
|
+
return (result ?? {
|
|
151
|
+
name: target.name,
|
|
152
|
+
ok: false,
|
|
153
|
+
toolCount: 0,
|
|
154
|
+
tools: [],
|
|
155
|
+
error: "connection produced no result",
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
dispose: () => manager.dispose(),
|
|
159
|
+
};
|
|
160
|
+
}, {
|
|
161
|
+
catalogCache: this.catalogCache,
|
|
162
|
+
modernProtocol: this.modernProtocol,
|
|
163
|
+
onElicit: this.onElicit,
|
|
164
|
+
});
|
|
165
|
+
if (!handle.result.ok) {
|
|
166
|
+
// Nothing was claimed on a failed connect, but releasing is idempotent
|
|
167
|
+
// and keeps this path honest if that ever changes.
|
|
168
|
+
await handle.release();
|
|
169
|
+
throw new Error(handle.result.error ?? "connection failed");
|
|
170
|
+
}
|
|
171
|
+
this.sharedHandles.set(config.name, handle);
|
|
172
|
+
// Wrap each pooled tool so a call from THIS session is attributable while
|
|
173
|
+
// it runs. The pool needs that to answer "which window asked?" when the
|
|
174
|
+
// server elicits mid-call; without it a shared server could only ever
|
|
175
|
+
// cancel its prompts.
|
|
176
|
+
return handle.result.tools.map((tool) => ({
|
|
177
|
+
...tool,
|
|
178
|
+
execute: async (args, context) => {
|
|
179
|
+
const endCall = handle.beginCall();
|
|
180
|
+
try {
|
|
181
|
+
return await tool.execute(args, context);
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
endCall();
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
/** Get-or-create the settlement record for one server name. */
|
|
190
|
+
connectionSlot(name) {
|
|
191
|
+
const existing = this.connections.get(name);
|
|
192
|
+
if (existing)
|
|
193
|
+
return existing;
|
|
194
|
+
let settle;
|
|
195
|
+
const promise = new Promise((resolve) => {
|
|
196
|
+
settle = resolve;
|
|
197
|
+
});
|
|
198
|
+
const slot = { promise, settle };
|
|
199
|
+
this.connections.set(name, slot);
|
|
200
|
+
return slot;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Resolve once a server's connection attempt has settled. Returns `ok:false`
|
|
204
|
+
* with the failure reason when that server could not connect, and times out
|
|
205
|
+
* rather than hanging when no attempt is ever made (e.g. a server that was
|
|
206
|
+
* removed from the config since the catalog cache was written).
|
|
207
|
+
*/
|
|
208
|
+
async whenConnected(name, timeoutMs = 30_000) {
|
|
209
|
+
if (this.servers.some((server) => server.name === name))
|
|
210
|
+
return { ok: true };
|
|
211
|
+
const slot = this.connectionSlot(name);
|
|
212
|
+
let timer;
|
|
213
|
+
const timeout = new Promise((resolve) => {
|
|
214
|
+
timer = setTimeout(() => resolve({ ok: false, error: `timed out after ${timeoutMs}ms` }), timeoutMs);
|
|
215
|
+
timer.unref?.();
|
|
216
|
+
});
|
|
217
|
+
try {
|
|
218
|
+
return await Promise.race([slot.promise, timeout]);
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
if (timer)
|
|
222
|
+
clearTimeout(timer);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
16
225
|
async connectAll(configs) {
|
|
17
226
|
const results = await this.connectAllDetailed(configs);
|
|
18
227
|
return results.flatMap((r) => r.tools);
|
|
@@ -21,12 +230,25 @@ export class MCPClientManager {
|
|
|
21
230
|
* Connect every enabled server and return one result per server (success →
|
|
22
231
|
* ok + toolCount; failure → ok:false with a human-readable error string).
|
|
23
232
|
* Keeps successfully connected servers in `this.servers`.
|
|
233
|
+
*
|
|
234
|
+
* Shareable servers — stdio, unless opted out with `shared: false` — are not
|
|
235
|
+
* connected here at all: they are claimed from the process-wide pool, so every
|
|
236
|
+
* session in the daemon multiplexes over one child process instead of spawning
|
|
237
|
+
* its own. The split is invisible to callers — results and tools come back in
|
|
238
|
+
* the same shape, `whenConnected` settles for both kinds, and `dispose`
|
|
239
|
+
* releases the pooled claims — so sharing applies wherever a manager is used
|
|
240
|
+
* (sessions, CLI, subagents) without each call site opting in.
|
|
24
241
|
*/
|
|
25
242
|
async connectAllDetailed(configs) {
|
|
26
243
|
const enabled = configs.filter((c) => c.enabled !== false);
|
|
27
244
|
if (enabled.length === 0)
|
|
28
245
|
return [];
|
|
29
|
-
|
|
246
|
+
// Claim a settlement slot for every server up front, so a `whenConnected`
|
|
247
|
+
// caller that arrives before a slow server's turn still waits on the right
|
|
248
|
+
// promise instead of creating a second one.
|
|
249
|
+
for (const config of enabled)
|
|
250
|
+
this.connectionSlot(config.name);
|
|
251
|
+
const settled = await Promise.allSettled(enabled.map((c) => (isShareableServer(c) ? this.connectShared(c) : this.connectServer(c))));
|
|
30
252
|
const results = settled.map((result, i) => {
|
|
31
253
|
const name = enabled[i].name;
|
|
32
254
|
if (result.status === "fulfilled") {
|
|
@@ -37,6 +259,10 @@ export class MCPClientManager {
|
|
|
37
259
|
log("WARN", "mcp", `Failed to connect to MCP server "${name}"`, { error });
|
|
38
260
|
return { name, ok: false, toolCount: 0, tools: [], error, requiresAuth };
|
|
39
261
|
});
|
|
262
|
+
// Release anyone waiting on a cached-only tool from these servers.
|
|
263
|
+
for (const result of results) {
|
|
264
|
+
this.connectionSlot(result.name).settle(result.ok ? { ok: true } : { ok: false, error: result.error ?? "connection failed" });
|
|
265
|
+
}
|
|
40
266
|
const connected = results.filter((r) => r.ok).length;
|
|
41
267
|
const toolCount = results.reduce((sum, r) => sum + r.toolCount, 0);
|
|
42
268
|
log("INFO", "mcp", `Connected ${connected} MCP server(s), ${toolCount} tool(s)`);
|
|
@@ -49,7 +275,7 @@ export class MCPClientManager {
|
|
|
49
275
|
*/
|
|
50
276
|
async probe(config) {
|
|
51
277
|
try {
|
|
52
|
-
const tools = await this.connectServer(config);
|
|
278
|
+
const tools = await this.connectServer(config, { probe: true });
|
|
53
279
|
const server = this.servers.find((s) => s.name === config.name);
|
|
54
280
|
if (server) {
|
|
55
281
|
this.servers = this.servers.filter((s) => s !== server);
|
|
@@ -88,6 +314,9 @@ export class MCPClientManager {
|
|
|
88
314
|
// Fresh PKCE/state for this attempt so a previous half-finished login can't
|
|
89
315
|
// poison the exchange.
|
|
90
316
|
await store.patch(config.name, { codeVerifier: undefined, state: undefined });
|
|
317
|
+
// The whole callback query string, not just `code`: v2's finishAuth reads
|
|
318
|
+
// `iss` from it and validates the issuer (RFC 9207) before redeeming the
|
|
319
|
+
// code, which is the defense against an authorization-server mix-up.
|
|
91
320
|
let codeResolve;
|
|
92
321
|
let codeReject;
|
|
93
322
|
const codePromise = new Promise((resolve, reject) => {
|
|
@@ -129,7 +358,7 @@ export class MCPClientManager {
|
|
|
129
358
|
return;
|
|
130
359
|
}
|
|
131
360
|
res.end("<html><body><h1>Login successful!</h1><p>You can close this tab and return to EZ Coder.</p></body></html>");
|
|
132
|
-
codeResolve?.(
|
|
361
|
+
codeResolve?.(reqUrl.searchParams);
|
|
133
362
|
});
|
|
134
363
|
try {
|
|
135
364
|
await new Promise((resolve, reject) => {
|
|
@@ -167,9 +396,9 @@ export class MCPClientManager {
|
|
|
167
396
|
if (!isUnauthorized(err))
|
|
168
397
|
throw err;
|
|
169
398
|
}
|
|
170
|
-
const
|
|
399
|
+
const callbackParams = await codePromise;
|
|
171
400
|
// Exchange the code for tokens (persisted via provider.saveTokens).
|
|
172
|
-
await loginTransport.finishAuth(
|
|
401
|
+
await loginTransport.finishAuth(callbackParams);
|
|
173
402
|
await loginClient.close().catch(() => { });
|
|
174
403
|
// Verify the authorized connection on a fresh transport + list tools.
|
|
175
404
|
const verifyTransport = new StreamableHTTPClientTransport(url, {
|
|
@@ -192,8 +421,20 @@ export class MCPClientManager {
|
|
|
192
421
|
await store.patch(config.name, { codeVerifier: undefined, state: undefined });
|
|
193
422
|
}
|
|
194
423
|
}
|
|
195
|
-
async connectServer(config) {
|
|
424
|
+
async connectServer(config, opts = {}) {
|
|
196
425
|
const timeout = config.timeout ?? 30_000;
|
|
426
|
+
// `command` is what actually selects the stdio path further down.
|
|
427
|
+
const negotiate = opts.probe
|
|
428
|
+
? {}
|
|
429
|
+
: this.negotiationOptions(config.command ? "stdio" : "http");
|
|
430
|
+
// A server we already know speaks the 2025 handshake skips the discovery
|
|
431
|
+
// probe entirely. Only the legacy verdict is reusable from an era string:
|
|
432
|
+
// adopting a 'modern' verdict needs the full DiscoverResult, so those still
|
|
433
|
+
// probe (a modern server answers immediately, so that costs nothing).
|
|
434
|
+
const prior = "versionNegotiation" in negotiate && (await this.cachedEra(config)) === "legacy"
|
|
435
|
+
? { kind: "legacy" }
|
|
436
|
+
: undefined;
|
|
437
|
+
const connectOptions = { timeout, ...(prior ? { prior } : {}) };
|
|
197
438
|
let client;
|
|
198
439
|
let transport;
|
|
199
440
|
if (config.command) {
|
|
@@ -226,9 +467,9 @@ export class MCPClientManager {
|
|
|
226
467
|
transport.stderr?.on("data", (chunk) => {
|
|
227
468
|
stderrChunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
|
|
228
469
|
});
|
|
229
|
-
client =
|
|
470
|
+
client = this.createClient(config.name, negotiate);
|
|
230
471
|
try {
|
|
231
|
-
await client.connect(transport,
|
|
472
|
+
await client.connect(transport, connectOptions);
|
|
232
473
|
}
|
|
233
474
|
catch (err) {
|
|
234
475
|
const stderr = stderrChunks.join("").slice(-4000);
|
|
@@ -244,7 +485,7 @@ export class MCPClientManager {
|
|
|
244
485
|
const url = new URL(config.url);
|
|
245
486
|
const isLocal = isLocalhost(url);
|
|
246
487
|
try {
|
|
247
|
-
const r = await this.connectHttp(url, config, isLocal,
|
|
488
|
+
const r = await this.connectHttp(url, config, isLocal, negotiate, connectOptions);
|
|
248
489
|
client = r.client;
|
|
249
490
|
transport = r.transport;
|
|
250
491
|
}
|
|
@@ -261,13 +502,40 @@ export class MCPClientManager {
|
|
|
261
502
|
});
|
|
262
503
|
const altUrl = new URL(url);
|
|
263
504
|
altUrl.hostname = alt;
|
|
264
|
-
const r = await this.connectHttp(altUrl, config, isLocal,
|
|
505
|
+
const r = await this.connectHttp(altUrl, config, isLocal, negotiate, connectOptions);
|
|
265
506
|
client = r.client;
|
|
266
507
|
transport = r.transport;
|
|
267
508
|
}
|
|
268
509
|
}
|
|
269
|
-
this.servers.push({ name: config.name, client, transport, lastCallTime: 0 });
|
|
510
|
+
this.servers.push({ name: config.name, client, transport, lastCallTime: 0, config });
|
|
511
|
+
// Report an unexpected close. A stdio child that crashes has no recovery
|
|
512
|
+
// path (`canRecoverSession` excludes stdio deliberately: respawning one
|
|
513
|
+
// mid-call would be a surprise), so the only safe response is to let the
|
|
514
|
+
// owner drop this connection and rebuild on the next use. That matters most
|
|
515
|
+
// for a POOLED connection, where a corpse would otherwise be handed to every
|
|
516
|
+
// session in the daemon, including ones that connect later.
|
|
517
|
+
client.onclose = () => {
|
|
518
|
+
if (this.disposing)
|
|
519
|
+
return;
|
|
520
|
+
log("WARN", "mcp", `MCP server "${config.name}" closed unexpectedly`);
|
|
521
|
+
this.onServerClosed?.(config.name);
|
|
522
|
+
};
|
|
270
523
|
const { tools } = await client.listTools(undefined, { timeout });
|
|
524
|
+
// Persist the live tool list so the NEXT cold start can answer tool_search
|
|
525
|
+
// before this server has finished connecting. Awaited (a small serialized
|
|
526
|
+
// JSON write) so the entry is on disk before the connect is reported done —
|
|
527
|
+
// otherwise a session that starts right after would still see a cold cache.
|
|
528
|
+
// A failed write must never fail the connect.
|
|
529
|
+
try {
|
|
530
|
+
await this.catalogCache.save(config, tools.map((tool) => ({
|
|
531
|
+
name: `mcp__${config.name}__${tool.name}`,
|
|
532
|
+
description: tool.description ?? "",
|
|
533
|
+
rawInputSchema: tool.inputSchema,
|
|
534
|
+
})), this.negotiatedEra(client));
|
|
535
|
+
}
|
|
536
|
+
catch {
|
|
537
|
+
// Cache is an optimization; a connected server is still fully usable.
|
|
538
|
+
}
|
|
271
539
|
return tools.map((tool) => {
|
|
272
540
|
const toolName = `mcp__${config.name}__${tool.name}`;
|
|
273
541
|
return {
|
|
@@ -275,7 +543,7 @@ export class MCPClientManager {
|
|
|
275
543
|
description: tool.description ?? "",
|
|
276
544
|
parameters: z.record(z.string(), z.unknown()),
|
|
277
545
|
rawInputSchema: tool.inputSchema,
|
|
278
|
-
execute: async (args) => {
|
|
546
|
+
execute: async (args, context) => {
|
|
279
547
|
const server = this.servers.find((s) => s.name === config.name);
|
|
280
548
|
if (server) {
|
|
281
549
|
const elapsed = Date.now() - server.lastCallTime;
|
|
@@ -285,8 +553,16 @@ export class MCPClientManager {
|
|
|
285
553
|
}
|
|
286
554
|
server.lastCallTime = Date.now();
|
|
287
555
|
}
|
|
288
|
-
|
|
289
|
-
|
|
556
|
+
// Resolve the client from `this.servers` on every attempt rather than
|
|
557
|
+
// closing over the one from connect time: a session rebuild swaps the
|
|
558
|
+
// entry, and a stale capture would keep calling the dead client.
|
|
559
|
+
const liveClient = () => this.servers.find((s) => s.name === config.name)?.client ?? client;
|
|
560
|
+
// The client the attempt actually ran against, so a failure can be
|
|
561
|
+
// attributed to "my client was replaced" vs "the server hung up".
|
|
562
|
+
let attemptClient = liveClient();
|
|
563
|
+
const callOnce = async () => {
|
|
564
|
+
attemptClient = liveClient();
|
|
565
|
+
const result = await attemptClient.callTool({ name: tool.name, arguments: args }, { timeout: config.timeout ?? 60_000 });
|
|
290
566
|
if (!("content" in result) || !Array.isArray(result.content)) {
|
|
291
567
|
return "(empty response)";
|
|
292
568
|
}
|
|
@@ -300,8 +576,23 @@ export class MCPClientManager {
|
|
|
300
576
|
}
|
|
301
577
|
}
|
|
302
578
|
return texts.join("\n") || "(empty response)";
|
|
579
|
+
};
|
|
580
|
+
try {
|
|
581
|
+
return await callOnce();
|
|
303
582
|
}
|
|
304
583
|
catch (err) {
|
|
584
|
+
// An expired HTTP session is recoverable exactly once: rebuild the
|
|
585
|
+
// connection and replay the call. A second failure is a real error.
|
|
586
|
+
if (this.canRecoverSession(config, err, attemptClient, context?.signal)) {
|
|
587
|
+
try {
|
|
588
|
+
await this.reconnectServer(config);
|
|
589
|
+
if (!context?.signal?.aborted)
|
|
590
|
+
return await callOnce();
|
|
591
|
+
}
|
|
592
|
+
catch (retryErr) {
|
|
593
|
+
return `MCP tool error: ${formatConnectError(retryErr)}`;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
305
596
|
const msg = err instanceof Error ? err.message : String(err);
|
|
306
597
|
if (msg.includes("Too Many R") || msg.includes("429")) {
|
|
307
598
|
return "Rate limited — too many requests. Wait a moment before searching again.";
|
|
@@ -312,6 +603,66 @@ export class MCPClientManager {
|
|
|
312
603
|
};
|
|
313
604
|
});
|
|
314
605
|
}
|
|
606
|
+
/**
|
|
607
|
+
* Is this failure a recoverable expired HTTP session?
|
|
608
|
+
*
|
|
609
|
+
* Stdio servers are excluded: they have no HTTP session to expire, so a 404
|
|
610
|
+
* from one means something else entirely and respawning the child process
|
|
611
|
+
* mid-call would be a surprise. An already-aborted call is excluded too —
|
|
612
|
+
* reconnecting to replay work the user cancelled resurrects it.
|
|
613
|
+
*/
|
|
614
|
+
canRecoverSession(config, err, attemptClient, signal) {
|
|
615
|
+
if (config.command)
|
|
616
|
+
return false;
|
|
617
|
+
if (signal?.aborted)
|
|
618
|
+
return false;
|
|
619
|
+
if (isSessionExpired(err))
|
|
620
|
+
return true;
|
|
621
|
+
// A sibling call that hit the same expired session may already have torn
|
|
622
|
+
// the transport down underneath us, which surfaces as a generic "connection
|
|
623
|
+
// closed" rather than the 404. Recover only when the client we called has
|
|
624
|
+
// genuinely been retired — otherwise this is a real disconnect and a retry
|
|
625
|
+
// would just fail again.
|
|
626
|
+
const current = this.servers.find((s) => s.name === config.name)?.client;
|
|
627
|
+
return current !== attemptClient && this.isConnectionClosed(err);
|
|
628
|
+
}
|
|
629
|
+
/** A local "the transport went away mid-request" failure, as the SDK reports it. */
|
|
630
|
+
isConnectionClosed(reason) {
|
|
631
|
+
return SdkError.isInstance(reason) && reason.code === SdkErrorCode.ConnectionClosed;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Rebuild one server's connection from its stored config, replacing the dead
|
|
635
|
+
* entry in `this.servers`. Concurrent callers share a single rebuild.
|
|
636
|
+
*
|
|
637
|
+
* Rejects if the reconnect itself fails, so the caller reports a real error
|
|
638
|
+
* rather than retrying against a client that was never replaced.
|
|
639
|
+
*/
|
|
640
|
+
async reconnectServer(config) {
|
|
641
|
+
const inFlight = this.reconnecting.get(config.name);
|
|
642
|
+
if (inFlight)
|
|
643
|
+
return inFlight;
|
|
644
|
+
const attempt = (async () => {
|
|
645
|
+
log("INFO", "mcp", `MCP session expired for "${config.name}", reconnecting`);
|
|
646
|
+
const dead = this.servers.find((s) => s.name === config.name);
|
|
647
|
+
if (dead) {
|
|
648
|
+
this.servers = this.servers.filter((s) => s !== dead);
|
|
649
|
+
try {
|
|
650
|
+
await dead.client.close();
|
|
651
|
+
}
|
|
652
|
+
catch {
|
|
653
|
+
// The session is already gone server-side; a failed close is expected.
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
await this.connectServer(config);
|
|
657
|
+
})();
|
|
658
|
+
this.reconnecting.set(config.name, attempt);
|
|
659
|
+
try {
|
|
660
|
+
await attempt;
|
|
661
|
+
}
|
|
662
|
+
finally {
|
|
663
|
+
this.reconnecting.delete(config.name);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
315
666
|
/**
|
|
316
667
|
* Connect a single HTTP (Streamable HTTP or SSE) server and return the live
|
|
317
668
|
* client + transport. Transport selection:
|
|
@@ -322,7 +673,7 @@ export class MCPClientManager {
|
|
|
322
673
|
* needs OAuth and attaching it there is dead weight that can misdiagnose a
|
|
323
674
|
* protocol mismatch as a login requirement.
|
|
324
675
|
*/
|
|
325
|
-
async connectHttp(url, config, isLocal,
|
|
676
|
+
async connectHttp(url, config, isLocal, negotiate, connectOptions) {
|
|
326
677
|
const reqInit = config.headers ? { headers: config.headers } : undefined;
|
|
327
678
|
const authProvider = isLocal ? undefined : new McpOAuthProvider({ serverName: config.name });
|
|
328
679
|
const sseTransport = () => new SSEClientTransport(url, {
|
|
@@ -332,8 +683,8 @@ export class MCPClientManager {
|
|
|
332
683
|
});
|
|
333
684
|
if (config.transport === "sse") {
|
|
334
685
|
const transport = sseTransport();
|
|
335
|
-
const client =
|
|
336
|
-
await client.connect(transport,
|
|
686
|
+
const client = this.createClient(config.name, negotiate);
|
|
687
|
+
await client.connect(transport, connectOptions);
|
|
337
688
|
return { client, transport };
|
|
338
689
|
}
|
|
339
690
|
try {
|
|
@@ -341,8 +692,8 @@ export class MCPClientManager {
|
|
|
341
692
|
requestInit: reqInit,
|
|
342
693
|
authProvider,
|
|
343
694
|
});
|
|
344
|
-
const client =
|
|
345
|
-
await client.connect(transport,
|
|
695
|
+
const client = this.createClient(config.name, negotiate);
|
|
696
|
+
await client.connect(transport, connectOptions);
|
|
346
697
|
return { client, transport };
|
|
347
698
|
}
|
|
348
699
|
catch (streamableErr) {
|
|
@@ -356,12 +707,40 @@ export class MCPClientManager {
|
|
|
356
707
|
error: String(streamableErr),
|
|
357
708
|
});
|
|
358
709
|
const transport = sseTransport();
|
|
359
|
-
const client =
|
|
360
|
-
await client.connect(transport,
|
|
710
|
+
const client = this.createClient(config.name, negotiate);
|
|
711
|
+
await client.connect(transport, connectOptions);
|
|
361
712
|
return { client, transport };
|
|
362
713
|
}
|
|
363
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* Protocol era actually negotiated with a server, as the SDK reports it.
|
|
717
|
+
* Undefined before a connect completes; with negotiation off the SDK always
|
|
718
|
+
* settles on `legacy` (the 2025 `initialize` handshake).
|
|
719
|
+
*/
|
|
720
|
+
negotiatedEra(client) {
|
|
721
|
+
return client.getProtocolEra() ?? "legacy";
|
|
722
|
+
}
|
|
723
|
+
/** Previously negotiated era for this exact server config, if still cached. */
|
|
724
|
+
async cachedEra(config) {
|
|
725
|
+
try {
|
|
726
|
+
return await this.catalogCache.protocolEraFor(config);
|
|
727
|
+
}
|
|
728
|
+
catch {
|
|
729
|
+
return undefined;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
364
732
|
async dispose() {
|
|
733
|
+
// Our own teardown closes transports too; flag it so those closes are not
|
|
734
|
+
// reported as unexpected deaths.
|
|
735
|
+
this.disposing = true;
|
|
736
|
+
this.connections.clear();
|
|
737
|
+
this.reconnecting.clear();
|
|
738
|
+
// Hand back pooled claims first. These are references, not connections —
|
|
739
|
+
// the shared child survives if another session still holds it, and exits
|
|
740
|
+
// when this was the last claim.
|
|
741
|
+
const handles = [...this.sharedHandles.values()];
|
|
742
|
+
this.sharedHandles.clear();
|
|
743
|
+
await Promise.all(handles.map((handle) => handle.release()));
|
|
365
744
|
for (const server of this.servers) {
|
|
366
745
|
try {
|
|
367
746
|
await server.client.close();
|
|
@@ -374,25 +753,81 @@ export class MCPClientManager {
|
|
|
374
753
|
}
|
|
375
754
|
}
|
|
376
755
|
/**
|
|
377
|
-
* Turn a thrown connection error into a short human-readable string.
|
|
378
|
-
*
|
|
756
|
+
* Turn a thrown connection error into a short human-readable string.
|
|
757
|
+
*
|
|
758
|
+
* v2 splits the old `McpError` into `ProtocolError` (a JSON-RPC error that
|
|
759
|
+
* crossed the wire) and `SdkError` (a purely local failure: timeout, closed
|
|
760
|
+
* connection, HTTP status). Matching the typed classes gives far better wording
|
|
761
|
+
* than sniffing message text, which survives only as the fallback for errors
|
|
762
|
+
* thrown before the SDK's machinery engages (raw fetch failures).
|
|
379
763
|
*/
|
|
380
764
|
function formatConnectError(reason) {
|
|
765
|
+
if (SdkHttpError.isInstance(reason)) {
|
|
766
|
+
if (reason.status === 429)
|
|
767
|
+
return "Rate limited (429) — try again in a moment.";
|
|
768
|
+
const statusText = reason.statusText ? ` ${reason.statusText}` : "";
|
|
769
|
+
return `Server responded with HTTP ${reason.status}${statusText}.`;
|
|
770
|
+
}
|
|
771
|
+
if (SdkError.isInstance(reason)) {
|
|
772
|
+
if (reason.code === SdkErrorCode.RequestTimeout) {
|
|
773
|
+
return "Timed out waiting for the server to respond.";
|
|
774
|
+
}
|
|
775
|
+
if (reason.code === SdkErrorCode.ConnectionClosed) {
|
|
776
|
+
return "Connection closed before the server finished starting up.";
|
|
777
|
+
}
|
|
778
|
+
return reason.message;
|
|
779
|
+
}
|
|
780
|
+
if (OAuthError.isInstance(reason))
|
|
781
|
+
return `OAuth error (${reason.code}): ${reason.message}`;
|
|
782
|
+
if (ProtocolError.isInstance(reason))
|
|
783
|
+
return reason.message;
|
|
381
784
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
382
785
|
if (msg.includes("Too Many R") || msg.includes("429")) {
|
|
383
786
|
return "Rate limited (429) — try again in a moment.";
|
|
384
787
|
}
|
|
385
788
|
return msg;
|
|
386
789
|
}
|
|
790
|
+
/**
|
|
791
|
+
* How long to wait for a stdio server to answer `server/discover` before
|
|
792
|
+
* concluding it is a 2025-era server. A local child that has not replied in
|
|
793
|
+
* this window is not going to; the SDK then falls back to `initialize`.
|
|
794
|
+
*/
|
|
795
|
+
const STDIO_PROBE_TIMEOUT_MS = 2_500;
|
|
796
|
+
/** OAuth failures that mean the saved credentials can no longer be used. */
|
|
797
|
+
const REAUTH_OAUTH_CODES = [
|
|
798
|
+
OAuthErrorCode.InvalidToken,
|
|
799
|
+
OAuthErrorCode.InvalidGrant,
|
|
800
|
+
OAuthErrorCode.InvalidClient,
|
|
801
|
+
OAuthErrorCode.AccessDenied,
|
|
802
|
+
];
|
|
803
|
+
/**
|
|
804
|
+
* Whether an error means the remote server has dropped our HTTP session.
|
|
805
|
+
*
|
|
806
|
+
* `StreamableHTTPClientTransport.send` handles 401/403 (it re-runs auth) but
|
|
807
|
+
* returns a bare `SdkHttpError{status:404}` when the `Mcp-Session-Id` we are
|
|
808
|
+
* carrying is no longer known to the server — which is exactly what happens
|
|
809
|
+
* after a server restart or an idle-session sweep on a long desktop session.
|
|
810
|
+
* The SDK has no recovery for it, so we rebuild the connection ourselves.
|
|
811
|
+
*/
|
|
812
|
+
function isSessionExpired(reason) {
|
|
813
|
+
return SdkHttpError.isInstance(reason) && reason.status === 404;
|
|
814
|
+
}
|
|
387
815
|
/**
|
|
388
816
|
* Whether a connect error means the server needs OAuth login. The SDK throws
|
|
389
|
-
* its typed `UnauthorizedError` when auth is required,
|
|
390
|
-
*
|
|
391
|
-
*
|
|
817
|
+
* its typed `UnauthorizedError` when auth is required, a `SdkHttpError` for a
|
|
818
|
+
* bare 401, and an `OAuthError` when the token endpoint rejects the saved
|
|
819
|
+
* credentials. A raw 401 from before the auth machinery engages can still
|
|
820
|
+
* surface as a plain message — so the string check remains as a fallback.
|
|
392
821
|
*/
|
|
393
822
|
function isUnauthorized(reason) {
|
|
394
|
-
if (reason
|
|
823
|
+
if (UnauthorizedError.isInstance(reason))
|
|
395
824
|
return true;
|
|
825
|
+
if (SdkHttpError.isInstance(reason))
|
|
826
|
+
return reason.status === 401;
|
|
827
|
+
// Token/grant/client codes mean "log in again"; the rest (server_error,
|
|
828
|
+
// temporarily_unavailable, …) are transient, not an auth gap.
|
|
829
|
+
if (OAuthError.isInstance(reason))
|
|
830
|
+
return REAUTH_OAUTH_CODES.includes(reason.code);
|
|
396
831
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
397
832
|
return (msg.includes("Unauthorized") ||
|
|
398
833
|
msg.includes("401") ||
|
|
@@ -410,12 +845,18 @@ function escapeHtml(value) {
|
|
|
410
845
|
* Create a custom fetch wrapper that injects extra headers into every request.
|
|
411
846
|
* Used for SSEClientTransport's eventSourceInit to pass auth headers
|
|
412
847
|
* on the initial SSE GET connection (which doesn't use requestInit).
|
|
848
|
+
*
|
|
849
|
+
* The incoming headers may be a Web Standard `Headers` instance (v2 uses it
|
|
850
|
+
* throughout) or a plain object. Spreading a `Headers` yields `{}`, silently
|
|
851
|
+
* dropping the SDK's own auth header — so normalize through `Headers`, which
|
|
852
|
+
* accepts both shapes, and merge with `set()` rather than bracket access.
|
|
413
853
|
*/
|
|
414
854
|
function createHeaderFetch(extraHeaders) {
|
|
415
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
416
855
|
return (url, init) => {
|
|
417
|
-
const
|
|
418
|
-
|
|
856
|
+
const headers = new Headers(init?.headers);
|
|
857
|
+
for (const [name, value] of Object.entries(extraHeaders))
|
|
858
|
+
headers.set(name, value);
|
|
859
|
+
return fetch(url, { ...init, headers });
|
|
419
860
|
};
|
|
420
861
|
}
|
|
421
862
|
//# sourceMappingURL=client.js.map
|