@machina.ai/cell-cli-core 1.19.4-rc3 → 1.22.5-rc1
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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/agents/delegate-to-agent-tool.d.ts +19 -0
- package/dist/src/agents/delegate-to-agent-tool.js +111 -0
- package/dist/src/agents/delegate-to-agent-tool.js.map +1 -0
- package/dist/src/agents/delegate-to-agent-tool.test.d.ts +6 -0
- package/dist/src/agents/delegate-to-agent-tool.test.js +133 -0
- package/dist/src/agents/delegate-to-agent-tool.test.js.map +1 -0
- package/dist/src/agents/executor.js +12 -17
- package/dist/src/agents/executor.js.map +1 -1
- package/dist/src/agents/executor.test.js +8 -9
- package/dist/src/agents/executor.test.js.map +1 -1
- package/dist/src/agents/registry.d.ts +15 -0
- package/dist/src/agents/registry.js +58 -2
- package/dist/src/agents/registry.js.map +1 -1
- package/dist/src/agents/registry.test.js +61 -0
- package/dist/src/agents/registry.test.js.map +1 -1
- package/dist/src/availability/errorClassification.d.ts +7 -0
- package/dist/src/availability/errorClassification.js +20 -0
- package/dist/src/availability/errorClassification.js.map +1 -0
- package/dist/src/availability/modelAvailabilityService.d.ts +2 -1
- package/dist/src/availability/modelAvailabilityService.js +5 -2
- package/dist/src/availability/modelAvailabilityService.js.map +1 -1
- package/dist/src/availability/modelAvailabilityService.test.js +3 -3
- package/dist/src/availability/modelAvailabilityService.test.js.map +1 -1
- package/dist/src/availability/modelPolicy.d.ts +8 -1
- package/dist/src/availability/policyCatalog.d.ts +4 -1
- package/dist/src/availability/policyCatalog.js +8 -9
- package/dist/src/availability/policyCatalog.js.map +1 -1
- package/dist/src/availability/policyCatalog.test.js +2 -2
- package/dist/src/availability/policyCatalog.test.js.map +1 -1
- package/dist/src/availability/policyHelpers.d.ts +51 -0
- package/dist/src/availability/policyHelpers.js +145 -0
- package/dist/src/availability/policyHelpers.js.map +1 -0
- package/dist/src/availability/policyHelpers.test.d.ts +6 -0
- package/dist/src/availability/policyHelpers.test.js +172 -0
- package/dist/src/availability/policyHelpers.test.js.map +1 -0
- package/dist/src/availability/testUtils.d.ts +10 -0
- package/dist/src/availability/testUtils.js +22 -0
- package/dist/src/availability/testUtils.js.map +1 -0
- package/dist/src/code_assist/experiments/client_metadata.js +3 -2
- package/dist/src/code_assist/experiments/client_metadata.js.map +1 -1
- package/dist/src/code_assist/experiments/client_metadata.test.js +7 -10
- package/dist/src/code_assist/experiments/client_metadata.test.js.map +1 -1
- package/dist/src/code_assist/experiments/experiments.js +2 -2
- package/dist/src/code_assist/experiments/experiments.js.map +1 -1
- package/dist/src/code_assist/oauth2.d.ts +2 -0
- package/dist/src/code_assist/oauth2.js +41 -15
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +114 -7
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/code_assist/server.d.ts +2 -1
- package/dist/src/code_assist/server.js +7 -4
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +24 -0
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/code_assist/types.d.ts +14 -0
- package/dist/src/commands/init.d.ts +7 -0
- package/dist/src/commands/init.js +53 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/init.test.d.ts +6 -0
- package/dist/src/commands/init.test.js +25 -0
- package/dist/src/commands/init.test.js.map +1 -0
- package/dist/src/commands/restore.d.ts +9 -0
- package/dist/src/commands/restore.js +46 -0
- package/dist/src/commands/restore.js.map +1 -0
- package/dist/src/commands/restore.test.d.ts +6 -0
- package/dist/src/commands/restore.test.js +137 -0
- package/dist/src/commands/restore.test.js.map +1 -0
- package/dist/src/commands/types.d.ts +41 -0
- package/dist/src/commands/types.js +7 -0
- package/dist/src/commands/types.js.map +1 -0
- package/dist/src/config/config.d.ts +43 -3
- package/dist/src/config/config.js +182 -29
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +217 -9
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/defaultModelConfigs.js +21 -0
- package/dist/src/config/defaultModelConfigs.js.map +1 -1
- package/dist/src/config/models.d.ts +33 -11
- package/dist/src/config/models.js +82 -24
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/models.test.js +70 -76
- package/dist/src/config/models.test.js.map +1 -1
- package/dist/src/confirmation-bus/message-bus.js +1 -0
- package/dist/src/confirmation-bus/message-bus.js.map +1 -1
- package/dist/src/confirmation-bus/types.d.ts +4 -0
- package/dist/src/core/AuthenticatedContentGenerator.js +4 -4
- package/dist/src/core/AuthenticatedContentGenerator.js.map +1 -1
- package/dist/src/core/baseLlmClient.d.ts +3 -1
- package/dist/src/core/baseLlmClient.js +40 -3
- package/dist/src/core/baseLlmClient.js.map +1 -1
- package/dist/src/core/baseLlmClient.test.js +184 -7
- package/dist/src/core/baseLlmClient.test.js.map +1 -1
- package/dist/src/core/client.js +52 -51
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +178 -6
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.js +5 -3
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +29 -22
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolHookTriggers.d.ts +55 -0
- package/dist/src/core/coreToolHookTriggers.js +244 -0
- package/dist/src/core/coreToolHookTriggers.js.map +1 -0
- package/dist/src/core/coreToolScheduler.d.ts +1 -1
- package/dist/src/core/coreToolScheduler.js +87 -36
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +201 -38
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.js +144 -41
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +251 -192
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/geminiChatHookTriggers.d.ts +64 -0
- package/dist/src/core/geminiChatHookTriggers.js +136 -0
- package/dist/src/core/geminiChatHookTriggers.js.map +1 -0
- package/dist/src/core/geminiChat_network_retry.test.d.ts +6 -0
- package/dist/src/core/geminiChat_network_retry.test.js +198 -0
- package/dist/src/core/geminiChat_network_retry.test.js.map +1 -0
- package/dist/src/core/logger.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.js +9 -4
- package/dist/src/core/loggingContentGenerator.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +4 -5
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/prompts.js +50 -29
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +19 -8
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/core/sessionHookTriggers.d.ts +28 -0
- package/dist/src/core/sessionHookTriggers.js +68 -0
- package/dist/src/core/sessionHookTriggers.js.map +1 -0
- package/dist/src/core/turn.d.ts +1 -0
- package/dist/src/core/turn.js +1 -1
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/fallback/handler.js +82 -69
- package/dist/src/fallback/handler.js.map +1 -1
- package/dist/src/fallback/handler.test.js +186 -170
- package/dist/src/fallback/handler.test.js.map +1 -1
- package/dist/src/fallback/types.d.ts +8 -0
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/hooks/hookEventHandler.js +70 -12
- package/dist/src/hooks/hookEventHandler.js.map +1 -1
- package/dist/src/hooks/hookEventHandler.test.js +8 -1
- package/dist/src/hooks/hookEventHandler.test.js.map +1 -1
- package/dist/src/hooks/hookRegistry.d.ts +0 -7
- package/dist/src/hooks/hookRegistry.js +8 -21
- package/dist/src/hooks/hookRegistry.js.map +1 -1
- package/dist/src/hooks/hookRegistry.test.js +2 -7
- package/dist/src/hooks/hookRegistry.test.js.map +1 -1
- package/dist/src/hooks/hookRunner.js +19 -3
- package/dist/src/hooks/hookRunner.js.map +1 -1
- package/dist/src/hooks/hookRunner.test.js +2 -1
- package/dist/src/hooks/hookRunner.test.js.map +1 -1
- package/dist/src/hooks/hookSystem.d.ts +0 -8
- package/dist/src/hooks/hookSystem.js +0 -18
- package/dist/src/hooks/hookSystem.js.map +1 -1
- package/dist/src/hooks/hookSystem.test.js +124 -18
- package/dist/src/hooks/hookSystem.test.js.map +1 -1
- package/dist/src/hooks/index.d.ts +3 -1
- package/dist/src/hooks/index.js +3 -0
- package/dist/src/hooks/index.js.map +1 -1
- package/dist/src/hooks/types.d.ts +1 -2
- package/dist/src/hooks/types.js +0 -1
- package/dist/src/hooks/types.js.map +1 -1
- package/dist/src/ide/detect-ide.test.js +32 -1
- package/dist/src/ide/detect-ide.test.js.map +1 -1
- package/dist/src/ide/ide-client.js +5 -3
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-client.test.js +17 -0
- package/dist/src/ide/ide-client.test.js.map +1 -1
- package/dist/src/ide/ide-installer.test.js +1 -1
- package/dist/src/ide/ide-installer.test.js.map +1 -1
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/auth-provider.d.ts +16 -0
- package/dist/src/mcp/auth-provider.js +7 -0
- package/dist/src/mcp/auth-provider.js.map +1 -0
- package/dist/src/mcp/google-auth-provider.d.ts +10 -2
- package/dist/src/mcp/google-auth-provider.js +28 -0
- package/dist/src/mcp/google-auth-provider.js.map +1 -1
- package/dist/src/mcp/google-auth-provider.test.js +45 -0
- package/dist/src/mcp/google-auth-provider.test.js.map +1 -1
- package/dist/src/mcp/mcpLauncher.js +6 -3
- package/dist/src/mcp/mcpLauncher.js.map +1 -1
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/sa-impersonation-provider.d.ts +2 -2
- package/dist/src/mcp/sa-impersonation-provider.js.map +1 -1
- package/dist/src/mcp/token-storage/hybrid-token-storage.js +1 -1
- package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -1
- package/dist/src/output/json-formatter.d.ts +2 -2
- package/dist/src/output/json-formatter.js +6 -3
- package/dist/src/output/json-formatter.js.map +1 -1
- package/dist/src/output/json-formatter.test.js +37 -9
- package/dist/src/output/json-formatter.test.js.map +1 -1
- package/dist/src/output/stream-json-formatter.js +6 -0
- package/dist/src/output/stream-json-formatter.js.map +1 -1
- package/dist/src/output/stream-json-formatter.test.js +98 -100
- package/dist/src/output/stream-json-formatter.test.js.map +1 -1
- package/dist/src/output/types.d.ts +3 -0
- package/dist/src/output/types.js.map +1 -1
- package/dist/src/policy/config.js +97 -11
- package/dist/src/policy/config.js.map +1 -1
- package/dist/src/policy/persistence.test.d.ts +6 -0
- package/dist/src/policy/persistence.test.js +154 -0
- package/dist/src/policy/persistence.test.js.map +1 -0
- package/dist/src/policy/policies/agent.toml +31 -0
- package/dist/src/policy/policies/read-only.toml +5 -0
- package/dist/src/policy/policy-engine.d.ts +10 -1
- package/dist/src/policy/policy-engine.js +79 -5
- package/dist/src/policy/policy-engine.js.map +1 -1
- package/dist/src/policy/policy-engine.test.js +26 -2
- package/dist/src/policy/policy-engine.test.js.map +1 -1
- package/dist/src/policy/policy-updater.test.d.ts +6 -0
- package/dist/src/policy/policy-updater.test.js +116 -0
- package/dist/src/policy/policy-updater.test.js.map +1 -0
- package/dist/src/policy/shell-safety.test.d.ts +6 -0
- package/dist/src/policy/shell-safety.test.js +75 -0
- package/dist/src/policy/shell-safety.test.js.map +1 -0
- package/dist/src/policy/toml-loader.d.ts +11 -5
- package/dist/src/policy/toml-loader.js +38 -23
- package/dist/src/policy/toml-loader.js.map +1 -1
- package/dist/src/policy/toml-loader.test.js +28 -7
- package/dist/src/policy/toml-loader.test.js.map +1 -1
- package/dist/src/policy/types.d.ts +15 -0
- package/dist/src/resources/resource-registry.d.ts +30 -0
- package/dist/src/resources/resource-registry.js +57 -0
- package/dist/src/resources/resource-registry.js.map +1 -0
- package/dist/src/resources/resource-registry.test.d.ts +6 -0
- package/dist/src/resources/resource-registry.test.js +54 -0
- package/dist/src/resources/resource-registry.test.js.map +1 -0
- package/dist/src/routing/modelRouterService.js +0 -15
- package/dist/src/routing/modelRouterService.js.map +1 -1
- package/dist/src/routing/modelRouterService.test.js +0 -62
- package/dist/src/routing/modelRouterService.test.js.map +1 -1
- package/dist/src/routing/strategies/classifierStrategy.js +10 -21
- package/dist/src/routing/strategies/classifierStrategy.js.map +1 -1
- package/dist/src/routing/strategies/classifierStrategy.test.js +2 -1
- package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -1
- package/dist/src/routing/strategies/fallbackStrategy.js +23 -12
- package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -1
- package/dist/src/routing/strategies/fallbackStrategy.test.js +69 -39
- package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -1
- package/dist/src/routing/strategies/overrideStrategy.js +4 -3
- package/dist/src/routing/strategies/overrideStrategy.js.map +1 -1
- package/dist/src/safety/checker-runner.js +17 -6
- package/dist/src/safety/checker-runner.js.map +1 -1
- package/dist/src/services/chatCompressionService.js +17 -3
- package/dist/src/services/chatCompressionService.js.map +1 -1
- package/dist/src/services/chatCompressionService.test.js +9 -0
- package/dist/src/services/chatCompressionService.test.js.map +1 -1
- package/dist/src/services/chatRecordingService.d.ts +14 -0
- package/dist/src/services/chatRecordingService.js +37 -0
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/contextManager.d.ts +35 -0
- package/dist/src/services/contextManager.js +68 -0
- package/dist/src/services/contextManager.js.map +1 -0
- package/dist/src/services/contextManager.test.d.ts +6 -0
- package/dist/src/services/contextManager.test.js +105 -0
- package/dist/src/services/contextManager.test.js.map +1 -0
- package/dist/src/services/fileSystemService.d.ts +0 -9
- package/dist/src/services/fileSystemService.js +0 -11
- package/dist/src/services/fileSystemService.js.map +1 -1
- package/dist/src/services/gitService.js +5 -0
- package/dist/src/services/gitService.js.map +1 -1
- package/dist/src/services/gitService.test.js +28 -0
- package/dist/src/services/gitService.test.js.map +1 -1
- package/dist/src/services/loopDetectionService.js +3 -3
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/modelConfig.golden.test.js +32 -0
- package/dist/src/services/modelConfig.golden.test.js.map +1 -1
- package/dist/src/services/modelConfigService.d.ts +3 -0
- package/dist/src/services/modelConfigService.js +3 -2
- package/dist/src/services/modelConfigService.js.map +1 -1
- package/dist/src/services/modelConfigService.test.js +110 -0
- package/dist/src/services/modelConfigService.test.js.map +1 -1
- package/dist/src/services/modelConfigServiceTestUtils.d.ts +10 -0
- package/dist/src/services/modelConfigServiceTestUtils.js +17 -0
- package/dist/src/services/modelConfigServiceTestUtils.js.map +1 -0
- package/dist/src/services/sessionSummaryService.d.ts +28 -0
- package/dist/src/services/sessionSummaryService.js +131 -0
- package/dist/src/services/sessionSummaryService.js.map +1 -0
- package/dist/src/services/sessionSummaryService.test.d.ts +6 -0
- package/dist/src/services/sessionSummaryService.test.js +785 -0
- package/dist/src/services/sessionSummaryService.test.js.map +1 -0
- package/dist/src/services/sessionSummaryUtils.d.ts +16 -0
- package/dist/src/services/sessionSummaryUtils.js +129 -0
- package/dist/src/services/sessionSummaryUtils.js.map +1 -0
- package/dist/src/services/sessionSummaryUtils.test.d.ts +6 -0
- package/dist/src/services/sessionSummaryUtils.test.js +137 -0
- package/dist/src/services/sessionSummaryUtils.test.js.map +1 -0
- package/dist/src/services/shellExecutionService.js +56 -22
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +137 -5
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/services/test-data/resolved-aliases-retry.golden.json +238 -0
- package/dist/src/services/test-data/resolved-aliases.golden.json +16 -0
- package/dist/src/telemetry/activity-detector.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +28 -5
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +67 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +1 -0
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +3 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/config.js +2 -0
- package/dist/src/telemetry/config.js.map +1 -1
- package/dist/src/telemetry/config.test.js +25 -0
- package/dist/src/telemetry/config.test.js.map +1 -1
- package/dist/src/telemetry/gcp-exporters.d.ts +4 -3
- package/dist/src/telemetry/gcp-exporters.js +8 -4
- package/dist/src/telemetry/gcp-exporters.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +2 -1
- package/dist/src/telemetry/index.js +2 -1
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +2 -1
- package/dist/src/telemetry/loggers.js +345 -338
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +195 -18
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/metrics.test.js.map +1 -1
- package/dist/src/telemetry/sdk.d.ts +9 -2
- package/dist/src/telemetry/sdk.js +143 -17
- package/dist/src/telemetry/sdk.js.map +1 -1
- package/dist/src/telemetry/sdk.test.js +130 -28
- package/dist/src/telemetry/sdk.test.js.map +1 -1
- package/dist/src/telemetry/startupProfiler.d.ts +51 -0
- package/dist/src/telemetry/startupProfiler.js +170 -0
- package/dist/src/telemetry/startupProfiler.js.map +1 -0
- package/dist/src/telemetry/startupProfiler.test.d.ts +6 -0
- package/dist/src/telemetry/startupProfiler.test.js +289 -0
- package/dist/src/telemetry/startupProfiler.test.js.map +1 -0
- package/dist/src/telemetry/telemetry.test.js +10 -3
- package/dist/src/telemetry/telemetry.test.js.map +1 -1
- package/dist/src/telemetry/trace.js +2 -2
- package/dist/src/telemetry/trace.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +37 -10
- package/dist/src/telemetry/types.js +82 -17
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +1 -0
- package/dist/src/telemetry/uiTelemetry.js +2 -0
- package/dist/src/telemetry/uiTelemetry.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +4 -0
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/test-utils/mock-message-bus.js.map +1 -1
- package/dist/src/tools/confirmation-policy.test.d.ts +6 -0
- package/dist/src/tools/confirmation-policy.test.js +152 -0
- package/dist/src/tools/confirmation-policy.test.js.map +1 -0
- package/dist/src/tools/edit.js +5 -0
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.d.ts +3 -1
- package/dist/src/tools/mcp-client-manager.js +30 -4
- package/dist/src/tools/mcp-client-manager.js.map +1 -1
- package/dist/src/tools/mcp-client-manager.test.js +38 -10
- package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +40 -3
- package/dist/src/tools/mcp-client.js +437 -174
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +695 -28
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/mcp-tool.js +13 -0
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +25 -0
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/memoryTool.js +1 -0
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.test.js +22 -13
- package/dist/src/tools/modifiable-tool.test.js.map +1 -1
- package/dist/src/tools/read-file.js +1 -1
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/tools/read-many-files.js +6 -4
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/read-many-files.test.js +1 -1
- package/dist/src/tools/read-many-files.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +2 -1
- package/dist/src/tools/shell.js +58 -4
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +25 -5
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/smart-edit.js +6 -1
- package/dist/src/tools/smart-edit.js.map +1 -1
- package/dist/src/tools/smart-edit.test.js.map +1 -1
- package/dist/src/tools/tool-names.d.ts +2 -0
- package/dist/src/tools/tool-names.js +2 -0
- package/dist/src/tools/tool-names.js.map +1 -1
- package/dist/src/tools/tools.d.ts +19 -0
- package/dist/src/tools/tools.js +29 -8
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/web-fetch.js +18 -5
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-fetch.test.js +1 -0
- package/dist/src/tools/web-fetch.test.js.map +1 -1
- package/dist/src/tools/write-file.js +5 -0
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.d.ts +8 -0
- package/dist/src/utils/bfsFileSearch.js +63 -23
- package/dist/src/utils/bfsFileSearch.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.test.js +65 -1
- package/dist/src/utils/bfsFileSearch.test.js.map +1 -1
- package/dist/src/utils/checkpointUtils.d.ts +82 -0
- package/dist/src/utils/checkpointUtils.js +117 -0
- package/dist/src/utils/checkpointUtils.js.map +1 -0
- package/dist/src/utils/checkpointUtils.test.d.ts +6 -0
- package/dist/src/utils/checkpointUtils.test.js +229 -0
- package/dist/src/utils/checkpointUtils.test.js.map +1 -0
- package/dist/src/utils/debugLogger.d.ts +3 -0
- package/dist/src/utils/debugLogger.js +27 -0
- package/dist/src/utils/debugLogger.js.map +1 -1
- package/dist/src/utils/editCorrector.test.js +4 -0
- package/dist/src/utils/editCorrector.test.js.map +1 -1
- package/dist/src/utils/editor.d.ts +9 -1
- package/dist/src/utils/editor.js +23 -14
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/errors.d.ts +8 -0
- package/dist/src/utils/errors.js +39 -2
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/errors.test.d.ts +6 -0
- package/dist/src/utils/errors.test.js +155 -0
- package/dist/src/utils/errors.test.js.map +1 -0
- package/dist/src/utils/exitCodes.d.ts +12 -0
- package/dist/src/utils/exitCodes.js +13 -0
- package/dist/src/utils/exitCodes.js.map +1 -0
- package/dist/src/utils/extensionLoader.d.ts +2 -2
- package/dist/src/utils/extensionLoader.js +5 -6
- package/dist/src/utils/extensionLoader.js.map +1 -1
- package/dist/src/utils/extensionLoader.test.js +11 -0
- package/dist/src/utils/extensionLoader.test.js.map +1 -1
- package/dist/src/utils/fetch.d.ts +1 -1
- package/dist/src/utils/fetch.js +3 -3
- package/dist/src/utils/fetch.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +15 -0
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/filesearch/crawlCache.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
- package/dist/src/utils/flashFallback.test.js +1 -1
- package/dist/src/utils/flashFallback.test.js.map +1 -1
- package/dist/src/utils/googleErrors.js +31 -18
- package/dist/src/utils/googleErrors.js.map +1 -1
- package/dist/src/utils/googleErrors.test.js +10 -2
- package/dist/src/utils/googleErrors.test.js.map +1 -1
- package/dist/src/utils/googleQuotaErrors.d.ts +3 -3
- package/dist/src/utils/googleQuotaErrors.js +32 -6
- package/dist/src/utils/googleQuotaErrors.js.map +1 -1
- package/dist/src/utils/googleQuotaErrors.test.js +94 -2
- package/dist/src/utils/googleQuotaErrors.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.d.ts +5 -0
- package/dist/src/utils/memoryDiscovery.js +7 -3
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +28 -0
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.test.js +4 -0
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
- package/dist/src/utils/pathCorrector.js +12 -2
- package/dist/src/utils/pathCorrector.js.map +1 -1
- package/dist/src/utils/pathCorrector.test.js +6 -2
- package/dist/src/utils/pathCorrector.test.js.map +1 -1
- package/dist/src/utils/retry.d.ts +11 -0
- package/dist/src/utils/retry.js +54 -13
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.js +170 -10
- package/dist/src/utils/retry.test.js.map +1 -1
- package/dist/src/utils/shell-permissions.d.ts +52 -0
- package/dist/src/utils/shell-permissions.js +188 -0
- package/dist/src/utils/shell-permissions.js.map +1 -0
- package/dist/src/utils/shell-permissions.test.d.ts +6 -0
- package/dist/src/utils/shell-permissions.test.js +347 -0
- package/dist/src/utils/shell-permissions.test.js.map +1 -0
- package/dist/src/utils/shell-utils.d.ts +10 -47
- package/dist/src/utils/shell-utils.js +1 -182
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/shell-utils.test.js +1 -288
- package/dist/src/utils/shell-utils.test.js.map +1 -1
- package/dist/src/utils/stdio.d.ts +2 -2
- package/dist/src/utils/stdio.js +2 -2
- package/dist/src/utils/stdio.js.map +1 -1
- package/dist/src/utils/stdio.test.js +5 -5
- package/dist/src/utils/stdio.test.js.map +1 -1
- package/dist/src/utils/terminalSerializer.test.js +17 -0
- package/dist/src/utils/terminalSerializer.test.js.map +1 -1
- package/dist/src/utils/tokenCalculation.d.ts +19 -0
- package/dist/src/utils/tokenCalculation.js +70 -0
- package/dist/src/utils/tokenCalculation.js.map +1 -0
- package/dist/src/utils/tokenCalculation.test.d.ts +6 -0
- package/dist/src/utils/tokenCalculation.test.js +78 -0
- package/dist/src/utils/tokenCalculation.test.js.map +1 -0
- package/dist/src/utils/tool-utils.js.map +1 -1
- package/dist/src/utils/version.d.ts +6 -0
- package/dist/src/utils/version.js +15 -0
- package/dist/src/utils/version.js.map +1 -0
- package/dist/src/utils/version.test.d.ts +6 -0
- package/dist/src/utils/version.test.js +39 -0
- package/dist/src/utils/version.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
import { GoogleAuth } from 'google-auth-library';
|
|
6
7
|
import * as ClientLib from '@modelcontextprotocol/sdk/client/index.js';
|
|
7
8
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
8
9
|
import * as SdkClientStdioLib from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
9
|
-
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
10
|
+
import { StreamableHTTPClientTransport, StreamableHTTPError, } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
10
11
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
11
12
|
import { AuthProviderType } from '../config/config.js';
|
|
12
13
|
import { GoogleCredentialProvider } from '../mcp/google-auth-provider.js';
|
|
13
14
|
import { MCPOAuthProvider } from '../mcp/oauth-provider.js';
|
|
14
15
|
import { MCPOAuthTokenStorage } from '../mcp/oauth-token-storage.js';
|
|
15
16
|
import { OAuthUtils } from '../mcp/oauth-utils.js';
|
|
17
|
+
import { ToolListChangedNotificationSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
16
18
|
import { WorkspaceContext } from '../utils/workspaceContext.js';
|
|
17
19
|
import { connectToMcpServer, createTransport, hasNetworkTransport, isEnabled, McpClient, populateMcpServerCommand, } from './mcp-client.js';
|
|
18
20
|
import * as fs from 'node:fs';
|
|
@@ -59,6 +61,7 @@ describe('mcp-client', () => {
|
|
|
59
61
|
getStatus: vi.fn(),
|
|
60
62
|
registerCapabilities: vi.fn(),
|
|
61
63
|
setRequestHandler: vi.fn(),
|
|
64
|
+
setNotificationHandler: vi.fn(),
|
|
62
65
|
getServerCapabilities: vi.fn().mockReturnValue({ tools: {} }),
|
|
63
66
|
listTools: vi.fn().mockResolvedValue({
|
|
64
67
|
tools: [
|
|
@@ -83,12 +86,20 @@ describe('mcp-client', () => {
|
|
|
83
86
|
sortTools: vi.fn(),
|
|
84
87
|
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
85
88
|
};
|
|
89
|
+
const promptRegistry = {
|
|
90
|
+
registerPrompt: vi.fn(),
|
|
91
|
+
removePromptsByServer: vi.fn(),
|
|
92
|
+
};
|
|
93
|
+
const resourceRegistry = {
|
|
94
|
+
setResourcesForServer: vi.fn(),
|
|
95
|
+
removeResourcesByServer: vi.fn(),
|
|
96
|
+
};
|
|
86
97
|
const client = new McpClient('test-server', {
|
|
87
98
|
command: 'test-command',
|
|
88
|
-
}, mockedToolRegistry,
|
|
99
|
+
}, mockedToolRegistry, promptRegistry, resourceRegistry, workspaceContext, {}, false);
|
|
89
100
|
await client.connect();
|
|
90
101
|
await client.discover({});
|
|
91
|
-
expect(mockedClient.listTools).toHaveBeenCalledWith({});
|
|
102
|
+
expect(mockedClient.listTools).toHaveBeenCalledWith({}, { timeout: 600000 });
|
|
92
103
|
});
|
|
93
104
|
it('should not skip tools even if a parameter is missing a type', async () => {
|
|
94
105
|
const consoleWarnSpy = vi
|
|
@@ -101,6 +112,7 @@ describe('mcp-client', () => {
|
|
|
101
112
|
getStatus: vi.fn(),
|
|
102
113
|
registerCapabilities: vi.fn(),
|
|
103
114
|
setRequestHandler: vi.fn(),
|
|
115
|
+
setNotificationHandler: vi.fn(),
|
|
104
116
|
getServerCapabilities: vi.fn().mockReturnValue({ tools: {} }),
|
|
105
117
|
listTools: vi.fn().mockResolvedValue({
|
|
106
118
|
tools: [
|
|
@@ -136,9 +148,17 @@ describe('mcp-client', () => {
|
|
|
136
148
|
sortTools: vi.fn(),
|
|
137
149
|
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
138
150
|
};
|
|
151
|
+
const promptRegistry = {
|
|
152
|
+
registerPrompt: vi.fn(),
|
|
153
|
+
removePromptsByServer: vi.fn(),
|
|
154
|
+
};
|
|
155
|
+
const resourceRegistry = {
|
|
156
|
+
setResourcesForServer: vi.fn(),
|
|
157
|
+
removeResourcesByServer: vi.fn(),
|
|
158
|
+
};
|
|
139
159
|
const client = new McpClient('test-server', {
|
|
140
160
|
command: 'test-command',
|
|
141
|
-
}, mockedToolRegistry,
|
|
161
|
+
}, mockedToolRegistry, promptRegistry, resourceRegistry, workspaceContext, {}, false);
|
|
142
162
|
await client.connect();
|
|
143
163
|
await client.discover({});
|
|
144
164
|
expect(mockedToolRegistry.registerTool).toHaveBeenCalledTimes(2);
|
|
@@ -153,6 +173,7 @@ describe('mcp-client', () => {
|
|
|
153
173
|
getStatus: vi.fn(),
|
|
154
174
|
registerCapabilities: vi.fn(),
|
|
155
175
|
setRequestHandler: vi.fn(),
|
|
176
|
+
setNotificationHandler: vi.fn(),
|
|
156
177
|
getServerCapabilities: vi.fn().mockReturnValue({ prompts: {} }),
|
|
157
178
|
listTools: vi.fn().mockResolvedValue({ tools: [] }),
|
|
158
179
|
listPrompts: vi.fn().mockRejectedValue(new Error('Test error')),
|
|
@@ -164,11 +185,19 @@ describe('mcp-client', () => {
|
|
|
164
185
|
registerTool: vi.fn(),
|
|
165
186
|
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
166
187
|
};
|
|
188
|
+
const promptRegistry = {
|
|
189
|
+
registerPrompt: vi.fn(),
|
|
190
|
+
removePromptsByServer: vi.fn(),
|
|
191
|
+
};
|
|
192
|
+
const resourceRegistry = {
|
|
193
|
+
setResourcesForServer: vi.fn(),
|
|
194
|
+
removeResourcesByServer: vi.fn(),
|
|
195
|
+
};
|
|
167
196
|
const client = new McpClient('test-server', {
|
|
168
197
|
command: 'test-command',
|
|
169
|
-
}, mockedToolRegistry,
|
|
198
|
+
}, mockedToolRegistry, promptRegistry, resourceRegistry, workspaceContext, {}, false);
|
|
170
199
|
await client.connect();
|
|
171
|
-
await expect(client.discover({})).rejects.toThrow('No prompts or
|
|
200
|
+
await expect(client.discover({})).rejects.toThrow('No prompts, tools, or resources found on the server.');
|
|
172
201
|
expect(coreEvents.emitFeedback).toHaveBeenCalledWith('error', `Error discovering prompts from test-server: Test error`, expect.any(Error));
|
|
173
202
|
});
|
|
174
203
|
it('should not discover tools if server does not support them', async () => {
|
|
@@ -179,6 +208,7 @@ describe('mcp-client', () => {
|
|
|
179
208
|
getStatus: vi.fn(),
|
|
180
209
|
registerCapabilities: vi.fn(),
|
|
181
210
|
setRequestHandler: vi.fn(),
|
|
211
|
+
setNotificationHandler: vi.fn(),
|
|
182
212
|
getServerCapabilities: vi.fn().mockReturnValue({ prompts: {} }),
|
|
183
213
|
listPrompts: vi.fn().mockResolvedValue({ prompts: [] }),
|
|
184
214
|
request: vi.fn().mockResolvedValue({}),
|
|
@@ -190,11 +220,19 @@ describe('mcp-client', () => {
|
|
|
190
220
|
sortTools: vi.fn(),
|
|
191
221
|
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
192
222
|
};
|
|
223
|
+
const promptRegistry = {
|
|
224
|
+
registerPrompt: vi.fn(),
|
|
225
|
+
removePromptsByServer: vi.fn(),
|
|
226
|
+
};
|
|
227
|
+
const resourceRegistry = {
|
|
228
|
+
setResourcesForServer: vi.fn(),
|
|
229
|
+
removeResourcesByServer: vi.fn(),
|
|
230
|
+
};
|
|
193
231
|
const client = new McpClient('test-server', {
|
|
194
232
|
command: 'test-command',
|
|
195
|
-
}, mockedToolRegistry,
|
|
233
|
+
}, mockedToolRegistry, promptRegistry, resourceRegistry, workspaceContext, {}, false);
|
|
196
234
|
await client.connect();
|
|
197
|
-
await expect(client.discover({})).rejects.toThrow('No prompts or
|
|
235
|
+
await expect(client.discover({})).rejects.toThrow('No prompts, tools, or resources found on the server.');
|
|
198
236
|
});
|
|
199
237
|
it('should discover tools if server supports them', async () => {
|
|
200
238
|
const mockedClient = {
|
|
@@ -204,6 +242,7 @@ describe('mcp-client', () => {
|
|
|
204
242
|
getStatus: vi.fn(),
|
|
205
243
|
registerCapabilities: vi.fn(),
|
|
206
244
|
setRequestHandler: vi.fn(),
|
|
245
|
+
setNotificationHandler: vi.fn(),
|
|
207
246
|
getServerCapabilities: vi.fn().mockReturnValue({ tools: {} }),
|
|
208
247
|
listTools: vi.fn().mockResolvedValue({
|
|
209
248
|
tools: [
|
|
@@ -224,9 +263,17 @@ describe('mcp-client', () => {
|
|
|
224
263
|
sortTools: vi.fn(),
|
|
225
264
|
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
226
265
|
};
|
|
266
|
+
const promptRegistry = {
|
|
267
|
+
registerPrompt: vi.fn(),
|
|
268
|
+
removePromptsByServer: vi.fn(),
|
|
269
|
+
};
|
|
270
|
+
const resourceRegistry = {
|
|
271
|
+
setResourcesForServer: vi.fn(),
|
|
272
|
+
removeResourcesByServer: vi.fn(),
|
|
273
|
+
};
|
|
227
274
|
const client = new McpClient('test-server', {
|
|
228
275
|
command: 'test-command',
|
|
229
|
-
}, mockedToolRegistry,
|
|
276
|
+
}, mockedToolRegistry, promptRegistry, resourceRegistry, workspaceContext, {}, false);
|
|
230
277
|
await client.connect();
|
|
231
278
|
await client.discover({});
|
|
232
279
|
expect(mockedToolRegistry.registerTool).toHaveBeenCalledOnce();
|
|
@@ -239,6 +286,7 @@ describe('mcp-client', () => {
|
|
|
239
286
|
getStatus: vi.fn(),
|
|
240
287
|
registerCapabilities: vi.fn(),
|
|
241
288
|
setRequestHandler: vi.fn(),
|
|
289
|
+
setNotificationHandler: vi.fn(),
|
|
242
290
|
getServerCapabilities: vi.fn().mockReturnValue({ tools: {} }),
|
|
243
291
|
listTools: vi.fn().mockResolvedValue({
|
|
244
292
|
tools: [
|
|
@@ -274,9 +322,17 @@ describe('mcp-client', () => {
|
|
|
274
322
|
sortTools: vi.fn(),
|
|
275
323
|
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
276
324
|
};
|
|
325
|
+
const promptRegistry = {
|
|
326
|
+
registerPrompt: vi.fn(),
|
|
327
|
+
removePromptsByServer: vi.fn(),
|
|
328
|
+
};
|
|
329
|
+
const resourceRegistry = {
|
|
330
|
+
setResourcesForServer: vi.fn(),
|
|
331
|
+
removeResourcesByServer: vi.fn(),
|
|
332
|
+
};
|
|
277
333
|
const client = new McpClient('test-server', {
|
|
278
334
|
command: 'test-command',
|
|
279
|
-
}, mockedToolRegistry,
|
|
335
|
+
}, mockedToolRegistry, promptRegistry, resourceRegistry, workspaceContext, {}, false);
|
|
280
336
|
await client.connect();
|
|
281
337
|
await client.discover({});
|
|
282
338
|
expect(mockedToolRegistry.registerTool).toHaveBeenCalledOnce();
|
|
@@ -297,6 +353,126 @@ describe('mcp-client', () => {
|
|
|
297
353
|
},
|
|
298
354
|
});
|
|
299
355
|
});
|
|
356
|
+
it('should discover resources when a server only exposes resources', async () => {
|
|
357
|
+
const mockedClient = {
|
|
358
|
+
connect: vi.fn(),
|
|
359
|
+
discover: vi.fn(),
|
|
360
|
+
disconnect: vi.fn(),
|
|
361
|
+
getStatus: vi.fn(),
|
|
362
|
+
registerCapabilities: vi.fn(),
|
|
363
|
+
setRequestHandler: vi.fn(),
|
|
364
|
+
setNotificationHandler: vi.fn(),
|
|
365
|
+
getServerCapabilities: vi.fn().mockReturnValue({ resources: {} }),
|
|
366
|
+
request: vi.fn().mockImplementation(({ method }) => {
|
|
367
|
+
if (method === 'resources/list') {
|
|
368
|
+
return Promise.resolve({
|
|
369
|
+
resources: [
|
|
370
|
+
{
|
|
371
|
+
uri: 'file:///tmp/resource.txt',
|
|
372
|
+
name: 'resource',
|
|
373
|
+
description: 'Test Resource',
|
|
374
|
+
mimeType: 'text/plain',
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
return Promise.resolve({ prompts: [] });
|
|
380
|
+
}),
|
|
381
|
+
};
|
|
382
|
+
vi.mocked(ClientLib.Client).mockReturnValue(mockedClient);
|
|
383
|
+
vi.spyOn(SdkClientStdioLib, 'StdioClientTransport').mockReturnValue({});
|
|
384
|
+
const mockedToolRegistry = {
|
|
385
|
+
registerTool: vi.fn(),
|
|
386
|
+
sortTools: vi.fn(),
|
|
387
|
+
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
388
|
+
};
|
|
389
|
+
const promptRegistry = {
|
|
390
|
+
registerPrompt: vi.fn(),
|
|
391
|
+
removePromptsByServer: vi.fn(),
|
|
392
|
+
};
|
|
393
|
+
const resourceRegistry = {
|
|
394
|
+
setResourcesForServer: vi.fn(),
|
|
395
|
+
removeResourcesByServer: vi.fn(),
|
|
396
|
+
};
|
|
397
|
+
const client = new McpClient('test-server', {
|
|
398
|
+
command: 'test-command',
|
|
399
|
+
}, mockedToolRegistry, promptRegistry, resourceRegistry, workspaceContext, {}, false);
|
|
400
|
+
await client.connect();
|
|
401
|
+
await client.discover({});
|
|
402
|
+
expect(resourceRegistry.setResourcesForServer).toHaveBeenCalledWith('test-server', [
|
|
403
|
+
expect.objectContaining({
|
|
404
|
+
uri: 'file:///tmp/resource.txt',
|
|
405
|
+
name: 'resource',
|
|
406
|
+
}),
|
|
407
|
+
]);
|
|
408
|
+
});
|
|
409
|
+
it('refreshes registry when resource list change notification is received', async () => {
|
|
410
|
+
let listCallCount = 0;
|
|
411
|
+
let resourceListHandler;
|
|
412
|
+
const mockedClient = {
|
|
413
|
+
connect: vi.fn(),
|
|
414
|
+
discover: vi.fn(),
|
|
415
|
+
disconnect: vi.fn(),
|
|
416
|
+
getStatus: vi.fn(),
|
|
417
|
+
registerCapabilities: vi.fn(),
|
|
418
|
+
setRequestHandler: vi.fn(),
|
|
419
|
+
setNotificationHandler: vi.fn((_, handler) => {
|
|
420
|
+
resourceListHandler = handler;
|
|
421
|
+
}),
|
|
422
|
+
getServerCapabilities: vi
|
|
423
|
+
.fn()
|
|
424
|
+
.mockReturnValue({ resources: { listChanged: true } }),
|
|
425
|
+
request: vi.fn().mockImplementation(({ method }) => {
|
|
426
|
+
if (method === 'resources/list') {
|
|
427
|
+
listCallCount += 1;
|
|
428
|
+
if (listCallCount === 1) {
|
|
429
|
+
return Promise.resolve({
|
|
430
|
+
resources: [
|
|
431
|
+
{
|
|
432
|
+
uri: 'file:///tmp/one.txt',
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
return Promise.resolve({
|
|
438
|
+
resources: [
|
|
439
|
+
{
|
|
440
|
+
uri: 'file:///tmp/two.txt',
|
|
441
|
+
},
|
|
442
|
+
],
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
return Promise.resolve({ prompts: [] });
|
|
446
|
+
}),
|
|
447
|
+
};
|
|
448
|
+
vi.mocked(ClientLib.Client).mockReturnValue(mockedClient);
|
|
449
|
+
vi.spyOn(SdkClientStdioLib, 'StdioClientTransport').mockReturnValue({});
|
|
450
|
+
const mockedToolRegistry = {
|
|
451
|
+
registerTool: vi.fn(),
|
|
452
|
+
sortTools: vi.fn(),
|
|
453
|
+
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
454
|
+
};
|
|
455
|
+
const promptRegistry = {
|
|
456
|
+
registerPrompt: vi.fn(),
|
|
457
|
+
removePromptsByServer: vi.fn(),
|
|
458
|
+
};
|
|
459
|
+
const resourceRegistry = {
|
|
460
|
+
setResourcesForServer: vi.fn(),
|
|
461
|
+
removeResourcesByServer: vi.fn(),
|
|
462
|
+
};
|
|
463
|
+
const client = new McpClient('test-server', {
|
|
464
|
+
command: 'test-command',
|
|
465
|
+
}, mockedToolRegistry, promptRegistry, resourceRegistry, workspaceContext, {}, false);
|
|
466
|
+
await client.connect();
|
|
467
|
+
await client.discover({});
|
|
468
|
+
expect(mockedClient.setNotificationHandler).toHaveBeenCalledOnce();
|
|
469
|
+
expect(resourceListHandler).toBeDefined();
|
|
470
|
+
await resourceListHandler?.({
|
|
471
|
+
method: 'notifications/resources/list_changed',
|
|
472
|
+
});
|
|
473
|
+
expect(resourceRegistry.setResourcesForServer).toHaveBeenLastCalledWith('test-server', [expect.objectContaining({ uri: 'file:///tmp/two.txt' })]);
|
|
474
|
+
expect(coreEvents.emitFeedback).toHaveBeenCalledWith('info', 'Resources updated for server: test-server');
|
|
475
|
+
});
|
|
300
476
|
it('should remove tools and prompts on disconnect', async () => {
|
|
301
477
|
const mockedClient = {
|
|
302
478
|
connect: vi.fn(),
|
|
@@ -304,6 +480,7 @@ describe('mcp-client', () => {
|
|
|
304
480
|
getStatus: vi.fn(),
|
|
305
481
|
registerCapabilities: vi.fn(),
|
|
306
482
|
setRequestHandler: vi.fn(),
|
|
483
|
+
setNotificationHandler: vi.fn(),
|
|
307
484
|
getServerCapabilities: vi
|
|
308
485
|
.fn()
|
|
309
486
|
.mockReturnValue({ tools: {}, prompts: {} }),
|
|
@@ -335,9 +512,13 @@ describe('mcp-client', () => {
|
|
|
335
512
|
unregisterPrompt: vi.fn(),
|
|
336
513
|
removePromptsByServer: vi.fn(),
|
|
337
514
|
};
|
|
515
|
+
const resourceRegistry = {
|
|
516
|
+
setResourcesForServer: vi.fn(),
|
|
517
|
+
removeResourcesByServer: vi.fn(),
|
|
518
|
+
};
|
|
338
519
|
const client = new McpClient('test-server', {
|
|
339
520
|
command: 'test-command',
|
|
340
|
-
}, mockedToolRegistry, mockedPromptRegistry, workspaceContext, false);
|
|
521
|
+
}, mockedToolRegistry, mockedPromptRegistry, resourceRegistry, workspaceContext, {}, false);
|
|
341
522
|
await client.connect();
|
|
342
523
|
await client.discover({});
|
|
343
524
|
expect(mockedToolRegistry.registerTool).toHaveBeenCalledOnce();
|
|
@@ -346,6 +527,251 @@ describe('mcp-client', () => {
|
|
|
346
527
|
expect(mockedClient.close).toHaveBeenCalledOnce();
|
|
347
528
|
expect(mockedToolRegistry.removeMcpToolsByServer).toHaveBeenCalledOnce();
|
|
348
529
|
expect(mockedPromptRegistry.removePromptsByServer).toHaveBeenCalledOnce();
|
|
530
|
+
expect(resourceRegistry.removeResourcesByServer).toHaveBeenCalledOnce();
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
describe('Dynamic Tool Updates', () => {
|
|
534
|
+
it('should set up notification handler if server supports tool list changes', async () => {
|
|
535
|
+
const mockedClient = {
|
|
536
|
+
connect: vi.fn(),
|
|
537
|
+
getStatus: vi.fn(),
|
|
538
|
+
registerCapabilities: vi.fn(),
|
|
539
|
+
setRequestHandler: vi.fn(),
|
|
540
|
+
// Capability enables the listener
|
|
541
|
+
getServerCapabilities: vi
|
|
542
|
+
.fn()
|
|
543
|
+
.mockReturnValue({ tools: { listChanged: true } }),
|
|
544
|
+
setNotificationHandler: vi.fn(),
|
|
545
|
+
listTools: vi.fn().mockResolvedValue({ tools: [] }),
|
|
546
|
+
listPrompts: vi.fn().mockResolvedValue({ prompts: [] }),
|
|
547
|
+
request: vi.fn().mockResolvedValue({}),
|
|
548
|
+
};
|
|
549
|
+
vi.mocked(ClientLib.Client).mockReturnValue(mockedClient);
|
|
550
|
+
vi.spyOn(SdkClientStdioLib, 'StdioClientTransport').mockReturnValue({});
|
|
551
|
+
const client = new McpClient('test-server', { command: 'test-command' }, {}, {}, {}, workspaceContext, {}, false);
|
|
552
|
+
await client.connect();
|
|
553
|
+
expect(mockedClient.setNotificationHandler).toHaveBeenCalledWith(ToolListChangedNotificationSchema, expect.any(Function));
|
|
554
|
+
});
|
|
555
|
+
it('should NOT set up notification handler if server lacks capability', async () => {
|
|
556
|
+
const mockedClient = {
|
|
557
|
+
connect: vi.fn(),
|
|
558
|
+
getServerCapabilities: vi.fn().mockReturnValue({ tools: {} }), // No listChanged
|
|
559
|
+
setNotificationHandler: vi.fn(),
|
|
560
|
+
request: vi.fn().mockResolvedValue({}),
|
|
561
|
+
registerCapabilities: vi.fn().mockResolvedValue({}),
|
|
562
|
+
setRequestHandler: vi.fn().mockResolvedValue({}),
|
|
563
|
+
};
|
|
564
|
+
vi.mocked(ClientLib.Client).mockReturnValue(mockedClient);
|
|
565
|
+
vi.spyOn(SdkClientStdioLib, 'StdioClientTransport').mockReturnValue({});
|
|
566
|
+
const client = new McpClient('test-server', { command: 'test-command' }, {}, {}, {}, workspaceContext, {}, false);
|
|
567
|
+
await client.connect();
|
|
568
|
+
expect(mockedClient.setNotificationHandler).not.toHaveBeenCalled();
|
|
569
|
+
});
|
|
570
|
+
it('should refresh tools and notify manager when notification is received', async () => {
|
|
571
|
+
// Setup mocks
|
|
572
|
+
const mockedClient = {
|
|
573
|
+
connect: vi.fn(),
|
|
574
|
+
getServerCapabilities: vi
|
|
575
|
+
.fn()
|
|
576
|
+
.mockReturnValue({ tools: { listChanged: true } }),
|
|
577
|
+
setNotificationHandler: vi.fn(),
|
|
578
|
+
listTools: vi.fn().mockResolvedValue({
|
|
579
|
+
tools: [
|
|
580
|
+
{
|
|
581
|
+
name: 'newTool',
|
|
582
|
+
inputSchema: { type: 'object', properties: {} },
|
|
583
|
+
},
|
|
584
|
+
],
|
|
585
|
+
}),
|
|
586
|
+
listPrompts: vi.fn().mockResolvedValue({ prompts: [] }),
|
|
587
|
+
request: vi.fn().mockResolvedValue({}),
|
|
588
|
+
registerCapabilities: vi.fn().mockResolvedValue({}),
|
|
589
|
+
setRequestHandler: vi.fn().mockResolvedValue({}),
|
|
590
|
+
};
|
|
591
|
+
vi.mocked(ClientLib.Client).mockReturnValue(mockedClient);
|
|
592
|
+
vi.spyOn(SdkClientStdioLib, 'StdioClientTransport').mockReturnValue({});
|
|
593
|
+
const mockedToolRegistry = {
|
|
594
|
+
removeMcpToolsByServer: vi.fn(),
|
|
595
|
+
registerTool: vi.fn(),
|
|
596
|
+
sortTools: vi.fn(),
|
|
597
|
+
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
598
|
+
};
|
|
599
|
+
const onToolsUpdatedSpy = vi.fn().mockResolvedValue(undefined);
|
|
600
|
+
// Initialize client with onToolsUpdated callback
|
|
601
|
+
const client = new McpClient('test-server', { command: 'test-command' }, mockedToolRegistry, {}, {}, workspaceContext, {}, false, onToolsUpdatedSpy);
|
|
602
|
+
// 1. Connect (sets up listener)
|
|
603
|
+
await client.connect();
|
|
604
|
+
// 2. Extract the callback passed to setNotificationHandler
|
|
605
|
+
const notificationCallback = mockedClient.setNotificationHandler.mock.calls[0][1];
|
|
606
|
+
// 3. Trigger the notification manually
|
|
607
|
+
await notificationCallback();
|
|
608
|
+
// 4. Assertions
|
|
609
|
+
// It should clear old tools
|
|
610
|
+
expect(mockedToolRegistry.removeMcpToolsByServer).toHaveBeenCalledWith('test-server');
|
|
611
|
+
// It should fetch new tools (listTools called inside discoverTools)
|
|
612
|
+
expect(mockedClient.listTools).toHaveBeenCalled();
|
|
613
|
+
// It should register the new tool
|
|
614
|
+
expect(mockedToolRegistry.registerTool).toHaveBeenCalled();
|
|
615
|
+
// It should notify the manager
|
|
616
|
+
expect(onToolsUpdatedSpy).toHaveBeenCalled();
|
|
617
|
+
// It should emit feedback event
|
|
618
|
+
expect(coreEvents.emitFeedback).toHaveBeenCalledWith('info', 'Tools updated for server: test-server');
|
|
619
|
+
});
|
|
620
|
+
it('should handle errors during tool refresh gracefully', async () => {
|
|
621
|
+
const mockedClient = {
|
|
622
|
+
connect: vi.fn(),
|
|
623
|
+
getServerCapabilities: vi
|
|
624
|
+
.fn()
|
|
625
|
+
.mockReturnValue({ tools: { listChanged: true } }),
|
|
626
|
+
setNotificationHandler: vi.fn(),
|
|
627
|
+
// Simulate error during discovery
|
|
628
|
+
listTools: vi.fn().mockRejectedValue(new Error('Network blip')),
|
|
629
|
+
request: vi.fn().mockResolvedValue({}),
|
|
630
|
+
registerCapabilities: vi.fn().mockResolvedValue({}),
|
|
631
|
+
setRequestHandler: vi.fn().mockResolvedValue({}),
|
|
632
|
+
};
|
|
633
|
+
vi.mocked(ClientLib.Client).mockReturnValue(mockedClient);
|
|
634
|
+
vi.spyOn(SdkClientStdioLib, 'StdioClientTransport').mockReturnValue({});
|
|
635
|
+
const mockedToolRegistry = {
|
|
636
|
+
removeMcpToolsByServer: vi.fn(),
|
|
637
|
+
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
638
|
+
};
|
|
639
|
+
const client = new McpClient('test-server', { command: 'test-command' }, mockedToolRegistry, {}, {}, workspaceContext, {}, false);
|
|
640
|
+
await client.connect();
|
|
641
|
+
const notificationCallback = mockedClient.setNotificationHandler.mock.calls[0][1];
|
|
642
|
+
// Trigger notification - should fail internally but catch the error
|
|
643
|
+
await notificationCallback();
|
|
644
|
+
// Should try to remove tools
|
|
645
|
+
expect(mockedToolRegistry.removeMcpToolsByServer).toHaveBeenCalled();
|
|
646
|
+
// Should NOT emit success feedback
|
|
647
|
+
expect(coreEvents.emitFeedback).not.toHaveBeenCalledWith('info', expect.stringContaining('Tools updated'));
|
|
648
|
+
});
|
|
649
|
+
it('should handle concurrent updates from multiple servers', async () => {
|
|
650
|
+
const createMockSdkClient = (toolName) => ({
|
|
651
|
+
connect: vi.fn(),
|
|
652
|
+
getServerCapabilities: vi
|
|
653
|
+
.fn()
|
|
654
|
+
.mockReturnValue({ tools: { listChanged: true } }),
|
|
655
|
+
setNotificationHandler: vi.fn(),
|
|
656
|
+
listTools: vi.fn().mockResolvedValue({
|
|
657
|
+
tools: [
|
|
658
|
+
{
|
|
659
|
+
name: toolName,
|
|
660
|
+
inputSchema: { type: 'object', properties: {} },
|
|
661
|
+
},
|
|
662
|
+
],
|
|
663
|
+
}),
|
|
664
|
+
listPrompts: vi.fn().mockResolvedValue({ prompts: [] }),
|
|
665
|
+
request: vi.fn().mockResolvedValue({}),
|
|
666
|
+
registerCapabilities: vi.fn().mockResolvedValue({}),
|
|
667
|
+
setRequestHandler: vi.fn().mockResolvedValue({}),
|
|
668
|
+
});
|
|
669
|
+
const mockClientA = createMockSdkClient('tool-from-A');
|
|
670
|
+
const mockClientB = createMockSdkClient('tool-from-B');
|
|
671
|
+
vi.mocked(ClientLib.Client)
|
|
672
|
+
.mockReturnValueOnce(mockClientA)
|
|
673
|
+
.mockReturnValueOnce(mockClientB);
|
|
674
|
+
vi.spyOn(SdkClientStdioLib, 'StdioClientTransport').mockReturnValue({});
|
|
675
|
+
const mockedToolRegistry = {
|
|
676
|
+
removeMcpToolsByServer: vi.fn(),
|
|
677
|
+
registerTool: vi.fn(),
|
|
678
|
+
sortTools: vi.fn(),
|
|
679
|
+
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
680
|
+
};
|
|
681
|
+
const onToolsUpdatedSpy = vi.fn().mockResolvedValue(undefined);
|
|
682
|
+
const clientA = new McpClient('server-A', { command: 'cmd-a' }, mockedToolRegistry, {}, {}, workspaceContext, {}, false, onToolsUpdatedSpy);
|
|
683
|
+
const clientB = new McpClient('server-B', { command: 'cmd-b' }, mockedToolRegistry, {}, {}, workspaceContext, {}, false, onToolsUpdatedSpy);
|
|
684
|
+
await clientA.connect();
|
|
685
|
+
await clientB.connect();
|
|
686
|
+
const handlerA = mockClientA.setNotificationHandler.mock.calls[0][1];
|
|
687
|
+
const handlerB = mockClientB.setNotificationHandler.mock.calls[0][1];
|
|
688
|
+
// Trigger burst updates simultaneously
|
|
689
|
+
await Promise.all([handlerA(), handlerB()]);
|
|
690
|
+
expect(mockedToolRegistry.removeMcpToolsByServer).toHaveBeenCalledWith('server-A');
|
|
691
|
+
expect(mockedToolRegistry.removeMcpToolsByServer).toHaveBeenCalledWith('server-B');
|
|
692
|
+
// Verify fetching happened on both clients
|
|
693
|
+
expect(mockClientA.listTools).toHaveBeenCalled();
|
|
694
|
+
expect(mockClientB.listTools).toHaveBeenCalled();
|
|
695
|
+
// Verify tools from both servers were registered (2 total calls)
|
|
696
|
+
expect(mockedToolRegistry.registerTool).toHaveBeenCalledTimes(2);
|
|
697
|
+
// Verify the update callback was triggered for both
|
|
698
|
+
expect(onToolsUpdatedSpy).toHaveBeenCalledTimes(2);
|
|
699
|
+
});
|
|
700
|
+
it('should abort discovery and log error if timeout is exceeded during refresh', async () => {
|
|
701
|
+
vi.useFakeTimers();
|
|
702
|
+
const mockedClient = {
|
|
703
|
+
connect: vi.fn(),
|
|
704
|
+
getServerCapabilities: vi
|
|
705
|
+
.fn()
|
|
706
|
+
.mockReturnValue({ tools: { listChanged: true } }),
|
|
707
|
+
setNotificationHandler: vi.fn(),
|
|
708
|
+
// Mock listTools to simulate a long running process that respects the abort signal
|
|
709
|
+
listTools: vi.fn().mockImplementation(async (params, options) => new Promise((resolve, reject) => {
|
|
710
|
+
if (options?.signal?.aborted) {
|
|
711
|
+
return reject(new Error('Operation aborted'));
|
|
712
|
+
}
|
|
713
|
+
options?.signal?.addEventListener('abort', () => {
|
|
714
|
+
reject(new Error('Operation aborted'));
|
|
715
|
+
});
|
|
716
|
+
// Intentionally do not resolve immediately to simulate lag
|
|
717
|
+
})),
|
|
718
|
+
listPrompts: vi.fn().mockResolvedValue({ prompts: [] }),
|
|
719
|
+
request: vi.fn().mockResolvedValue({}),
|
|
720
|
+
registerCapabilities: vi.fn().mockResolvedValue({}),
|
|
721
|
+
setRequestHandler: vi.fn().mockResolvedValue({}),
|
|
722
|
+
};
|
|
723
|
+
vi.mocked(ClientLib.Client).mockReturnValue(mockedClient);
|
|
724
|
+
vi.spyOn(SdkClientStdioLib, 'StdioClientTransport').mockReturnValue({});
|
|
725
|
+
const mockedToolRegistry = {
|
|
726
|
+
removeMcpToolsByServer: vi.fn(),
|
|
727
|
+
registerTool: vi.fn(),
|
|
728
|
+
sortTools: vi.fn(),
|
|
729
|
+
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
730
|
+
};
|
|
731
|
+
const client = new McpClient('test-server',
|
|
732
|
+
// Set a short timeout
|
|
733
|
+
{ command: 'test-command', timeout: 100 }, mockedToolRegistry, {}, {}, workspaceContext, {}, false);
|
|
734
|
+
await client.connect();
|
|
735
|
+
const notificationCallback = mockedClient.setNotificationHandler.mock.calls[0][1];
|
|
736
|
+
const refreshPromise = notificationCallback();
|
|
737
|
+
vi.advanceTimersByTime(150);
|
|
738
|
+
await refreshPromise;
|
|
739
|
+
expect(mockedClient.listTools).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
|
|
740
|
+
signal: expect.any(AbortSignal),
|
|
741
|
+
}));
|
|
742
|
+
expect(mockedToolRegistry.registerTool).not.toHaveBeenCalled();
|
|
743
|
+
vi.useRealTimers();
|
|
744
|
+
});
|
|
745
|
+
it('should pass abort signal to onToolsUpdated callback', async () => {
|
|
746
|
+
const mockedClient = {
|
|
747
|
+
connect: vi.fn(),
|
|
748
|
+
getServerCapabilities: vi
|
|
749
|
+
.fn()
|
|
750
|
+
.mockReturnValue({ tools: { listChanged: true } }),
|
|
751
|
+
setNotificationHandler: vi.fn(),
|
|
752
|
+
listTools: vi.fn().mockResolvedValue({ tools: [] }),
|
|
753
|
+
listPrompts: vi.fn().mockResolvedValue({ prompts: [] }),
|
|
754
|
+
request: vi.fn().mockResolvedValue({}),
|
|
755
|
+
registerCapabilities: vi.fn().mockResolvedValue({}),
|
|
756
|
+
setRequestHandler: vi.fn().mockResolvedValue({}),
|
|
757
|
+
};
|
|
758
|
+
vi.mocked(ClientLib.Client).mockReturnValue(mockedClient);
|
|
759
|
+
vi.spyOn(SdkClientStdioLib, 'StdioClientTransport').mockReturnValue({});
|
|
760
|
+
const mockedToolRegistry = {
|
|
761
|
+
removeMcpToolsByServer: vi.fn(),
|
|
762
|
+
registerTool: vi.fn(),
|
|
763
|
+
sortTools: vi.fn(),
|
|
764
|
+
getMessageBus: vi.fn().mockReturnValue(undefined),
|
|
765
|
+
};
|
|
766
|
+
const onToolsUpdatedSpy = vi.fn().mockResolvedValue(undefined);
|
|
767
|
+
const client = new McpClient('test-server', { command: 'test-command' }, mockedToolRegistry, {}, {}, workspaceContext, {}, false, onToolsUpdatedSpy);
|
|
768
|
+
await client.connect();
|
|
769
|
+
const notificationCallback = mockedClient.setNotificationHandler.mock.calls[0][1];
|
|
770
|
+
await notificationCallback();
|
|
771
|
+
expect(onToolsUpdatedSpy).toHaveBeenCalledWith(expect.any(AbortSignal));
|
|
772
|
+
// Verify the signal passed was not aborted (happy path)
|
|
773
|
+
const signal = onToolsUpdatedSpy.mock.calls[0][0];
|
|
774
|
+
expect(signal.aborted).toBe(false);
|
|
349
775
|
});
|
|
350
776
|
});
|
|
351
777
|
describe('appendMcpServerCommand', () => {
|
|
@@ -374,19 +800,23 @@ describe('mcp-client', () => {
|
|
|
374
800
|
httpUrl: 'http://test-server',
|
|
375
801
|
}, false);
|
|
376
802
|
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
377
|
-
expect(transport).
|
|
803
|
+
expect(transport).toMatchObject({
|
|
804
|
+
_url: new URL('http://test-server'),
|
|
805
|
+
_requestInit: { headers: {} },
|
|
806
|
+
});
|
|
378
807
|
});
|
|
379
808
|
it('with headers', async () => {
|
|
380
|
-
// We need this to be an any type because we dig into its private state.
|
|
381
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
382
809
|
const transport = await createTransport('test-server', {
|
|
383
810
|
httpUrl: 'http://test-server',
|
|
384
811
|
headers: { Authorization: 'derp' },
|
|
385
812
|
}, false);
|
|
386
813
|
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
387
|
-
expect(transport).
|
|
388
|
-
|
|
389
|
-
|
|
814
|
+
expect(transport).toMatchObject({
|
|
815
|
+
_url: new URL('http://test-server'),
|
|
816
|
+
_requestInit: {
|
|
817
|
+
headers: { Authorization: 'derp' },
|
|
818
|
+
},
|
|
819
|
+
});
|
|
390
820
|
});
|
|
391
821
|
});
|
|
392
822
|
describe('should connect via url', () => {
|
|
@@ -394,20 +824,96 @@ describe('mcp-client', () => {
|
|
|
394
824
|
const transport = await createTransport('test-server', {
|
|
395
825
|
url: 'http://test-server',
|
|
396
826
|
}, false);
|
|
397
|
-
expect(transport).toBeInstanceOf(
|
|
398
|
-
expect(transport).
|
|
827
|
+
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
828
|
+
expect(transport).toMatchObject({
|
|
829
|
+
_url: new URL('http://test-server'),
|
|
830
|
+
_requestInit: { headers: {} },
|
|
831
|
+
});
|
|
399
832
|
});
|
|
400
833
|
it('with headers', async () => {
|
|
401
|
-
// We need this to be an any type because we dig into its private state.
|
|
402
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
403
834
|
const transport = await createTransport('test-server', {
|
|
404
835
|
url: 'http://test-server',
|
|
405
836
|
headers: { Authorization: 'derp' },
|
|
406
837
|
}, false);
|
|
838
|
+
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
839
|
+
expect(transport).toMatchObject({
|
|
840
|
+
_url: new URL('http://test-server'),
|
|
841
|
+
_requestInit: {
|
|
842
|
+
headers: { Authorization: 'derp' },
|
|
843
|
+
},
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
it('with type="http" creates StreamableHTTPClientTransport', async () => {
|
|
847
|
+
const transport = await createTransport('test-server', {
|
|
848
|
+
url: 'http://test-server',
|
|
849
|
+
type: 'http',
|
|
850
|
+
}, false);
|
|
851
|
+
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
852
|
+
expect(transport).toMatchObject({
|
|
853
|
+
_url: new URL('http://test-server'),
|
|
854
|
+
_requestInit: { headers: {} },
|
|
855
|
+
});
|
|
856
|
+
});
|
|
857
|
+
it('with type="sse" creates SSEClientTransport', async () => {
|
|
858
|
+
const transport = await createTransport('test-server', {
|
|
859
|
+
url: 'http://test-server',
|
|
860
|
+
type: 'sse',
|
|
861
|
+
}, false);
|
|
862
|
+
expect(transport).toBeInstanceOf(SSEClientTransport);
|
|
863
|
+
expect(transport).toMatchObject({
|
|
864
|
+
_url: new URL('http://test-server'),
|
|
865
|
+
_requestInit: { headers: {} },
|
|
866
|
+
});
|
|
867
|
+
});
|
|
868
|
+
it('without type defaults to StreamableHTTPClientTransport', async () => {
|
|
869
|
+
const transport = await createTransport('test-server', {
|
|
870
|
+
url: 'http://test-server',
|
|
871
|
+
}, false);
|
|
872
|
+
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
873
|
+
expect(transport).toMatchObject({
|
|
874
|
+
_url: new URL('http://test-server'),
|
|
875
|
+
_requestInit: { headers: {} },
|
|
876
|
+
});
|
|
877
|
+
});
|
|
878
|
+
it('with type="http" and headers applies headers correctly', async () => {
|
|
879
|
+
const transport = await createTransport('test-server', {
|
|
880
|
+
url: 'http://test-server',
|
|
881
|
+
type: 'http',
|
|
882
|
+
headers: { Authorization: 'Bearer token' },
|
|
883
|
+
}, false);
|
|
884
|
+
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
885
|
+
expect(transport).toMatchObject({
|
|
886
|
+
_url: new URL('http://test-server'),
|
|
887
|
+
_requestInit: {
|
|
888
|
+
headers: { Authorization: 'Bearer token' },
|
|
889
|
+
},
|
|
890
|
+
});
|
|
891
|
+
});
|
|
892
|
+
it('with type="sse" and headers applies headers correctly', async () => {
|
|
893
|
+
const transport = await createTransport('test-server', {
|
|
894
|
+
url: 'http://test-server',
|
|
895
|
+
type: 'sse',
|
|
896
|
+
headers: { 'X-API-Key': 'key123' },
|
|
897
|
+
}, false);
|
|
407
898
|
expect(transport).toBeInstanceOf(SSEClientTransport);
|
|
408
|
-
expect(transport).
|
|
409
|
-
|
|
410
|
-
|
|
899
|
+
expect(transport).toMatchObject({
|
|
900
|
+
_url: new URL('http://test-server'),
|
|
901
|
+
_requestInit: {
|
|
902
|
+
headers: { 'X-API-Key': 'key123' },
|
|
903
|
+
},
|
|
904
|
+
});
|
|
905
|
+
});
|
|
906
|
+
it('httpUrl takes priority over url when both are present', async () => {
|
|
907
|
+
const transport = await createTransport('test-server', {
|
|
908
|
+
httpUrl: 'http://test-server-http',
|
|
909
|
+
url: 'http://test-server-url',
|
|
910
|
+
}, false);
|
|
911
|
+
// httpUrl should take priority and create HTTP transport
|
|
912
|
+
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
913
|
+
expect(transport).toMatchObject({
|
|
914
|
+
_url: new URL('http://test-server-http'),
|
|
915
|
+
_requestInit: { headers: {} },
|
|
916
|
+
});
|
|
411
917
|
});
|
|
412
918
|
});
|
|
413
919
|
it('should connect via command', async () => {
|
|
@@ -429,6 +935,14 @@ describe('mcp-client', () => {
|
|
|
429
935
|
});
|
|
430
936
|
});
|
|
431
937
|
describe('useGoogleCredentialProvider', () => {
|
|
938
|
+
beforeEach(() => {
|
|
939
|
+
// Mock GoogleAuth client
|
|
940
|
+
const mockClient = {
|
|
941
|
+
getAccessToken: vi.fn().mockResolvedValue({ token: 'test-token' }),
|
|
942
|
+
quotaProjectId: 'myproject',
|
|
943
|
+
};
|
|
944
|
+
GoogleAuth.prototype.getClient = vi.fn().mockResolvedValue(mockClient);
|
|
945
|
+
});
|
|
432
946
|
it('should use GoogleCredentialProvider when specified', async () => {
|
|
433
947
|
const transport = await createTransport('test-server', {
|
|
434
948
|
httpUrl: 'http://test.googleapis.com',
|
|
@@ -448,9 +962,48 @@ describe('mcp-client', () => {
|
|
|
448
962
|
const googUserProject = transport._requestInit?.headers?.['X-Goog-User-Project'];
|
|
449
963
|
expect(googUserProject).toBe('myproject');
|
|
450
964
|
});
|
|
965
|
+
it('should use headers from GoogleCredentialProvider', async () => {
|
|
966
|
+
const mockGetRequestHeaders = vi.fn().mockResolvedValue({
|
|
967
|
+
'X-Goog-User-Project': 'provider-project',
|
|
968
|
+
});
|
|
969
|
+
vi.spyOn(GoogleCredentialProvider.prototype, 'getRequestHeaders').mockImplementation(mockGetRequestHeaders);
|
|
970
|
+
const transport = await createTransport('test-server', {
|
|
971
|
+
httpUrl: 'http://test.googleapis.com',
|
|
972
|
+
authProviderType: AuthProviderType.GOOGLE_CREDENTIALS,
|
|
973
|
+
oauth: {
|
|
974
|
+
scopes: ['scope1'],
|
|
975
|
+
},
|
|
976
|
+
}, false);
|
|
977
|
+
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
978
|
+
expect(mockGetRequestHeaders).toHaveBeenCalled();
|
|
979
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
980
|
+
const headers = transport._requestInit?.headers;
|
|
981
|
+
expect(headers['X-Goog-User-Project']).toBe('provider-project');
|
|
982
|
+
});
|
|
983
|
+
it('should prioritize provider headers over config headers', async () => {
|
|
984
|
+
const mockGetRequestHeaders = vi.fn().mockResolvedValue({
|
|
985
|
+
'X-Goog-User-Project': 'provider-project',
|
|
986
|
+
});
|
|
987
|
+
vi.spyOn(GoogleCredentialProvider.prototype, 'getRequestHeaders').mockImplementation(mockGetRequestHeaders);
|
|
988
|
+
const transport = await createTransport('test-server', {
|
|
989
|
+
httpUrl: 'http://test.googleapis.com',
|
|
990
|
+
authProviderType: AuthProviderType.GOOGLE_CREDENTIALS,
|
|
991
|
+
oauth: {
|
|
992
|
+
scopes: ['scope1'],
|
|
993
|
+
},
|
|
994
|
+
headers: {
|
|
995
|
+
'X-Goog-User-Project': 'config-project',
|
|
996
|
+
},
|
|
997
|
+
}, false);
|
|
998
|
+
expect(transport).toBeInstanceOf(StreamableHTTPClientTransport);
|
|
999
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1000
|
+
const headers = transport._requestInit?.headers;
|
|
1001
|
+
expect(headers['X-Goog-User-Project']).toBe('provider-project');
|
|
1002
|
+
});
|
|
451
1003
|
it('should use GoogleCredentialProvider with SSE transport', async () => {
|
|
452
1004
|
const transport = await createTransport('test-server', {
|
|
453
1005
|
url: 'http://test.googleapis.com',
|
|
1006
|
+
type: 'sse',
|
|
454
1007
|
authProviderType: AuthProviderType.GOOGLE_CREDENTIALS,
|
|
455
1008
|
oauth: {
|
|
456
1009
|
scopes: ['scope1'],
|
|
@@ -573,7 +1126,7 @@ describe('connectToMcpServer with OAuth', () => {
|
|
|
573
1126
|
const authUrl = 'http://auth.example.com/auth';
|
|
574
1127
|
const tokenUrl = 'http://auth.example.com/token';
|
|
575
1128
|
const wwwAuthHeader = `Bearer realm="test", resource_metadata="http://test-server.com/.well-known/oauth-protected-resource"`;
|
|
576
|
-
vi.mocked(mockedClient.connect).mockRejectedValueOnce(new
|
|
1129
|
+
vi.mocked(mockedClient.connect).mockRejectedValueOnce(new StreamableHTTPError(401, `Unauthorized\nwww-authenticate: ${wwwAuthHeader}`));
|
|
577
1130
|
vi.mocked(OAuthUtils.discoverOAuthConfig).mockResolvedValue({
|
|
578
1131
|
authorizationUrl: authUrl,
|
|
579
1132
|
tokenUrl,
|
|
@@ -586,7 +1139,7 @@ describe('connectToMcpServer with OAuth', () => {
|
|
|
586
1139
|
capturedTransport = transport;
|
|
587
1140
|
return Promise.resolve();
|
|
588
1141
|
});
|
|
589
|
-
const client = await connectToMcpServer('test-server', { httpUrl: serverUrl }, false, workspaceContext);
|
|
1142
|
+
const client = await connectToMcpServer('test-server', { httpUrl: serverUrl, oauth: { enabled: true } }, false, workspaceContext);
|
|
590
1143
|
expect(client).toBe(mockedClient);
|
|
591
1144
|
expect(mockedClient.connect).toHaveBeenCalledTimes(2);
|
|
592
1145
|
expect(mockAuthProvider.authenticate).toHaveBeenCalledOnce();
|
|
@@ -597,7 +1150,7 @@ describe('connectToMcpServer with OAuth', () => {
|
|
|
597
1150
|
const serverUrl = 'http://test-server.com';
|
|
598
1151
|
const authUrl = 'http://auth.example.com/auth';
|
|
599
1152
|
const tokenUrl = 'http://auth.example.com/token';
|
|
600
|
-
vi.mocked(mockedClient.connect).mockRejectedValueOnce(new
|
|
1153
|
+
vi.mocked(mockedClient.connect).mockRejectedValueOnce(new StreamableHTTPError(401, 'Unauthorized'));
|
|
601
1154
|
vi.mocked(OAuthUtils.discoverOAuthConfig).mockResolvedValue({
|
|
602
1155
|
authorizationUrl: authUrl,
|
|
603
1156
|
tokenUrl,
|
|
@@ -611,7 +1164,7 @@ describe('connectToMcpServer with OAuth', () => {
|
|
|
611
1164
|
capturedTransport = transport;
|
|
612
1165
|
return Promise.resolve();
|
|
613
1166
|
});
|
|
614
|
-
const client = await connectToMcpServer('test-server', { httpUrl: serverUrl }, false, workspaceContext);
|
|
1167
|
+
const client = await connectToMcpServer('test-server', { httpUrl: serverUrl, oauth: { enabled: true } }, false, workspaceContext);
|
|
615
1168
|
expect(client).toBe(mockedClient);
|
|
616
1169
|
expect(mockedClient.connect).toHaveBeenCalledTimes(2);
|
|
617
1170
|
expect(mockAuthProvider.authenticate).toHaveBeenCalledOnce();
|
|
@@ -620,4 +1173,118 @@ describe('connectToMcpServer with OAuth', () => {
|
|
|
620
1173
|
expect(authHeader).toBe('Bearer test-access-token-from-discovery');
|
|
621
1174
|
});
|
|
622
1175
|
});
|
|
1176
|
+
describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
|
1177
|
+
let mockedClient;
|
|
1178
|
+
let workspaceContext;
|
|
1179
|
+
let testWorkspace;
|
|
1180
|
+
beforeEach(() => {
|
|
1181
|
+
mockedClient = {
|
|
1182
|
+
connect: vi.fn(),
|
|
1183
|
+
close: vi.fn(),
|
|
1184
|
+
registerCapabilities: vi.fn(),
|
|
1185
|
+
setRequestHandler: vi.fn(),
|
|
1186
|
+
onclose: vi.fn(),
|
|
1187
|
+
notification: vi.fn(),
|
|
1188
|
+
};
|
|
1189
|
+
vi.mocked(ClientLib.Client).mockImplementation(() => mockedClient);
|
|
1190
|
+
testWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), 'gemini-agent-test-'));
|
|
1191
|
+
workspaceContext = new WorkspaceContext(testWorkspace);
|
|
1192
|
+
vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
1193
|
+
vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
1194
|
+
vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
1195
|
+
});
|
|
1196
|
+
afterEach(() => {
|
|
1197
|
+
vi.clearAllMocks();
|
|
1198
|
+
});
|
|
1199
|
+
it('should NOT trigger fallback when type="http" is explicit', async () => {
|
|
1200
|
+
vi.mocked(mockedClient.connect).mockRejectedValueOnce(new Error('Connection failed'));
|
|
1201
|
+
await expect(connectToMcpServer('test-server', { url: 'http://test-server', type: 'http' }, false, workspaceContext)).rejects.toThrow('Connection failed');
|
|
1202
|
+
// Should only try once (no fallback)
|
|
1203
|
+
expect(mockedClient.connect).toHaveBeenCalledTimes(1);
|
|
1204
|
+
});
|
|
1205
|
+
it('should NOT trigger fallback when type="sse" is explicit', async () => {
|
|
1206
|
+
vi.mocked(mockedClient.connect).mockRejectedValueOnce(new Error('Connection failed'));
|
|
1207
|
+
await expect(connectToMcpServer('test-server', { url: 'http://test-server', type: 'sse' }, false, workspaceContext)).rejects.toThrow('Connection failed');
|
|
1208
|
+
// Should only try once (no fallback)
|
|
1209
|
+
expect(mockedClient.connect).toHaveBeenCalledTimes(1);
|
|
1210
|
+
});
|
|
1211
|
+
it('should trigger fallback when url provided without type and HTTP fails', async () => {
|
|
1212
|
+
vi.mocked(mockedClient.connect)
|
|
1213
|
+
.mockRejectedValueOnce(new StreamableHTTPError(500, 'Server error'))
|
|
1214
|
+
.mockResolvedValueOnce(undefined);
|
|
1215
|
+
const client = await connectToMcpServer('test-server', { url: 'http://test-server' }, false, workspaceContext);
|
|
1216
|
+
expect(client).toBe(mockedClient);
|
|
1217
|
+
// First HTTP attempt fails, second SSE attempt succeeds
|
|
1218
|
+
expect(mockedClient.connect).toHaveBeenCalledTimes(2);
|
|
1219
|
+
});
|
|
1220
|
+
it('should throw original HTTP error when both HTTP and SSE fail (non-401)', async () => {
|
|
1221
|
+
const httpError = new StreamableHTTPError(500, 'Server error');
|
|
1222
|
+
const sseError = new Error('SSE connection failed');
|
|
1223
|
+
vi.mocked(mockedClient.connect)
|
|
1224
|
+
.mockRejectedValueOnce(httpError)
|
|
1225
|
+
.mockRejectedValueOnce(sseError);
|
|
1226
|
+
await expect(connectToMcpServer('test-server', { url: 'http://test-server' }, false, workspaceContext)).rejects.toThrow('Server error');
|
|
1227
|
+
expect(mockedClient.connect).toHaveBeenCalledTimes(2);
|
|
1228
|
+
});
|
|
1229
|
+
it('should handle HTTP 404 followed by SSE success', async () => {
|
|
1230
|
+
vi.mocked(mockedClient.connect)
|
|
1231
|
+
.mockRejectedValueOnce(new StreamableHTTPError(404, 'Not Found'))
|
|
1232
|
+
.mockResolvedValueOnce(undefined);
|
|
1233
|
+
const client = await connectToMcpServer('test-server', { url: 'http://test-server' }, false, workspaceContext);
|
|
1234
|
+
expect(client).toBe(mockedClient);
|
|
1235
|
+
expect(mockedClient.connect).toHaveBeenCalledTimes(2);
|
|
1236
|
+
});
|
|
1237
|
+
});
|
|
1238
|
+
describe('connectToMcpServer - OAuth with transport fallback', () => {
|
|
1239
|
+
let mockedClient;
|
|
1240
|
+
let workspaceContext;
|
|
1241
|
+
let testWorkspace;
|
|
1242
|
+
let mockAuthProvider;
|
|
1243
|
+
let mockTokenStorage;
|
|
1244
|
+
beforeEach(() => {
|
|
1245
|
+
mockedClient = {
|
|
1246
|
+
connect: vi.fn(),
|
|
1247
|
+
close: vi.fn(),
|
|
1248
|
+
registerCapabilities: vi.fn(),
|
|
1249
|
+
setRequestHandler: vi.fn(),
|
|
1250
|
+
onclose: vi.fn(),
|
|
1251
|
+
notification: vi.fn(),
|
|
1252
|
+
};
|
|
1253
|
+
vi.mocked(ClientLib.Client).mockImplementation(() => mockedClient);
|
|
1254
|
+
testWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), 'gemini-agent-test-'));
|
|
1255
|
+
workspaceContext = new WorkspaceContext(testWorkspace);
|
|
1256
|
+
vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
1257
|
+
vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
1258
|
+
vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
1259
|
+
mockTokenStorage = {
|
|
1260
|
+
getCredentials: vi.fn().mockResolvedValue({ clientId: 'test-client' }),
|
|
1261
|
+
};
|
|
1262
|
+
vi.mocked(MCPOAuthTokenStorage).mockReturnValue(mockTokenStorage);
|
|
1263
|
+
mockAuthProvider = {
|
|
1264
|
+
authenticate: vi.fn().mockResolvedValue(undefined),
|
|
1265
|
+
getValidToken: vi.fn().mockResolvedValue('test-access-token'),
|
|
1266
|
+
tokenStorage: mockTokenStorage,
|
|
1267
|
+
};
|
|
1268
|
+
vi.mocked(MCPOAuthProvider).mockReturnValue(mockAuthProvider);
|
|
1269
|
+
vi.mocked(OAuthUtils.discoverOAuthConfig).mockResolvedValue({
|
|
1270
|
+
authorizationUrl: 'http://auth.example.com/auth',
|
|
1271
|
+
tokenUrl: 'http://auth.example.com/token',
|
|
1272
|
+
scopes: ['test-scope'],
|
|
1273
|
+
});
|
|
1274
|
+
});
|
|
1275
|
+
afterEach(() => {
|
|
1276
|
+
vi.clearAllMocks();
|
|
1277
|
+
});
|
|
1278
|
+
it('should handle HTTP 404 → SSE 401 → OAuth → SSE+OAuth succeeds', async () => {
|
|
1279
|
+
// Tests that OAuth flow works when SSE (not HTTP) requires auth
|
|
1280
|
+
vi.mocked(mockedClient.connect)
|
|
1281
|
+
.mockRejectedValueOnce(new StreamableHTTPError(404, 'Not Found'))
|
|
1282
|
+
.mockRejectedValueOnce(new StreamableHTTPError(401, 'Unauthorized'))
|
|
1283
|
+
.mockResolvedValueOnce(undefined);
|
|
1284
|
+
const client = await connectToMcpServer('test-server', { url: 'http://test-server', oauth: { enabled: true } }, false, workspaceContext);
|
|
1285
|
+
expect(client).toBe(mockedClient);
|
|
1286
|
+
expect(mockedClient.connect).toHaveBeenCalledTimes(3);
|
|
1287
|
+
expect(mockAuthProvider.authenticate).toHaveBeenCalledOnce();
|
|
1288
|
+
});
|
|
1289
|
+
});
|
|
623
1290
|
//# sourceMappingURL=mcp-client.test.js.map
|