@1mcp/agent 0.35.0-beta.3 → 0.35.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/build/admin/assets/admin-AboutRuntimeWorkspace-DH2QC2nJ.js +1 -0
- package/build/admin/assets/admin-BackendLogsWorkspace-BKavXT9B.js +1 -0
- package/build/admin/assets/admin-InstructionTemplatesWorkspace-CvG4EPdZ.js +1 -0
- package/build/admin/assets/admin-OAuthServicesWorkspace-B8y2weAX.js +1 -0
- package/build/admin/assets/admin-PresetAuthoringWorkspace-BboJVBFl.js +1 -0
- package/build/admin/assets/admin-console-D6jyI73o.css +1 -0
- package/build/admin/assets/{admin-console-Bbil6BQU.css → admin-console-DmRyrCWD.css} +1 -1
- package/build/admin/assets/admin-console-HJH52xGP.js +3 -0
- package/build/admin/assets/admin-icons-CYGTDZA5.js +1 -0
- package/build/admin/assets/admin-mantine-xwbfu5Gd.js +53 -0
- package/build/admin/assets/admin-react-BSaWeqgv.js +8 -0
- package/build/admin/assets/admin-rolldown-runtime-Bh1tDfsg.js +1 -0
- package/build/admin/favicon.svg +4 -0
- package/build/admin/index.html +8 -2
- package/build/auth/sdkOAuthClientProvider.d.ts +4 -0
- package/build/auth/sdkOAuthClientProvider.d.ts.map +1 -1
- package/build/auth/sdkOAuthClientProvider.js +29 -0
- package/build/auth/sdkOAuthClientProvider.js.map +1 -1
- package/build/auth/sessionTypes.d.ts +2 -0
- package/build/auth/sessionTypes.d.ts.map +1 -1
- package/build/auth/sessionTypes.js.map +1 -1
- package/build/commands/inspect/inspect.d.ts +1 -0
- package/build/commands/inspect/inspect.d.ts.map +1 -1
- package/build/commands/inspect/inspect.js +4 -1
- package/build/commands/inspect/inspect.js.map +1 -1
- package/build/commands/instructions/instructions.d.ts.map +1 -1
- package/build/commands/instructions/instructions.js +56 -1
- package/build/commands/instructions/instructions.js.map +1 -1
- package/build/commands/mcp/add.js +2 -2
- package/build/commands/mcp/add.js.map +1 -1
- package/build/commands/mcp/list.js +1 -1
- package/build/commands/mcp/list.js.map +1 -1
- package/build/commands/mcp/update.js +1 -1
- package/build/commands/mcp/update.js.map +1 -1
- package/build/commands/proxy/proxy.d.ts.map +1 -1
- package/build/commands/proxy/proxy.js +2 -0
- package/build/commands/proxy/proxy.js.map +1 -1
- package/build/commands/run/run.d.ts +1 -0
- package/build/commands/run/run.d.ts.map +1 -1
- package/build/commands/run/run.js +4 -1
- package/build/commands/run/run.js.map +1 -1
- package/build/commands/serve/index.d.ts +10 -0
- package/build/commands/serve/index.d.ts.map +1 -1
- package/build/commands/serve/index.js +10 -0
- package/build/commands/serve/index.js.map +1 -1
- package/build/commands/serve/serve.d.ts +3 -0
- package/build/commands/serve/serve.d.ts.map +1 -1
- package/build/commands/serve/serve.js +20 -0
- package/build/commands/serve/serve.js.map +1 -1
- package/build/commands/serve/serveBackground.d.ts.map +1 -1
- package/build/commands/serve/serveBackground.js +2 -0
- package/build/commands/serve/serveBackground.js.map +1 -1
- package/build/commands/serve/templateContextTrust.d.ts +12 -0
- package/build/commands/serve/templateContextTrust.d.ts.map +1 -0
- package/build/commands/serve/templateContextTrust.js +23 -0
- package/build/commands/serve/templateContextTrust.js.map +1 -0
- package/build/commands/shared/apiClient.d.ts +3 -0
- package/build/commands/shared/apiClient.d.ts.map +1 -1
- package/build/commands/shared/apiClient.js +6 -0
- package/build/commands/shared/apiClient.js.map +1 -1
- package/build/commands/shared/clientSurfaceAttachment.d.ts +11 -0
- package/build/commands/shared/clientSurfaceAttachment.d.ts.map +1 -1
- package/build/commands/shared/clientSurfaceAttachment.js +64 -6
- package/build/commands/shared/clientSurfaceAttachment.js.map +1 -1
- package/build/commands/shared/inspectApiSchemas.d.ts +8 -8
- package/build/commands/shared/serveClient.d.ts +2 -1
- package/build/commands/shared/serveClient.d.ts.map +1 -1
- package/build/commands/shared/serveClient.js +3 -1
- package/build/commands/shared/serveClient.js.map +1 -1
- package/build/commands/shared/serveTargetResolver.d.ts +4 -0
- package/build/commands/shared/serveTargetResolver.d.ts.map +1 -1
- package/build/commands/shared/serveTargetResolver.js +22 -22
- package/build/commands/shared/serveTargetResolver.js.map +1 -1
- package/build/commands/wait/wait.d.ts.map +1 -1
- package/build/commands/wait/wait.js +1 -0
- package/build/commands/wait/wait.js.map +1 -1
- package/build/config/configManager.d.ts +11 -1
- package/build/config/configManager.d.ts.map +1 -1
- package/build/config/configManager.js +13 -0
- package/build/config/configManager.js.map +1 -1
- package/build/config/configuredServerTargets.d.ts +3 -0
- package/build/config/configuredServerTargets.d.ts.map +1 -0
- package/build/config/configuredServerTargets.js +8 -0
- package/build/config/configuredServerTargets.js.map +1 -0
- package/build/config/mcpConfigManager.d.ts +3 -0
- package/build/config/mcpConfigManager.d.ts.map +1 -1
- package/build/config/mcpConfigManager.js +15 -0
- package/build/config/mcpConfigManager.js.map +1 -1
- package/build/constants/api.d.ts +1 -0
- package/build/constants/api.d.ts.map +1 -1
- package/build/constants/api.js +1 -0
- package/build/constants/api.js.map +1 -1
- package/build/constants/mcp.d.ts +1 -1
- package/build/constants/mcp.d.ts.map +1 -1
- package/build/constants/mcp.js +1 -1
- package/build/constants/mcp.js.map +1 -1
- package/build/core/capabilities/capabilityAggregator.d.ts.map +1 -1
- package/build/core/capabilities/capabilityAggregator.js +33 -14
- package/build/core/capabilities/capabilityAggregator.js.map +1 -1
- package/build/core/capabilities/capabilityCatalog.d.ts +23 -1
- package/build/core/capabilities/capabilityCatalog.d.ts.map +1 -1
- package/build/core/capabilities/capabilityCatalog.js +81 -7
- package/build/core/capabilities/capabilityCatalog.js.map +1 -1
- package/build/core/capabilities/capabilityPagination.d.ts +40 -0
- package/build/core/capabilities/capabilityPagination.d.ts.map +1 -0
- package/build/core/capabilities/capabilityPagination.js +287 -0
- package/build/core/capabilities/capabilityPagination.js.map +1 -0
- package/build/core/capabilities/capabilityVisibility.d.ts +2 -1
- package/build/core/capabilities/capabilityVisibility.d.ts.map +1 -1
- package/build/core/capabilities/capabilityVisibility.js +2 -1
- package/build/core/capabilities/capabilityVisibility.js.map +1 -1
- package/build/core/capabilities/configuredToolSnapshot.d.ts +12 -0
- package/build/core/capabilities/configuredToolSnapshot.d.ts.map +1 -0
- package/build/core/capabilities/configuredToolSnapshot.js +98 -0
- package/build/core/capabilities/configuredToolSnapshot.js.map +1 -0
- package/build/core/capabilities/lazyLoadingOrchestrator.d.ts.map +1 -1
- package/build/core/capabilities/lazyLoadingOrchestrator.js +8 -4
- package/build/core/capabilities/lazyLoadingOrchestrator.js.map +1 -1
- package/build/core/capabilities/metaToolProvider.js +3 -3
- package/build/core/capabilities/metaToolProvider.js.map +1 -1
- package/build/core/client/clientManager.d.ts.map +1 -1
- package/build/core/client/clientManager.js +11 -6
- package/build/core/client/clientManager.js.map +1 -1
- package/build/core/client/postAuthOAuthRecovery.d.ts +6 -0
- package/build/core/client/postAuthOAuthRecovery.d.ts.map +1 -0
- package/build/core/client/postAuthOAuthRecovery.js +74 -0
- package/build/core/client/postAuthOAuthRecovery.js.map +1 -0
- package/build/core/configChangeHandler.d.ts +2 -0
- package/build/core/configChangeHandler.d.ts.map +1 -1
- package/build/core/configChangeHandler.js +38 -11
- package/build/core/configChangeHandler.js.map +1 -1
- package/build/core/context/templateContextTrust.d.ts +66 -0
- package/build/core/context/templateContextTrust.d.ts.map +1 -0
- package/build/core/context/templateContextTrust.js +184 -0
- package/build/core/context/templateContextTrust.js.map +1 -0
- package/build/core/instructions/effectiveServerInstructions.d.ts +17 -0
- package/build/core/instructions/effectiveServerInstructions.d.ts.map +1 -0
- package/build/core/instructions/effectiveServerInstructions.js +12 -0
- package/build/core/instructions/effectiveServerInstructions.js.map +1 -0
- package/build/core/instructions/instructionAggregator.d.ts +51 -2
- package/build/core/instructions/instructionAggregator.d.ts.map +1 -1
- package/build/core/instructions/instructionAggregator.js +191 -30
- package/build/core/instructions/instructionAggregator.js.map +1 -1
- package/build/core/instructions/instructionsDistribution.d.ts +53 -0
- package/build/core/instructions/instructionsDistribution.d.ts.map +1 -1
- package/build/core/instructions/instructionsDistribution.js +25 -0
- package/build/core/instructions/instructionsDistribution.js.map +1 -1
- package/build/core/instructions/templateTypes.d.ts +25 -3
- package/build/core/instructions/templateTypes.d.ts.map +1 -1
- package/build/core/instructions/templateTypes.js +45 -0
- package/build/core/instructions/templateTypes.js.map +1 -1
- package/build/core/protocol/notificationHandlers.d.ts.map +1 -1
- package/build/core/protocol/notificationHandlers.js +26 -4
- package/build/core/protocol/notificationHandlers.js.map +1 -1
- package/build/core/protocol/promptRequestHandlers.d.ts.map +1 -1
- package/build/core/protocol/promptRequestHandlers.js +20 -11
- package/build/core/protocol/promptRequestHandlers.js.map +1 -1
- package/build/core/protocol/requestHandlerUtils.d.ts +4 -0
- package/build/core/protocol/requestHandlerUtils.d.ts.map +1 -1
- package/build/core/protocol/requestHandlerUtils.js +24 -4
- package/build/core/protocol/requestHandlerUtils.js.map +1 -1
- package/build/core/protocol/resourceRequestHandlers.d.ts.map +1 -1
- package/build/core/protocol/resourceRequestHandlers.js +38 -18
- package/build/core/protocol/resourceRequestHandlers.js.map +1 -1
- package/build/core/protocol/toolRequestHandlers.d.ts.map +1 -1
- package/build/core/protocol/toolRequestHandlers.js +79 -23
- package/build/core/protocol/toolRequestHandlers.js.map +1 -1
- package/build/core/server/agentConfig.d.ts +4 -0
- package/build/core/server/agentConfig.d.ts.map +1 -1
- package/build/core/server/agentConfig.js +3 -0
- package/build/core/server/agentConfig.js.map +1 -1
- package/build/core/server/clientInstancePool.js +2 -2
- package/build/core/server/clientInstancePool.js.map +1 -1
- package/build/core/server/connectionManager.d.ts.map +1 -1
- package/build/core/server/connectionManager.js +2 -0
- package/build/core/server/connectionManager.js.map +1 -1
- package/build/core/server/disabledTools.d.ts +2 -0
- package/build/core/server/disabledTools.d.ts.map +1 -1
- package/build/core/server/disabledTools.js +7 -4
- package/build/core/server/disabledTools.js.map +1 -1
- package/build/core/server/requestContextPreparation.d.ts +2 -1
- package/build/core/server/requestContextPreparation.d.ts.map +1 -1
- package/build/core/server/requestContextPreparation.js.map +1 -1
- package/build/core/server/runtimeScopeOwnership.js +1 -1
- package/build/core/server/runtimeScopeOwnership.js.map +1 -1
- package/build/core/server/serverManager.d.ts.map +1 -1
- package/build/core/server/serverManager.js +1 -0
- package/build/core/server/serverManager.js.map +1 -1
- package/build/core/server/templateIdentity.d.ts +1 -0
- package/build/core/server/templateIdentity.d.ts.map +1 -1
- package/build/core/server/templateIdentity.js +11 -1
- package/build/core/server/templateIdentity.js.map +1 -1
- package/build/core/server/templateServerManager.d.ts +5 -1
- package/build/core/server/templateServerManager.d.ts.map +1 -1
- package/build/core/server/templateServerManager.js +27 -9
- package/build/core/server/templateServerManager.js.map +1 -1
- package/build/core/server/toolDescriptionOverrides.d.ts +9 -0
- package/build/core/server/toolDescriptionOverrides.d.ts.map +1 -0
- package/build/core/server/toolDescriptionOverrides.js +44 -0
- package/build/core/server/toolDescriptionOverrides.js.map +1 -0
- package/build/core/tools/handlers/serverManagementHandler.d.ts +15 -1
- package/build/core/tools/handlers/serverManagementHandler.d.ts.map +1 -1
- package/build/core/tools/internal/adapters/helpers/configTransformers.d.ts +2 -0
- package/build/core/tools/internal/adapters/helpers/configTransformers.d.ts.map +1 -1
- package/build/core/tools/internal/schemas/discovery.d.ts +2 -2
- package/build/core/tools/internal/schemas/index.d.ts +9 -9
- package/build/core/tools/internal/schemas/installation.d.ts +3 -3
- package/build/core/tools/internal/schemas/management.d.ts +4 -4
- package/build/core/types/server.d.ts +2 -0
- package/build/core/types/server.d.ts.map +1 -1
- package/build/core/types/server.js.map +1 -1
- package/build/core/types/transport.d.ts +43 -0
- package/build/core/types/transport.d.ts.map +1 -1
- package/build/core/types/transport.js +60 -1
- package/build/core/types/transport.js.map +1 -1
- package/build/domains/admin/adminConfiguredServerService.d.ts +131 -4
- package/build/domains/admin/adminConfiguredServerService.d.ts.map +1 -1
- package/build/domains/admin/adminConfiguredServerService.js +766 -39
- package/build/domains/admin/adminConfiguredServerService.js.map +1 -1
- package/build/domains/admin/adminDomain.d.ts +13 -0
- package/build/domains/admin/adminDomain.d.ts.map +1 -1
- package/build/domains/admin/adminDomain.js +16 -0
- package/build/domains/admin/adminDomain.js.map +1 -1
- package/build/domains/admin/adminInstructionPreviewRuntime.d.ts +7 -0
- package/build/domains/admin/adminInstructionPreviewRuntime.d.ts.map +1 -0
- package/build/domains/admin/adminInstructionPreviewRuntime.js +154 -0
- package/build/domains/admin/adminInstructionPreviewRuntime.js.map +1 -0
- package/build/domains/admin/adminInstructionTemplateService.d.ts +169 -0
- package/build/domains/admin/adminInstructionTemplateService.d.ts.map +1 -0
- package/build/domains/admin/adminInstructionTemplateService.js +181 -0
- package/build/domains/admin/adminInstructionTemplateService.js.map +1 -0
- package/build/domains/admin/configuredToolInventory.d.ts +46 -0
- package/build/domains/admin/configuredToolInventory.d.ts.map +1 -0
- package/build/domains/admin/configuredToolInventory.js +135 -0
- package/build/domains/admin/configuredToolInventory.js.map +1 -0
- package/build/domains/backend-logs/backendLogSource.d.ts.map +1 -1
- package/build/domains/backend-logs/backendLogSource.js.map +1 -1
- package/build/domains/config-change/configChange.d.ts +2 -1
- package/build/domains/config-change/configChange.d.ts.map +1 -1
- package/build/domains/config-change/configChange.js +241 -13
- package/build/domains/config-change/configChange.js.map +1 -1
- package/build/domains/config-change/types.d.ts +34 -2
- package/build/domains/config-change/types.d.ts.map +1 -1
- package/build/domains/installation/serverInstallationSourceResolution.js +2 -0
- package/build/domains/installation/serverInstallationSourceResolution.js.map +1 -1
- package/build/domains/installation/serverInstallationTypes.d.ts +8 -0
- package/build/domains/installation/serverInstallationTypes.d.ts.map +1 -1
- package/build/domains/installation/serverInstallationWorkflow.js +16 -3
- package/build/domains/installation/serverInstallationWorkflow.js.map +1 -1
- package/build/domains/instruction-template/instructionTemplateManager.d.ts +58 -0
- package/build/domains/instruction-template/instructionTemplateManager.d.ts.map +1 -0
- package/build/domains/instruction-template/instructionTemplateManager.js +167 -0
- package/build/domains/instruction-template/instructionTemplateManager.js.map +1 -0
- package/build/domains/runtime-targets/runtimeIdentityVerification.d.ts +1 -0
- package/build/domains/runtime-targets/runtimeIdentityVerification.d.ts.map +1 -1
- package/build/domains/runtime-targets/runtimeIdentityVerification.js +6 -5
- package/build/domains/runtime-targets/runtimeIdentityVerification.js.map +1 -1
- package/build/transport/http/middlewares/httpRequestLogger.d.ts.map +1 -1
- package/build/transport/http/middlewares/httpRequestLogger.js +3 -2
- package/build/transport/http/middlewares/httpRequestLogger.js.map +1 -1
- package/build/transport/http/routes/adminRoutes.d.ts +2 -0
- package/build/transport/http/routes/adminRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/adminRoutes.js +422 -23
- package/build/transport/http/routes/adminRoutes.js.map +1 -1
- package/build/transport/http/routes/apiRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/apiRoutes.js +2 -0
- package/build/transport/http/routes/apiRoutes.js.map +1 -1
- package/build/transport/http/routes/inspectRequestContext.d.ts +2 -0
- package/build/transport/http/routes/inspectRequestContext.d.ts.map +1 -1
- package/build/transport/http/routes/inspectRequestContext.js +15 -6
- package/build/transport/http/routes/inspectRequestContext.js.map +1 -1
- package/build/transport/http/routes/inspectRoutes.d.ts +9 -0
- package/build/transport/http/routes/inspectRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/inspectRoutes.js +15 -10
- package/build/transport/http/routes/inspectRoutes.js.map +1 -1
- package/build/transport/http/routes/instructionsRoutes.d.ts +4 -0
- package/build/transport/http/routes/instructionsRoutes.d.ts.map +1 -0
- package/build/transport/http/routes/instructionsRoutes.js +147 -0
- package/build/transport/http/routes/instructionsRoutes.js.map +1 -0
- package/build/transport/http/routes/streamableHttpRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/streamableHttpRoutes.js +13 -4
- package/build/transport/http/routes/streamableHttpRoutes.js.map +1 -1
- package/build/transport/http/routes/toolRoutes.d.ts.map +1 -1
- package/build/transport/http/routes/toolRoutes.js +5 -4
- package/build/transport/http/routes/toolRoutes.js.map +1 -1
- package/build/transport/http/server.d.ts.map +1 -1
- package/build/transport/http/server.js +17 -0
- package/build/transport/http/server.js.map +1 -1
- package/build/transport/http/storage/streamableSessionRepository.d.ts.map +1 -1
- package/build/transport/http/storage/streamableSessionRepository.js +2 -0
- package/build/transport/http/storage/streamableSessionRepository.js.map +1 -1
- package/build/transport/http/streamableSessionLifecycle.d.ts +3 -1
- package/build/transport/http/streamableSessionLifecycle.d.ts.map +1 -1
- package/build/transport/http/streamableSessionLifecycle.js +23 -6
- package/build/transport/http/streamableSessionLifecycle.js.map +1 -1
- package/build/transport/http/utils/contextExtractor.d.ts +14 -16
- package/build/transport/http/utils/contextExtractor.d.ts.map +1 -1
- package/build/transport/http/utils/contextExtractor.js +124 -33
- package/build/transport/http/utils/contextExtractor.js.map +1 -1
- package/build/transport/http/utils/templateContextAuthority.d.ts +13 -0
- package/build/transport/http/utils/templateContextAuthority.d.ts.map +1 -0
- package/build/transport/http/utils/templateContextAuthority.js +121 -0
- package/build/transport/http/utils/templateContextAuthority.js.map +1 -0
- package/build/transport/stdioProxyTransport.d.ts +3 -0
- package/build/transport/stdioProxyTransport.d.ts.map +1 -1
- package/build/transport/stdioProxyTransport.js +7 -2
- package/build/transport/stdioProxyTransport.js.map +1 -1
- package/build/utils/validation/discoveredServer.d.ts +15 -0
- package/build/utils/validation/discoveredServer.d.ts.map +1 -0
- package/build/utils/validation/discoveredServer.js +2 -0
- package/build/utils/validation/discoveredServer.js.map +1 -0
- package/build/utils/validation/urlDetection.d.ts +4 -5
- package/build/utils/validation/urlDetection.d.ts.map +1 -1
- package/build/utils/validation/urlDetection.js +94 -25
- package/build/utils/validation/urlDetection.js.map +1 -1
- package/package.json +2 -2
- package/build/admin/assets/admin-console-BOl7XR0p.js +0 -61
- package/build/utils/ui/pagination.d.ts +0 -35
- package/build/utils/ui/pagination.d.ts.map +0 -1
- package/build/utils/ui/pagination.js +0 -166
- package/build/utils/ui/pagination.js.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { mergeGlobalAndServerConfig } from '../../config/mcpConfigMerge.js';
|
|
3
|
+
import { normalizeDisabledToolsForServer } from '../../core/server/disabledTools.js';
|
|
3
4
|
import { GLOBAL_TRANSPORT_CONFIG_KEYS, transportConfigSchema, } from '../../core/types/index.js';
|
|
4
|
-
import { fingerprintConfiguredServerDefaults, fingerprintConfiguredServerSecretValue, fingerprintConfiguredServerTarget, isConfiguredServerTargetDisabled, } from '../../domains/config-change/configChange.js';
|
|
5
|
+
import { fingerprintConfiguredServerConfigDocument, fingerprintConfiguredServerDefaults, fingerprintConfiguredServerSecretValue, fingerprintConfiguredServerTarget, isConfiguredServerTargetDisabled, } from '../../domains/config-change/configChange.js';
|
|
6
|
+
import { createServerInstallationWorkflow, } from '../../domains/installation/serverInstallationWorkflow.js';
|
|
5
7
|
import { z } from 'zod';
|
|
6
8
|
const ENV_PLACEHOLDER_PATTERN = /\$\{[^}]+\}|\$[A-Za-z_][A-Za-z0-9_]*/u;
|
|
7
9
|
const previewTransportConfigSchema = transportConfigSchema.extend({
|
|
@@ -12,6 +14,365 @@ const previewTransportConfigSchema = transportConfigSchema.extend({
|
|
|
12
14
|
})
|
|
13
15
|
.optional(),
|
|
14
16
|
});
|
|
17
|
+
const configuredServerCreateSecretSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
fieldPath: z.array(z.string()).min(2),
|
|
20
|
+
action: z.literal('replace'),
|
|
21
|
+
replacement: z.object({
|
|
22
|
+
kind: z.enum(['environmentReference', 'inlineSecret']),
|
|
23
|
+
value: z.string(),
|
|
24
|
+
}),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
const configuredServerCreateDraftSchema = z
|
|
28
|
+
.object({
|
|
29
|
+
name: z.string(),
|
|
30
|
+
enabled: z.boolean().optional(),
|
|
31
|
+
tags: z.array(z.string()).optional(),
|
|
32
|
+
transport: z
|
|
33
|
+
.object({
|
|
34
|
+
type: z.enum(['stdio', 'http', 'sse']),
|
|
35
|
+
command: z.string().optional(),
|
|
36
|
+
url: z.string().optional(),
|
|
37
|
+
args: z.array(z.string()).optional(),
|
|
38
|
+
cwd: z.string().optional(),
|
|
39
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
40
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
41
|
+
timeout: z.number().optional(),
|
|
42
|
+
connectionTimeout: z.number().optional(),
|
|
43
|
+
requestTimeout: z.number().optional(),
|
|
44
|
+
restartOnExit: z.boolean().optional(),
|
|
45
|
+
maxRestarts: z.number().optional(),
|
|
46
|
+
restartDelay: z.number().optional(),
|
|
47
|
+
})
|
|
48
|
+
.strict(),
|
|
49
|
+
secrets: z.array(configuredServerCreateSecretSchema).optional(),
|
|
50
|
+
})
|
|
51
|
+
.strict();
|
|
52
|
+
const CONFIGURED_SERVER_CREATE_TRANSPORT_FIELD_KEYS = new Set([
|
|
53
|
+
'type',
|
|
54
|
+
'timeout',
|
|
55
|
+
'connectionTimeout',
|
|
56
|
+
'requestTimeout',
|
|
57
|
+
'url',
|
|
58
|
+
'headers',
|
|
59
|
+
'command',
|
|
60
|
+
'args',
|
|
61
|
+
'cwd',
|
|
62
|
+
'env',
|
|
63
|
+
'restartOnExit',
|
|
64
|
+
'maxRestarts',
|
|
65
|
+
'restartDelay',
|
|
66
|
+
]);
|
|
67
|
+
function normalizeConfiguredServerCreateDraft(value) {
|
|
68
|
+
const parsed = configuredServerCreateDraftSchema.safeParse(value);
|
|
69
|
+
if (!parsed.success) {
|
|
70
|
+
const record = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
71
|
+
const transport = record.transport && typeof record.transport === 'object' && !Array.isArray(record.transport)
|
|
72
|
+
? record.transport
|
|
73
|
+
: {};
|
|
74
|
+
const transportType = transport.type === 'http' || transport.type === 'sse' ? transport.type : 'stdio';
|
|
75
|
+
return {
|
|
76
|
+
draft: {},
|
|
77
|
+
source: {
|
|
78
|
+
type: 'direct',
|
|
79
|
+
localName: typeof record.name === 'string' ? record.name : '',
|
|
80
|
+
transport: transportType,
|
|
81
|
+
},
|
|
82
|
+
validation: {
|
|
83
|
+
status: 'invalid',
|
|
84
|
+
errors: parsed.error.issues.map((issue) => ({
|
|
85
|
+
fieldPath: issue.path.map(String),
|
|
86
|
+
code: 'invalid_create_field',
|
|
87
|
+
message: issue.message,
|
|
88
|
+
})),
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const { transport } = parsed.data;
|
|
93
|
+
const draft = {
|
|
94
|
+
name: parsed.data.name,
|
|
95
|
+
enabled: parsed.data.enabled,
|
|
96
|
+
tags: parsed.data.tags,
|
|
97
|
+
transport: { ...transport },
|
|
98
|
+
secrets: parsed.data.secrets,
|
|
99
|
+
};
|
|
100
|
+
const secretContainer = transport.type === 'stdio' ? 'env' : 'headers';
|
|
101
|
+
const secretValues = configuredServerCreateSecretValues(draft, secretContainer);
|
|
102
|
+
const source = {
|
|
103
|
+
type: 'direct',
|
|
104
|
+
localName: parsed.data.name,
|
|
105
|
+
transport: transport.type,
|
|
106
|
+
enabled: parsed.data.enabled,
|
|
107
|
+
tags: parsed.data.tags,
|
|
108
|
+
command: transport.command,
|
|
109
|
+
url: transport.url,
|
|
110
|
+
args: transport.args,
|
|
111
|
+
cwd: transport.cwd,
|
|
112
|
+
env: mergeConfiguredServerCreateRecord(transport.env, secretContainer === 'env' ? secretValues : undefined),
|
|
113
|
+
headers: mergeConfiguredServerCreateRecord(transport.headers, secretContainer === 'headers' ? secretValues : undefined),
|
|
114
|
+
timeout: transport.timeout,
|
|
115
|
+
connectionTimeout: transport.connectionTimeout,
|
|
116
|
+
requestTimeout: transport.requestTimeout,
|
|
117
|
+
autoRestart: transport.restartOnExit,
|
|
118
|
+
maxRestarts: transport.maxRestarts,
|
|
119
|
+
restartDelay: transport.restartDelay,
|
|
120
|
+
};
|
|
121
|
+
return { draft, source, validation: { status: 'valid', errors: [] } };
|
|
122
|
+
}
|
|
123
|
+
function configuredServerCreateSecretValues(draft, container) {
|
|
124
|
+
const values = {};
|
|
125
|
+
for (const secret of draft.secrets ?? []) {
|
|
126
|
+
if (secret.fieldPath.length !== 2 || secret.fieldPath[0] !== container || !isValidFieldPath(secret.fieldPath)) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (!secret.replacement)
|
|
130
|
+
continue;
|
|
131
|
+
values[secret.fieldPath[1]] = replacementValue(secret.replacement);
|
|
132
|
+
}
|
|
133
|
+
return values;
|
|
134
|
+
}
|
|
135
|
+
function mergeConfiguredServerCreateRecord(base, additions) {
|
|
136
|
+
const merged = { ...base, ...additions };
|
|
137
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
138
|
+
}
|
|
139
|
+
function createWorkflowValidation(workflow, config) {
|
|
140
|
+
const errors = [];
|
|
141
|
+
for (const [field, messages] of Object.entries(workflow.fieldErrors ?? {})) {
|
|
142
|
+
for (const message of messages) {
|
|
143
|
+
errors.push({
|
|
144
|
+
fieldPath: field === 'localName' ? ['name'] : ['transport', field],
|
|
145
|
+
code: 'invalid_create_field',
|
|
146
|
+
message,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (workflow.status === 'exists' || workflow.status === 'template_conflict') {
|
|
151
|
+
errors.push({
|
|
152
|
+
fieldPath: ['name'],
|
|
153
|
+
code: workflow.status === 'template_conflict' ? 'template_name_conflict' : 'configured_server_name_conflict',
|
|
154
|
+
message: 'A configured server or Template Server definition already uses this name.',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (config) {
|
|
158
|
+
const parsed = previewTransportConfigSchema.safeParse(config);
|
|
159
|
+
if (!parsed.success) {
|
|
160
|
+
for (const issue of parsed.error.issues) {
|
|
161
|
+
errors.push({
|
|
162
|
+
fieldPath: ['transport', ...issue.path.map(String)],
|
|
163
|
+
code: 'invalid_transport_config',
|
|
164
|
+
message: issue.message,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { status: errors.length === 0 ? 'valid' : 'invalid', errors };
|
|
170
|
+
}
|
|
171
|
+
function validateConfiguredServerCreateSecrets(draft) {
|
|
172
|
+
const errors = [];
|
|
173
|
+
const expectedContainer = draft.transport?.type === 'stdio' ? 'env' : 'headers';
|
|
174
|
+
for (const [index, secret] of (draft.secrets ?? []).entries()) {
|
|
175
|
+
const basePath = ['secrets', String(index)];
|
|
176
|
+
if (!isValidFieldPath(secret.fieldPath) ||
|
|
177
|
+
secret.fieldPath.length !== 2 ||
|
|
178
|
+
secret.fieldPath[0] !== expectedContainer) {
|
|
179
|
+
errors.push({
|
|
180
|
+
fieldPath: [...basePath, 'fieldPath'],
|
|
181
|
+
code: 'unsupported_secret_field',
|
|
182
|
+
message: `Secret replacement must target ${expectedContainer} for the selected transport.`,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (!secret.replacement) {
|
|
186
|
+
errors.push({
|
|
187
|
+
fieldPath: [...basePath, 'replacement'],
|
|
188
|
+
code: 'missing_secret_replacement',
|
|
189
|
+
message: 'Create secret actions require a replacement.',
|
|
190
|
+
});
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (secret.replacement.kind === 'environmentReference' && !isEnvironmentReferenceInput(secret.replacement.value)) {
|
|
194
|
+
errors.push({
|
|
195
|
+
fieldPath: [...basePath, 'replacement', 'value'],
|
|
196
|
+
code: 'invalid_environment_reference',
|
|
197
|
+
message: 'Environment reference must be an environment variable name or substitution expression.',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (secret.replacement.kind === 'inlineSecret' && secret.replacement.value.length === 0) {
|
|
201
|
+
errors.push({
|
|
202
|
+
fieldPath: [...basePath, 'replacement', 'value'],
|
|
203
|
+
code: 'empty_inline_secret',
|
|
204
|
+
message: 'Inline secret replacement cannot be empty.',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return { status: errors.length === 0 ? 'valid' : 'invalid', errors };
|
|
209
|
+
}
|
|
210
|
+
function createConfiguredServerCreateContract() {
|
|
211
|
+
const transportFields = TRANSPORT_FIELD_DEFINITIONS.filter((definition) => CONFIGURED_SERVER_CREATE_TRANSPORT_FIELD_KEYS.has(definition.key)).map((definition) => ({
|
|
212
|
+
fieldPath: ['transport', definition.key],
|
|
213
|
+
label: definition.label,
|
|
214
|
+
control: definition.control,
|
|
215
|
+
value: definition.defaultValue,
|
|
216
|
+
options: definition.key === 'type' ? ['stdio', 'http', 'sse'] : definition.options,
|
|
217
|
+
editable: true,
|
|
218
|
+
applicableTransportTypes: definition.applicableTransportTypes?.filter((type) => type !== 'streamableHttp'),
|
|
219
|
+
}));
|
|
220
|
+
return {
|
|
221
|
+
schemaVersion: 1,
|
|
222
|
+
capabilities: {
|
|
223
|
+
create: { supported: true },
|
|
224
|
+
forceReplacement: { supported: false },
|
|
225
|
+
rawJson: { supported: false },
|
|
226
|
+
preview: { supported: true },
|
|
227
|
+
apply: { supported: true },
|
|
228
|
+
},
|
|
229
|
+
secretPolicy: {
|
|
230
|
+
allowedActions: ['replace'],
|
|
231
|
+
environmentReference: {
|
|
232
|
+
recommended: true,
|
|
233
|
+
storesSecretMaterial: false,
|
|
234
|
+
guidance: 'Store an environment variable name or substitution expression instead of secret material.',
|
|
235
|
+
},
|
|
236
|
+
inlineReplacement: {
|
|
237
|
+
emphasis: 'secondary',
|
|
238
|
+
guidance: 'Inline secret material is stored in the Runtime Scope configuration.',
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
fieldGroups: [
|
|
242
|
+
{
|
|
243
|
+
id: 'identity',
|
|
244
|
+
label: 'Identity',
|
|
245
|
+
fields: [
|
|
246
|
+
{ fieldPath: ['name'], label: 'Server Name', control: 'text', editable: true },
|
|
247
|
+
{ fieldPath: ['enabled'], label: 'Enabled', control: 'switch', value: true, editable: true },
|
|
248
|
+
{ fieldPath: ['tags'], label: 'Tags', control: 'tag-list', value: [], editable: true },
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
{ id: 'transport', label: 'Transport', fields: transportFields },
|
|
252
|
+
],
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function createConfiguredServerCreateDiff(proposed, draft) {
|
|
256
|
+
const diff = [
|
|
257
|
+
{ fieldPath: ['name'], oldValue: undefined, newValue: proposed.id, riskFlags: [] },
|
|
258
|
+
{ fieldPath: ['enabled'], oldValue: undefined, newValue: proposed.enabled, riskFlags: ['connection_critical'] },
|
|
259
|
+
{ fieldPath: ['tags'], oldValue: undefined, newValue: proposed.tags, riskFlags: [] },
|
|
260
|
+
];
|
|
261
|
+
for (const [key, value] of Object.entries(proposed.transport)) {
|
|
262
|
+
diff.push({
|
|
263
|
+
fieldPath: ['transport', key],
|
|
264
|
+
oldValue: undefined,
|
|
265
|
+
newValue: value,
|
|
266
|
+
riskFlags: previewRiskFlags([key]),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
for (const secret of draft.secrets ?? []) {
|
|
270
|
+
diff.push({
|
|
271
|
+
fieldPath: secret.fieldPath,
|
|
272
|
+
oldValue: undefined,
|
|
273
|
+
newValue: previewSecretNewValue(secret),
|
|
274
|
+
secretAction: 'replace',
|
|
275
|
+
riskFlags: ['connection_critical', 'secret'],
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return diff;
|
|
279
|
+
}
|
|
280
|
+
function previewSecretNewValue(secret) {
|
|
281
|
+
const replacement = secret.replacement;
|
|
282
|
+
if (!replacement)
|
|
283
|
+
return { present: false, value: '[REDACTED]', secret: true };
|
|
284
|
+
if (replacement.kind === 'environmentReference') {
|
|
285
|
+
return {
|
|
286
|
+
kind: 'environmentReference',
|
|
287
|
+
value: normalizeEnvironmentReference(replacement.value),
|
|
288
|
+
storesSecretMaterial: false,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return { present: true, value: '[REDACTED]', secret: true };
|
|
292
|
+
}
|
|
293
|
+
function configuredServerCreatePreviewFingerprint(input) {
|
|
294
|
+
const redactedDraft = {
|
|
295
|
+
...input.draft,
|
|
296
|
+
secrets: input.draft.secrets?.map((secret) => ({
|
|
297
|
+
fieldPath: secret.fieldPath,
|
|
298
|
+
action: secret.action,
|
|
299
|
+
replacement: secret.replacement ? fingerprintSecretReplacement(secret.replacement) : undefined,
|
|
300
|
+
})),
|
|
301
|
+
};
|
|
302
|
+
return `preview_${createHash('sha256')
|
|
303
|
+
.update(stableStringify({
|
|
304
|
+
schemaVersion: 1,
|
|
305
|
+
configFingerprint: input.configFingerprint,
|
|
306
|
+
draft: redactedDraft,
|
|
307
|
+
proposed: input.proposed,
|
|
308
|
+
diff: input.diff,
|
|
309
|
+
validation: input.validation,
|
|
310
|
+
connectivityCheck: connectivityFingerprintFacts(input.connectivityCheck),
|
|
311
|
+
}))
|
|
312
|
+
.digest('hex')}`;
|
|
313
|
+
}
|
|
314
|
+
function connectivityFingerprintFacts(connectivityCheck) {
|
|
315
|
+
if (connectivityCheck.status !== 'passed')
|
|
316
|
+
return connectivityCheck;
|
|
317
|
+
return { status: connectivityCheck.status, mode: connectivityCheck.mode };
|
|
318
|
+
}
|
|
319
|
+
function previewCreateConfigChange(targetName, workflowStatus, changed) {
|
|
320
|
+
const status = workflowStatus === 'template_conflict'
|
|
321
|
+
? 'template_conflict'
|
|
322
|
+
: workflowStatus === 'exists'
|
|
323
|
+
? 'destination_conflict'
|
|
324
|
+
: changed
|
|
325
|
+
? 'changed'
|
|
326
|
+
: 'failed';
|
|
327
|
+
return {
|
|
328
|
+
status,
|
|
329
|
+
operation: 'create_static',
|
|
330
|
+
configPath: '[redacted]',
|
|
331
|
+
target: {
|
|
332
|
+
name: targetName,
|
|
333
|
+
...(workflowStatus === 'template_conflict'
|
|
334
|
+
? { source: 'mcpTemplates' }
|
|
335
|
+
: workflowStatus === 'exists'
|
|
336
|
+
? { source: 'mcpServers' }
|
|
337
|
+
: {}),
|
|
338
|
+
},
|
|
339
|
+
changed,
|
|
340
|
+
backup: { created: false },
|
|
341
|
+
retentionCleanup: { attempted: false, deletedPaths: [], warnings: [] },
|
|
342
|
+
reload: { status: 'skipped' },
|
|
343
|
+
warnings: [],
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function createDraftCandidateName(value) {
|
|
347
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
348
|
+
return '';
|
|
349
|
+
const name = value.name;
|
|
350
|
+
return typeof name === 'string' ? name : '';
|
|
351
|
+
}
|
|
352
|
+
function sanitizeCreateConfirmationFacts(facts) {
|
|
353
|
+
if (!facts)
|
|
354
|
+
return undefined;
|
|
355
|
+
const allowed = [
|
|
356
|
+
'previewConfirmed',
|
|
357
|
+
'targetNameConfirmed',
|
|
358
|
+
'secretChangeConfirmed',
|
|
359
|
+
'connectionCriticalConfirmed',
|
|
360
|
+
'connectivityFailureOverrideConfirmed',
|
|
361
|
+
];
|
|
362
|
+
const sanitized = Object.fromEntries(allowed.filter((key) => key in facts).map((key) => [key, facts[key]]));
|
|
363
|
+
return Object.keys(sanitized).length ? sanitized : undefined;
|
|
364
|
+
}
|
|
365
|
+
function assertSuccessfulConfiguredServerCreate(configChange) {
|
|
366
|
+
if (configChange.status === 'changed')
|
|
367
|
+
return;
|
|
368
|
+
if (configChange.status === 'source_conflict') {
|
|
369
|
+
throw new AdminConfiguredServerApplyError('configured_server_stale_preview');
|
|
370
|
+
}
|
|
371
|
+
if (configChange.status === 'destination_conflict' || configChange.status === 'template_conflict') {
|
|
372
|
+
throw new AdminConfiguredServerApplyError('configured_server_destination_conflict');
|
|
373
|
+
}
|
|
374
|
+
throw new AdminConfiguredServerApplyError('configured_server_create_failed');
|
|
375
|
+
}
|
|
15
376
|
export class AdminConfiguredServerNotFoundError extends Error {
|
|
16
377
|
targetName;
|
|
17
378
|
code = 'configured_server_not_found';
|
|
@@ -26,60 +387,157 @@ export class AdminConfiguredServerService {
|
|
|
26
387
|
constructor(options) {
|
|
27
388
|
this.options = options;
|
|
28
389
|
}
|
|
390
|
+
async getConfiguredServerCreateContract(input) {
|
|
391
|
+
return this.options.operationService.executeReadOnly({
|
|
392
|
+
context: input.context,
|
|
393
|
+
operationName: 'getConfiguredServerCreateContract',
|
|
394
|
+
run: async () => createConfiguredServerCreateContract(),
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
async previewConfiguredServerCreate(input) {
|
|
398
|
+
return this.options.operationService.executeDryRun({
|
|
399
|
+
context: input.context,
|
|
400
|
+
operationName: 'previewConfiguredServerCreate',
|
|
401
|
+
run: async () => this.previewConfiguredServerCreateResult(input),
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
async applyConfiguredServerCreate(input) {
|
|
405
|
+
const candidateName = createDraftCandidateName(input.draft);
|
|
406
|
+
const context = {
|
|
407
|
+
...input.context,
|
|
408
|
+
target: { type: 'configured_server', id: candidateName },
|
|
409
|
+
confirmationFacts: sanitizeCreateConfirmationFacts(input.context.confirmationFacts),
|
|
410
|
+
};
|
|
411
|
+
return this.options.operationService.executeMutation({
|
|
412
|
+
context,
|
|
413
|
+
operationName: 'applyConfiguredServerCreate',
|
|
414
|
+
prepare: async () => {
|
|
415
|
+
const document = this.options.readConfigDocument() ?? {};
|
|
416
|
+
const preview = await this.previewConfiguredServerCreateResult({ ...input, connectivityCheck: 'auto' }, true, document);
|
|
417
|
+
if (preview.previewFingerprint !== input.previewFingerprint) {
|
|
418
|
+
throw new AdminConfiguredServerApplyError('configured_server_stale_preview');
|
|
419
|
+
}
|
|
420
|
+
if (preview.validation.status !== 'valid' || !preview.configChange.changed) {
|
|
421
|
+
throw new AdminConfiguredServerApplyError('configured_server_create_invalid');
|
|
422
|
+
}
|
|
423
|
+
const prepared = await this.resolveConfiguredServerCreate(input.draft, document);
|
|
424
|
+
if (!prepared.config) {
|
|
425
|
+
throw new AdminConfiguredServerApplyError('configured_server_create_invalid');
|
|
426
|
+
}
|
|
427
|
+
if (prepared.enabled &&
|
|
428
|
+
!wouldUseLocalStdioTransport(prepared.config) &&
|
|
429
|
+
preview.connectivityCheck.status !== 'passed' &&
|
|
430
|
+
!(preview.connectivityCheck.status === 'skipped' && preview.connectivityCheck.reason === 'checker_unavailable') &&
|
|
431
|
+
!(preview.connectivityCheck.status === 'failed' &&
|
|
432
|
+
input.context.confirmationFacts?.connectivityFailureOverrideConfirmed === true)) {
|
|
433
|
+
throw new AdminConfiguredServerApplyError('configured_server_connectivity_blocked');
|
|
434
|
+
}
|
|
435
|
+
const secretChange = prepared.draft.secrets?.length;
|
|
436
|
+
const configFingerprint = fingerprintConfiguredServerConfigDocument(document);
|
|
437
|
+
return {
|
|
438
|
+
confirmationRequirements: [
|
|
439
|
+
{ code: 'previewConfirmed', expected: input.previewFingerprint },
|
|
440
|
+
{ code: 'targetNameConfirmed', expected: prepared.targetName },
|
|
441
|
+
{ code: 'connectionCriticalConfirmed', expected: true },
|
|
442
|
+
...(secretChange ? [{ code: 'secretChangeConfirmed', expected: true }] : []),
|
|
443
|
+
...(preview.connectivityCheck.status === 'failed'
|
|
444
|
+
? [{ code: 'connectivityFailureOverrideConfirmed', expected: true }]
|
|
445
|
+
: []),
|
|
446
|
+
],
|
|
447
|
+
run: async () => {
|
|
448
|
+
let configChange;
|
|
449
|
+
try {
|
|
450
|
+
configChange = await this.options.configChangeService.createStaticConfiguredServerTarget({
|
|
451
|
+
targetName: prepared.targetName,
|
|
452
|
+
serverConfig: prepared.config,
|
|
453
|
+
operation: 'install',
|
|
454
|
+
backup: 'skip',
|
|
455
|
+
expectedConfigFingerprint: configFingerprint,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
throw new AdminConfiguredServerApplyError('configured_server_create_failed');
|
|
460
|
+
}
|
|
461
|
+
assertSuccessfulConfiguredServerCreate(configChange);
|
|
462
|
+
return {
|
|
463
|
+
targetName: prepared.targetName,
|
|
464
|
+
previewFingerprint: preview.previewFingerprint,
|
|
465
|
+
configChange,
|
|
466
|
+
};
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
}
|
|
29
472
|
async listConfiguredServers(input) {
|
|
30
473
|
return this.options.operationService.executeReadOnly({
|
|
31
474
|
context: input.context,
|
|
32
475
|
operationName: 'listConfiguredServers',
|
|
33
|
-
run: async () =>
|
|
476
|
+
run: async () => this.readConfiguredServers(),
|
|
34
477
|
});
|
|
35
478
|
}
|
|
36
479
|
async getConfiguredServerDetail(input) {
|
|
480
|
+
const configuredState = this.readConfiguredServerState(input.targetName, input.targetSource);
|
|
481
|
+
const source = configuredState.source;
|
|
37
482
|
const context = {
|
|
38
483
|
...input.context,
|
|
39
|
-
target: { type: 'configured_server', id: input.targetName },
|
|
484
|
+
target: { type: 'configured_server', id: `${source}:${input.targetName}` },
|
|
40
485
|
};
|
|
41
486
|
return this.options.operationService.executeReadOnly({
|
|
42
487
|
context,
|
|
43
488
|
operationName: 'getConfiguredServerDetail',
|
|
44
489
|
run: async () => {
|
|
45
|
-
const { currentConfig, serverDefaults } =
|
|
46
|
-
const server = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig));
|
|
490
|
+
const { currentConfig, serverDefaults } = configuredState;
|
|
491
|
+
const server = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig), source, fingerprintConfiguredServerTarget(currentConfig), Object.hasOwn(currentConfig, 'instructionOverride') ? currentConfig.instructionOverride : undefined, Object.hasOwn(currentConfig, 'instructionOverride'));
|
|
47
492
|
return {
|
|
48
493
|
server,
|
|
49
494
|
editContract: createConfiguredServerEditContract(server, currentConfig, serverDefaults),
|
|
495
|
+
...(this.options.readToolInventory
|
|
496
|
+
? {
|
|
497
|
+
toolInventory: await this.options.readToolInventory({
|
|
498
|
+
targetName: input.targetName,
|
|
499
|
+
source,
|
|
500
|
+
config: currentConfig,
|
|
501
|
+
...(input.model ? { model: input.model } : {}),
|
|
502
|
+
}),
|
|
503
|
+
}
|
|
504
|
+
: {}),
|
|
50
505
|
};
|
|
51
506
|
},
|
|
52
507
|
});
|
|
53
508
|
}
|
|
54
509
|
async previewConfiguredServerEdit(input) {
|
|
510
|
+
const configuredState = this.readConfiguredServerState(input.targetName, input.targetSource);
|
|
511
|
+
const source = configuredState.source;
|
|
55
512
|
const context = {
|
|
56
513
|
...input.context,
|
|
57
|
-
target: { type: 'configured_server', id: input.targetName },
|
|
514
|
+
target: { type: 'configured_server', id: `${source}:${input.targetName}` },
|
|
58
515
|
};
|
|
59
|
-
const { currentConfig, serverDefaults } =
|
|
516
|
+
const { currentConfig, serverDefaults } = configuredState;
|
|
60
517
|
return this.options.operationService.executeDryRun({
|
|
61
518
|
context,
|
|
62
519
|
operationName: 'previewConfiguredServerEdit',
|
|
63
|
-
run: async () => this.previewConfiguredServerEditResult(input, currentConfig, serverDefaults),
|
|
520
|
+
run: async () => this.previewConfiguredServerEditResult(input, currentConfig, serverDefaults, false, source),
|
|
64
521
|
});
|
|
65
522
|
}
|
|
66
523
|
async applyConfiguredServerEdit(input) {
|
|
524
|
+
const source = this.resolveConfiguredServerSource(input.targetName, input.targetSource);
|
|
67
525
|
const context = {
|
|
68
526
|
...input.context,
|
|
69
|
-
target: { type: 'configured_server', id: input.targetName },
|
|
527
|
+
target: { type: 'configured_server', id: `${source}:${input.targetName}` },
|
|
70
528
|
confirmationFacts: sanitizeApplyConfirmationFacts(input.context.confirmationFacts),
|
|
71
529
|
};
|
|
72
530
|
return this.options.operationService.executeMutation({
|
|
73
531
|
context,
|
|
74
532
|
operationName: 'applyConfiguredServerEdit',
|
|
75
533
|
prepare: async () => {
|
|
76
|
-
const { currentConfig, serverDefaults } = this.readConfiguredServerState(input.targetName);
|
|
77
|
-
const preview = await this.previewConfiguredServerEditResult({ ...input, connectivityCheck: 'auto' }, currentConfig, serverDefaults, true);
|
|
534
|
+
const { currentConfig, serverDefaults } = this.readConfiguredServerState(input.targetName, source);
|
|
535
|
+
const preview = await this.previewConfiguredServerEditResult({ ...input, connectivityCheck: 'auto' }, currentConfig, serverDefaults, true, source);
|
|
78
536
|
const normalizedEdit = normalizeEditDraft(input.edit);
|
|
79
|
-
const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig));
|
|
537
|
+
const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig), source);
|
|
80
538
|
const applicableEdit = filterApplicableSecretEdits(normalizedEdit.edit, currentReadModel.secretInputs);
|
|
81
|
-
const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults);
|
|
82
|
-
const proposedReadModel = createConfiguredServerReadModel(preview.proposedTargetName, mergeGlobalAndServerConfig(serverDefaults, proposedConfig));
|
|
539
|
+
const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults, input.targetName);
|
|
540
|
+
const proposedReadModel = createConfiguredServerReadModel(preview.proposedTargetName, mergeGlobalAndServerConfig(serverDefaults, proposedConfig), source);
|
|
83
541
|
const connectionCritical = preview.diff.some((entry) => entry.riskFlags.includes('connection_critical'));
|
|
84
542
|
const secretChange = preview.diff.some((entry) => entry.riskFlags.includes('secret'));
|
|
85
543
|
if (preview.validation.status !== 'valid') {
|
|
@@ -107,6 +565,9 @@ export class AdminConfiguredServerService {
|
|
|
107
565
|
: []),
|
|
108
566
|
...(secretChange ? [{ code: 'secretChangeConfirmed', expected: true }] : []),
|
|
109
567
|
...(connectionCritical ? [{ code: 'connectionCriticalConfirmed', expected: true }] : []),
|
|
568
|
+
...(preview.toolSelection?.requiresZeroEnabledConfirmation
|
|
569
|
+
? [{ code: 'zeroEnabledToolsConfirmed', expected: true }]
|
|
570
|
+
: []),
|
|
110
571
|
...(preview.connectivityCheck.status === 'failed'
|
|
111
572
|
? [{ code: 'connectivityFailureOverrideConfirmed', expected: true }]
|
|
112
573
|
: []),
|
|
@@ -114,6 +575,7 @@ export class AdminConfiguredServerService {
|
|
|
114
575
|
run: async () => {
|
|
115
576
|
const configChange = await this.options.configChangeService.editConfiguredServerTarget({
|
|
116
577
|
sourceName: input.targetName,
|
|
578
|
+
targetSource: source,
|
|
117
579
|
targetName: preview.proposedTargetName,
|
|
118
580
|
serverConfig: proposedConfig,
|
|
119
581
|
expectedSourceFingerprint: fingerprintConfiguredServerTarget(currentConfig),
|
|
@@ -142,17 +604,22 @@ export class AdminConfiguredServerService {
|
|
|
142
604
|
}
|
|
143
605
|
async setConfiguredServerEnabledState(input, enabled) {
|
|
144
606
|
const operationName = enabled ? 'enableConfiguredServer' : 'disableConfiguredServer';
|
|
607
|
+
const source = input.targetSource ?? 'mcpServers';
|
|
145
608
|
const context = {
|
|
146
609
|
...input.context,
|
|
147
|
-
target: { type: 'configured_server', id: input.targetName },
|
|
610
|
+
target: { type: 'configured_server', id: `${source}:${input.targetName}` },
|
|
148
611
|
};
|
|
149
612
|
if (input.dryRun) {
|
|
150
613
|
return this.options.operationService.executeDryRun({
|
|
151
614
|
context,
|
|
152
615
|
operationName,
|
|
153
616
|
run: async () => {
|
|
617
|
+
if (source !== 'mcpServers') {
|
|
618
|
+
throw new Error('Template Server definitions do not support enable or disable operations');
|
|
619
|
+
}
|
|
154
620
|
const configChange = await this.options.configChangeService.previewConfiguredServerTargetEnabledState({
|
|
155
621
|
targetName: input.targetName,
|
|
622
|
+
targetSource: source,
|
|
156
623
|
enabled,
|
|
157
624
|
backup: 'skip',
|
|
158
625
|
});
|
|
@@ -172,8 +639,12 @@ export class AdminConfiguredServerService {
|
|
|
172
639
|
operationName,
|
|
173
640
|
confirmationRequirements: input.confirmationRequirements,
|
|
174
641
|
run: async () => {
|
|
642
|
+
if (source !== 'mcpServers') {
|
|
643
|
+
throw new Error('Template Server definitions do not support enable or disable operations');
|
|
644
|
+
}
|
|
175
645
|
const configChange = await this.options.configChangeService.setConfiguredServerTargetEnabledState({
|
|
176
646
|
targetName: input.targetName,
|
|
647
|
+
targetSource: source,
|
|
177
648
|
enabled,
|
|
178
649
|
backup: 'required',
|
|
179
650
|
});
|
|
@@ -190,49 +661,84 @@ export class AdminConfiguredServerService {
|
|
|
190
661
|
readConfiguredServers() {
|
|
191
662
|
const parsed = this.options.readConfigDocument();
|
|
192
663
|
if (!parsed) {
|
|
193
|
-
return [];
|
|
664
|
+
return { servers: [], configFingerprint: fingerprintConfiguredServerConfigDocument({}) };
|
|
194
665
|
}
|
|
195
|
-
return
|
|
666
|
+
return {
|
|
667
|
+
configFingerprint: fingerprintConfiguredServerConfigDocument(parsed),
|
|
668
|
+
servers: ['mcpServers', 'mcpTemplates'].flatMap((source) => Object.entries(parsed[source] ?? {}).map(([name, serverConfig]) => createConfiguredServerReadModel(name, mergeGlobalAndServerConfig(parsed.serverDefaults, serverConfig), source, fingerprintConfiguredServerTarget(serverConfig), Object.hasOwn(serverConfig, 'instructionOverride') ? serverConfig.instructionOverride : undefined, Object.hasOwn(serverConfig, 'instructionOverride')))),
|
|
669
|
+
};
|
|
196
670
|
}
|
|
197
|
-
readConfiguredServerState(targetName) {
|
|
671
|
+
readConfiguredServerState(targetName, targetSource) {
|
|
198
672
|
const parsed = this.options.readConfigDocument();
|
|
199
|
-
const
|
|
673
|
+
const source = this.resolveConfiguredServerSource(targetName, targetSource, parsed);
|
|
674
|
+
const currentConfig = parsed?.[source]?.[targetName];
|
|
200
675
|
if (!currentConfig) {
|
|
201
676
|
throw new AdminConfiguredServerNotFoundError(targetName);
|
|
202
677
|
}
|
|
203
|
-
return { currentConfig, serverDefaults: parsed?.serverDefaults };
|
|
678
|
+
return { currentConfig, serverDefaults: parsed?.serverDefaults, source };
|
|
679
|
+
}
|
|
680
|
+
resolveConfiguredServerSource(targetName, targetSource, document = this.options.readConfigDocument()) {
|
|
681
|
+
if (targetSource)
|
|
682
|
+
return targetSource;
|
|
683
|
+
return document?.mcpTemplates?.[targetName] ? 'mcpTemplates' : 'mcpServers';
|
|
204
684
|
}
|
|
205
685
|
readConfiguredServerConfig(targetName) {
|
|
206
686
|
const parsed = this.options.readConfigDocument();
|
|
207
|
-
const currentConfig = parsed?.mcpServers?.[targetName];
|
|
687
|
+
const currentConfig = parsed?.mcpTemplates?.[targetName] ?? parsed?.mcpServers?.[targetName];
|
|
208
688
|
if (!currentConfig) {
|
|
209
689
|
throw new AdminConfiguredServerNotFoundError(targetName);
|
|
210
690
|
}
|
|
211
691
|
return currentConfig;
|
|
212
692
|
}
|
|
213
|
-
async previewConfiguredServerEditResult(input, currentConfig, serverDefaults, requireCriticalConnectivity = false) {
|
|
693
|
+
async previewConfiguredServerEditResult(input, currentConfig, serverDefaults, requireCriticalConnectivity = false, source = 'mcpServers') {
|
|
214
694
|
const normalizedEdit = normalizeEditDraft(input.edit);
|
|
215
|
-
const
|
|
695
|
+
const sourceValidation = validateEditForSource(source, normalizedEdit.edit);
|
|
696
|
+
const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig), source);
|
|
216
697
|
const secretValidation = validateSecretEditCapabilities(normalizedEdit.edit, currentReadModel.secretInputs);
|
|
217
698
|
const transportApplicabilityValidation = validateTransportFieldApplicability(currentConfig, normalizedEdit.edit);
|
|
218
699
|
const overrideValidation = validateTransportOverrideClears(currentConfig, normalizedEdit.edit);
|
|
219
700
|
const applicableEdit = filterApplicableSecretEdits(normalizedEdit.edit, currentReadModel.secretInputs);
|
|
220
701
|
const proposedTargetName = applicableEdit.id?.trim() || input.targetName;
|
|
221
|
-
const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults);
|
|
222
|
-
const proposedReadModel = createConfiguredServerReadModel(proposedTargetName, mergeGlobalAndServerConfig(serverDefaults, proposedConfig));
|
|
702
|
+
const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults, input.targetName);
|
|
703
|
+
const proposedReadModel = createConfiguredServerReadModel(proposedTargetName, mergeGlobalAndServerConfig(serverDefaults, proposedConfig), source);
|
|
223
704
|
const proposedTransportType = configuredTransportType(proposedConfig);
|
|
224
705
|
const serverValidation = validatePreviewServerConfig(proposedConfig);
|
|
225
|
-
const
|
|
706
|
+
const currentToolInventory = this.options.readToolInventory
|
|
707
|
+
? await this.options.readToolInventory({
|
|
708
|
+
targetName: input.targetName,
|
|
709
|
+
source,
|
|
710
|
+
config: currentConfig,
|
|
711
|
+
...(input.model ? { model: input.model } : {}),
|
|
712
|
+
})
|
|
713
|
+
: undefined;
|
|
714
|
+
const toolEditValidation = validateConfiguredToolEdit(applicableEdit, currentToolInventory);
|
|
715
|
+
const validation = mergeValidation(mergeValidation(mergeValidation(mergeValidation(normalizedEdit.validation, secretValidation), transportApplicabilityValidation), overrideValidation), mergeValidation(mergeValidation(serverValidation, sourceValidation), toolEditValidation));
|
|
226
716
|
const diff = createPreviewDiff(currentReadModel, proposedReadModel, applicableEdit);
|
|
717
|
+
pushDiff(diff, ['disabledTools'], currentConfig.disabledTools ?? [], proposedConfig.disabledTools ?? [], [
|
|
718
|
+
'tool_visibility',
|
|
719
|
+
]);
|
|
720
|
+
pushDiff(diff, ['toolDescriptionOverrides'], currentConfig.toolDescriptionOverrides ?? {}, proposedConfig.toolDescriptionOverrides ?? {}, ['tool_metadata']);
|
|
227
721
|
appendClearedOverrideDiff(diff, currentReadModel, proposedReadModel, applicableEdit);
|
|
228
722
|
const changed = validation.status === 'valid' && diff.length > 0;
|
|
229
723
|
const endpointChangedWithPreservedSecrets = endpointAuthorityChanged(currentConfig, proposedConfig) &&
|
|
230
724
|
hasPreservedSecretInputs(currentReadModel.secretInputs.filter((input) => !proposedTransportType || transportFieldAppliesToType(input.fieldPath[0], proposedTransportType)), applicableEdit);
|
|
725
|
+
const proposedToolInventory = this.options.readToolInventory
|
|
726
|
+
? await this.options.readToolInventory({
|
|
727
|
+
targetName: proposedTargetName,
|
|
728
|
+
source,
|
|
729
|
+
config: proposedConfig,
|
|
730
|
+
...(input.model ? { model: input.model } : {}),
|
|
731
|
+
})
|
|
732
|
+
: undefined;
|
|
733
|
+
const toolSelection = currentToolInventory && proposedToolInventory
|
|
734
|
+
? createConfiguredToolSelectionPreview(currentToolInventory, proposedToolInventory)
|
|
735
|
+
: undefined;
|
|
231
736
|
return {
|
|
232
737
|
targetName: input.targetName,
|
|
233
738
|
proposedTargetName,
|
|
234
739
|
previewFingerprint: previewFingerprint({
|
|
235
740
|
targetName: input.targetName,
|
|
741
|
+
targetSource: source,
|
|
236
742
|
sourceFingerprint: fingerprintConfiguredServerTarget(currentConfig),
|
|
237
743
|
globalConfigFingerprint: fingerprintConfiguredServerDefaults(serverDefaults),
|
|
238
744
|
edit: normalizedEdit.edit,
|
|
@@ -240,10 +746,11 @@ export class AdminConfiguredServerService {
|
|
|
240
746
|
proposed: proposedReadModel,
|
|
241
747
|
diff,
|
|
242
748
|
validation,
|
|
749
|
+
toolSelection,
|
|
243
750
|
}),
|
|
244
751
|
validation,
|
|
245
752
|
diff,
|
|
246
|
-
configChange: previewConfigChange(input.targetName, changed),
|
|
753
|
+
configChange: previewConfigChange(input.targetName, changed, source),
|
|
247
754
|
connectivityCheck: await this.previewConnectivityCheck({
|
|
248
755
|
targetName: proposedTargetName,
|
|
249
756
|
serverConfig: proposedConfig,
|
|
@@ -254,6 +761,7 @@ export class AdminConfiguredServerService {
|
|
|
254
761
|
(requireCriticalConnectivity && diff.some((entry) => entry.riskFlags.includes('connection_critical'))),
|
|
255
762
|
endpointChangedWithPreservedSecrets,
|
|
256
763
|
}),
|
|
764
|
+
...(toolSelection ? { toolSelection } : {}),
|
|
257
765
|
};
|
|
258
766
|
}
|
|
259
767
|
async previewConnectivityCheck(input) {
|
|
@@ -280,6 +788,84 @@ export class AdminConfiguredServerService {
|
|
|
280
788
|
serverConfig: input.serverConfig,
|
|
281
789
|
});
|
|
282
790
|
}
|
|
791
|
+
async previewConfiguredServerCreateResult(input, forceConnectivity = false, document = this.options.readConfigDocument() ?? {}) {
|
|
792
|
+
const prepared = await this.resolveConfiguredServerCreate(input.draft, document);
|
|
793
|
+
const proposedReadModel = prepared.config
|
|
794
|
+
? createConfiguredServerReadModel(prepared.targetName, mergeGlobalAndServerConfig(document.serverDefaults, prepared.config))
|
|
795
|
+
: undefined;
|
|
796
|
+
const validation = prepared.validation;
|
|
797
|
+
const diff = proposedReadModel ? createConfiguredServerCreateDiff(proposedReadModel, prepared.draft) : [];
|
|
798
|
+
const changed = validation.status === 'valid' && prepared.workflow.status === 'preview';
|
|
799
|
+
const connectivityCheck = prepared.config
|
|
800
|
+
? await this.previewConnectivityCheck({
|
|
801
|
+
targetName: prepared.targetName,
|
|
802
|
+
serverConfig: prepared.config,
|
|
803
|
+
enabled: prepared.enabled,
|
|
804
|
+
validationStatus: validation.status,
|
|
805
|
+
connectionCriticalChanged: true,
|
|
806
|
+
force: forceConnectivity || input.connectivityCheck === 'manual',
|
|
807
|
+
endpointChangedWithPreservedSecrets: false,
|
|
808
|
+
})
|
|
809
|
+
: { status: 'skipped', reason: 'validation_failed' };
|
|
810
|
+
const preview = {
|
|
811
|
+
targetName: prepared.targetName,
|
|
812
|
+
previewFingerprint: configuredServerCreatePreviewFingerprint({
|
|
813
|
+
configFingerprint: fingerprintConfiguredServerConfigDocument(document),
|
|
814
|
+
draft: prepared.draft,
|
|
815
|
+
proposed: proposedReadModel,
|
|
816
|
+
diff,
|
|
817
|
+
validation,
|
|
818
|
+
connectivityCheck,
|
|
819
|
+
}),
|
|
820
|
+
validation,
|
|
821
|
+
diff,
|
|
822
|
+
configChange: previewCreateConfigChange(prepared.targetName, prepared.workflow.status, changed),
|
|
823
|
+
connectivityCheck,
|
|
824
|
+
expectedReload: {
|
|
825
|
+
policy: 'observe_after_write',
|
|
826
|
+
possibleStatuses: ['observed', 'runtime_not_running', 'reload_disabled', 'failed'],
|
|
827
|
+
},
|
|
828
|
+
};
|
|
829
|
+
return preview;
|
|
830
|
+
}
|
|
831
|
+
async resolveConfiguredServerCreate(draftValue, document = this.options.readConfigDocument() ?? {}) {
|
|
832
|
+
const normalized = normalizeConfiguredServerCreateDraft(draftValue);
|
|
833
|
+
const workflow = createServerInstallationWorkflow({
|
|
834
|
+
findConfiguredTarget: (targetName) => {
|
|
835
|
+
if (document.mcpTemplates?.[targetName])
|
|
836
|
+
return { name: targetName, source: 'mcpTemplates' };
|
|
837
|
+
if (document.mcpServers?.[targetName])
|
|
838
|
+
return { name: targetName, source: 'mcpServers' };
|
|
839
|
+
return null;
|
|
840
|
+
},
|
|
841
|
+
});
|
|
842
|
+
const workflowResult = await workflow.run({ mode: 'preview', source: normalized.source });
|
|
843
|
+
const config = workflowResult.config;
|
|
844
|
+
const validation = mergeValidation(mergeValidation(normalized.validation, validateConfiguredServerCreateSecrets(normalized.draft)), createWorkflowValidation(workflowResult, config));
|
|
845
|
+
return {
|
|
846
|
+
draft: normalized.draft,
|
|
847
|
+
source: normalized.source,
|
|
848
|
+
targetName: normalized.source.localName,
|
|
849
|
+
enabled: normalized.source.enabled !== false,
|
|
850
|
+
workflow: workflowResult,
|
|
851
|
+
config,
|
|
852
|
+
validation,
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function validateEditForSource(source, edit) {
|
|
857
|
+
if (source !== 'mcpTemplates')
|
|
858
|
+
return { status: 'valid', errors: [] };
|
|
859
|
+
const supportedTemplateMetadata = new Set(['instructionOverride', 'disabledTools', 'toolDescriptionOverrides']);
|
|
860
|
+
const unsupported = Object.keys(edit).filter((key) => !supportedTemplateMetadata.has(key));
|
|
861
|
+
return {
|
|
862
|
+
status: unsupported.length === 0 ? 'valid' : 'invalid',
|
|
863
|
+
errors: unsupported.map((key) => ({
|
|
864
|
+
fieldPath: [key],
|
|
865
|
+
code: 'template_edit_field_unsupported',
|
|
866
|
+
message: 'Template Server edits may only change instruction and tool metadata.',
|
|
867
|
+
})),
|
|
868
|
+
};
|
|
283
869
|
}
|
|
284
870
|
function assertSuccessfulConfigChange(configChange) {
|
|
285
871
|
if (configChange.reload.status === 'failed') {
|
|
@@ -325,6 +911,7 @@ function sanitizeApplyConfirmationFacts(facts) {
|
|
|
325
911
|
return undefined;
|
|
326
912
|
const allowed = ['previewConfirmed', 'targetNameConfirmed', 'secretChangeConfirmed', 'connectionCriticalConfirmed'];
|
|
327
913
|
allowed.push('connectivityFailureOverrideConfirmed');
|
|
914
|
+
allowed.push('zeroEnabledToolsConfirmed');
|
|
328
915
|
const sanitized = Object.fromEntries(allowed.filter((key) => key in facts).map((key) => [key, facts[key]]));
|
|
329
916
|
return Object.keys(sanitized).length > 0 ? sanitized : undefined;
|
|
330
917
|
}
|
|
@@ -337,7 +924,17 @@ function mutationOutcome(enabled, changed) {
|
|
|
337
924
|
function normalizeEditDraft(value) {
|
|
338
925
|
const errors = [];
|
|
339
926
|
const edit = {};
|
|
340
|
-
const supportedKeys = new Set([
|
|
927
|
+
const supportedKeys = new Set([
|
|
928
|
+
'id',
|
|
929
|
+
'enabled',
|
|
930
|
+
'tags',
|
|
931
|
+
'transport',
|
|
932
|
+
'secrets',
|
|
933
|
+
'clearTransportOverrides',
|
|
934
|
+
'instructionOverride',
|
|
935
|
+
'disabledTools',
|
|
936
|
+
'toolDescriptionOverrides',
|
|
937
|
+
]);
|
|
341
938
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
342
939
|
return {
|
|
343
940
|
edit,
|
|
@@ -399,6 +996,36 @@ function normalizeEditDraft(value) {
|
|
|
399
996
|
});
|
|
400
997
|
}
|
|
401
998
|
}
|
|
999
|
+
if (record.disabledTools !== undefined) {
|
|
1000
|
+
if (Array.isArray(record.disabledTools) && record.disabledTools.every((name) => typeof name === 'string')) {
|
|
1001
|
+
edit.disabledTools = Array.from(new Set(record.disabledTools.map((name) => name.trim()).filter((name) => name.length > 0))).sort((left, right) => left.localeCompare(right));
|
|
1002
|
+
}
|
|
1003
|
+
else {
|
|
1004
|
+
errors.push({
|
|
1005
|
+
fieldPath: ['disabledTools'],
|
|
1006
|
+
code: 'invalid_disabled_tools',
|
|
1007
|
+
message: 'Disabled tools must be a list of logical tool names.',
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (record.toolDescriptionOverrides !== undefined) {
|
|
1012
|
+
if (record.toolDescriptionOverrides &&
|
|
1013
|
+
typeof record.toolDescriptionOverrides === 'object' &&
|
|
1014
|
+
!Array.isArray(record.toolDescriptionOverrides) &&
|
|
1015
|
+
Object.entries(record.toolDescriptionOverrides).every(([name, description]) => name.length > 0 && name === name.trim() && typeof description === 'string')) {
|
|
1016
|
+
edit.toolDescriptionOverrides = Object.fromEntries(Object.entries(record.toolDescriptionOverrides)
|
|
1017
|
+
.map(([name, description]) => [name, description.trim()])
|
|
1018
|
+
.filter(([name, description]) => name.length > 0 && description.length > 0)
|
|
1019
|
+
.sort(([left], [right]) => left.localeCompare(right)));
|
|
1020
|
+
}
|
|
1021
|
+
else {
|
|
1022
|
+
errors.push({
|
|
1023
|
+
fieldPath: ['toolDescriptionOverrides'],
|
|
1024
|
+
code: 'invalid_tool_description_overrides',
|
|
1025
|
+
message: 'Tool description overrides must map logical tool names to descriptions.',
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
402
1029
|
if (record.transport !== undefined) {
|
|
403
1030
|
if (record.transport && typeof record.transport === 'object' && !Array.isArray(record.transport)) {
|
|
404
1031
|
edit.transport = normalizeTransportEditDraft(record.transport, errors);
|
|
@@ -437,6 +1064,23 @@ function normalizeEditDraft(value) {
|
|
|
437
1064
|
});
|
|
438
1065
|
}
|
|
439
1066
|
}
|
|
1067
|
+
if (record.instructionOverride !== undefined) {
|
|
1068
|
+
const parsed = z
|
|
1069
|
+
.discriminatedUnion('action', [
|
|
1070
|
+
z.object({ action: z.literal('set'), value: z.string() }).strict(),
|
|
1071
|
+
z.object({ action: z.literal('remove') }).strict(),
|
|
1072
|
+
])
|
|
1073
|
+
.safeParse(record.instructionOverride);
|
|
1074
|
+
if (parsed.success)
|
|
1075
|
+
edit.instructionOverride = parsed.data;
|
|
1076
|
+
else {
|
|
1077
|
+
errors.push({
|
|
1078
|
+
fieldPath: ['instructionOverride'],
|
|
1079
|
+
code: 'invalid_instruction_override',
|
|
1080
|
+
message: 'Instruction override must set a string value or remove the configured override.',
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
440
1084
|
return {
|
|
441
1085
|
edit,
|
|
442
1086
|
validation: { status: errors.length === 0 ? 'valid' : 'invalid', errors },
|
|
@@ -465,6 +1109,20 @@ function normalizeTransportEditDraft(record, errors) {
|
|
|
465
1109
|
}
|
|
466
1110
|
return transport;
|
|
467
1111
|
}
|
|
1112
|
+
function validateConfiguredToolEdit(edit, inventory) {
|
|
1113
|
+
if (!inventory)
|
|
1114
|
+
return { status: 'valid', errors: [] };
|
|
1115
|
+
const allowedNames = new Set(inventory.rows.map((row) => row.name));
|
|
1116
|
+
const requestedNames = new Set([...(edit.disabledTools ?? []), ...Object.keys(edit.toolDescriptionOverrides ?? {})]);
|
|
1117
|
+
const errors = Array.from(requestedNames)
|
|
1118
|
+
.filter((name) => !allowedNames.has(name))
|
|
1119
|
+
.map((name) => ({
|
|
1120
|
+
fieldPath: ['tools', name],
|
|
1121
|
+
code: 'unknown_configured_tool',
|
|
1122
|
+
message: 'Tool changes must refer to an observed tool or a retained unresolved entry.',
|
|
1123
|
+
}));
|
|
1124
|
+
return { status: errors.length === 0 ? 'valid' : 'invalid', errors };
|
|
1125
|
+
}
|
|
468
1126
|
function isSecretCapableRawTransportEdit(key, value) {
|
|
469
1127
|
if (key === 'url') {
|
|
470
1128
|
return isSecretCapableUrlEdit(value);
|
|
@@ -761,7 +1419,7 @@ function potentialSecretInput(fieldPath) {
|
|
|
761
1419
|
}
|
|
762
1420
|
return undefined;
|
|
763
1421
|
}
|
|
764
|
-
function applyEditDraft(currentConfig, edit, serverDefaults) {
|
|
1422
|
+
function applyEditDraft(currentConfig, edit, serverDefaults, logicalServerName) {
|
|
765
1423
|
const nextConfig = cloneServerConfig(currentConfig);
|
|
766
1424
|
for (const key of edit.clearTransportOverrides ?? []) {
|
|
767
1425
|
delete nextConfig[key];
|
|
@@ -777,6 +1435,27 @@ function applyEditDraft(currentConfig, edit, serverDefaults) {
|
|
|
777
1435
|
if (Array.isArray(edit.tags)) {
|
|
778
1436
|
nextConfig.tags = edit.tags.filter((tag) => typeof tag === 'string');
|
|
779
1437
|
}
|
|
1438
|
+
if (edit.instructionOverride?.action === 'set') {
|
|
1439
|
+
nextConfig.instructionOverride = edit.instructionOverride.value;
|
|
1440
|
+
}
|
|
1441
|
+
else if (edit.instructionOverride?.action === 'remove') {
|
|
1442
|
+
delete nextConfig.instructionOverride;
|
|
1443
|
+
}
|
|
1444
|
+
if (Array.isArray(edit.disabledTools)) {
|
|
1445
|
+
const disabledTools = logicalServerName
|
|
1446
|
+
? normalizeDisabledToolsForServer(logicalServerName, edit.disabledTools)
|
|
1447
|
+
: edit.disabledTools;
|
|
1448
|
+
if (disabledTools.length === 0)
|
|
1449
|
+
delete nextConfig.disabledTools;
|
|
1450
|
+
else
|
|
1451
|
+
nextConfig.disabledTools = disabledTools;
|
|
1452
|
+
}
|
|
1453
|
+
if (edit.toolDescriptionOverrides) {
|
|
1454
|
+
if (Object.keys(edit.toolDescriptionOverrides).length === 0)
|
|
1455
|
+
delete nextConfig.toolDescriptionOverrides;
|
|
1456
|
+
else
|
|
1457
|
+
nextConfig.toolDescriptionOverrides = { ...edit.toolDescriptionOverrides };
|
|
1458
|
+
}
|
|
780
1459
|
if (edit.transport && typeof edit.transport === 'object') {
|
|
781
1460
|
const currentType = configuredTransportType(currentConfig);
|
|
782
1461
|
const proposedType = configuredTransportType({ ...currentConfig, ...edit.transport });
|
|
@@ -1160,12 +1839,12 @@ function isValidUrlOrEnvReference(value) {
|
|
|
1160
1839
|
return false;
|
|
1161
1840
|
}
|
|
1162
1841
|
}
|
|
1163
|
-
function previewConfigChange(targetName, changed) {
|
|
1842
|
+
function previewConfigChange(targetName, changed, source = 'mcpServers') {
|
|
1164
1843
|
return {
|
|
1165
1844
|
status: changed ? 'changed' : 'unchanged',
|
|
1166
1845
|
operation: 'set_static',
|
|
1167
1846
|
configPath: '[redacted]',
|
|
1168
|
-
target: { name: targetName, source
|
|
1847
|
+
target: { name: targetName, source },
|
|
1169
1848
|
changed,
|
|
1170
1849
|
backup: { created: false },
|
|
1171
1850
|
retentionCleanup: { attempted: false, deletedPaths: [], warnings: [] },
|
|
@@ -1178,6 +1857,7 @@ function previewFingerprint(input) {
|
|
|
1178
1857
|
.update(stableStringify({
|
|
1179
1858
|
schemaVersion: 2,
|
|
1180
1859
|
targetName: input.targetName,
|
|
1860
|
+
targetSource: input.targetSource,
|
|
1181
1861
|
sourceFingerprint: input.sourceFingerprint,
|
|
1182
1862
|
globalConfigFingerprint: input.globalConfigFingerprint,
|
|
1183
1863
|
edit: redactEditDraftForFingerprint(input.edit),
|
|
@@ -1185,9 +1865,39 @@ function previewFingerprint(input) {
|
|
|
1185
1865
|
proposed: input.proposed,
|
|
1186
1866
|
diff: input.diff,
|
|
1187
1867
|
validation: input.validation,
|
|
1868
|
+
toolSelection: input.toolSelection,
|
|
1188
1869
|
}))
|
|
1189
1870
|
.digest('hex')}`;
|
|
1190
1871
|
}
|
|
1872
|
+
function createConfiguredToolSelectionPreview(current, proposed) {
|
|
1873
|
+
const currentRows = new Map(current.rows.map((row) => [row.name, row]));
|
|
1874
|
+
const proposedRows = new Map(proposed.rows.map((row) => [row.name, row]));
|
|
1875
|
+
const changedTools = Array.from(new Set([...currentRows.keys(), ...proposedRows.keys()]))
|
|
1876
|
+
.filter((name) => {
|
|
1877
|
+
const before = currentRows.get(name);
|
|
1878
|
+
const after = proposedRows.get(name);
|
|
1879
|
+
return (before?.enabled !== after?.enabled ||
|
|
1880
|
+
before?.descriptionOverride !== after?.descriptionOverride ||
|
|
1881
|
+
before?.effectiveDescription !== after?.effectiveDescription);
|
|
1882
|
+
})
|
|
1883
|
+
.sort((left, right) => left.localeCompare(right));
|
|
1884
|
+
return {
|
|
1885
|
+
capabilityGeneration: current.generation,
|
|
1886
|
+
model: proposed.model,
|
|
1887
|
+
changedTools,
|
|
1888
|
+
counts: proposed.counts,
|
|
1889
|
+
approximateTokens: {
|
|
1890
|
+
before: current.approximateTokens.enabled,
|
|
1891
|
+
after: proposed.approximateTokens.enabled,
|
|
1892
|
+
savings: current.approximateTokens.enabled - proposed.approximateTokens.enabled,
|
|
1893
|
+
},
|
|
1894
|
+
targetEnabled: proposed.targetEnabled,
|
|
1895
|
+
effect: proposed.targetEnabled ? 'immediate' : 'deferred_until_target_enabled',
|
|
1896
|
+
requiresZeroEnabledConfirmation: changedTools.length > 0 &&
|
|
1897
|
+
current.rows.some((row) => row.observed && row.enabled) &&
|
|
1898
|
+
!proposed.rows.some((row) => row.observed && row.enabled),
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1191
1901
|
function redactEditDraftForFingerprint(edit) {
|
|
1192
1902
|
return {
|
|
1193
1903
|
...edit,
|
|
@@ -1215,6 +1925,9 @@ function createPreviewDiff(current, proposed, edit) {
|
|
|
1215
1925
|
pushDiff(diff, ['id'], current.id, proposed.id, proposed.id === current.id ? [] : ['rename']);
|
|
1216
1926
|
pushDiff(diff, ['enabled'], current.enabled, proposed.enabled, ['connection_critical']);
|
|
1217
1927
|
pushDiff(diff, ['tags'], current.tags, proposed.tags, []);
|
|
1928
|
+
if (edit.instructionOverride) {
|
|
1929
|
+
pushDiff(diff, ['instructionOverride'], current.instructionOverride, proposed.instructionOverride, []);
|
|
1930
|
+
}
|
|
1218
1931
|
const secretEditedTopLevelKeys = new Set((edit.secrets ?? []).map((secret) => secret.fieldPath[0]));
|
|
1219
1932
|
const transportEditedKeys = new Set(Object.keys(edit.transport ?? {}));
|
|
1220
1933
|
const transportKeys = new Set([...Object.keys(current.transport), ...Object.keys(proposed.transport)]);
|
|
@@ -1351,11 +2064,15 @@ function stableStringify(value) {
|
|
|
1351
2064
|
}
|
|
1352
2065
|
return JSON.stringify(value);
|
|
1353
2066
|
}
|
|
1354
|
-
function createConfiguredServerReadModel(name, serverConfig) {
|
|
2067
|
+
function createConfiguredServerReadModel(name, serverConfig, source = 'mcpServers', revision = fingerprintConfiguredServerTarget(serverConfig), instructionOverride = serverConfig.instructionOverride, hasInstructionOverride = Object.hasOwn(serverConfig, 'instructionOverride')) {
|
|
1355
2068
|
const secretInputs = [];
|
|
1356
2069
|
const transport = {};
|
|
1357
2070
|
for (const [key, value] of Object.entries(serverConfig)) {
|
|
1358
|
-
if (key === 'disabled' ||
|
|
2071
|
+
if (key === 'disabled' ||
|
|
2072
|
+
key === 'tags' ||
|
|
2073
|
+
key === 'instructionOverride' ||
|
|
2074
|
+
key === 'disabledTools' ||
|
|
2075
|
+
key === 'toolDescriptionOverrides') {
|
|
1359
2076
|
continue;
|
|
1360
2077
|
}
|
|
1361
2078
|
if (key === 'env') {
|
|
@@ -1383,22 +2100,33 @@ function createConfiguredServerReadModel(name, serverConfig) {
|
|
|
1383
2100
|
const enabled = !isConfiguredServerTargetDisabled(serverConfig.disabled);
|
|
1384
2101
|
return {
|
|
1385
2102
|
id: name,
|
|
1386
|
-
source
|
|
2103
|
+
source,
|
|
1387
2104
|
target: {
|
|
1388
2105
|
type: 'configured_server',
|
|
1389
2106
|
id: name,
|
|
1390
|
-
source
|
|
2107
|
+
source,
|
|
1391
2108
|
},
|
|
2109
|
+
revision,
|
|
1392
2110
|
enabled,
|
|
1393
2111
|
tags: normalizeTags(serverConfig.tags),
|
|
1394
2112
|
transportSummary: createTransportSummary(serverConfig, transport),
|
|
1395
2113
|
mutationAvailability: {
|
|
1396
|
-
available:
|
|
1397
|
-
operations: ['enable', 'disable'],
|
|
2114
|
+
available: source === 'mcpServers',
|
|
2115
|
+
operations: source === 'mcpServers' ? ['enable', 'disable'] : [],
|
|
1398
2116
|
},
|
|
1399
|
-
actionState:
|
|
2117
|
+
actionState: source === 'mcpServers'
|
|
2118
|
+
? createActionState(name, enabled)
|
|
2119
|
+
: {
|
|
2120
|
+
enable: { available: false, label: `Enable ${name}`, disabledReason: 'already_enabled' },
|
|
2121
|
+
disable: { available: false, label: `Disable ${name}`, disabledReason: 'already_disabled' },
|
|
2122
|
+
},
|
|
1400
2123
|
transport,
|
|
1401
2124
|
secretInputs,
|
|
2125
|
+
instructionOverride: !hasInstructionOverride
|
|
2126
|
+
? { state: 'upstream' }
|
|
2127
|
+
: instructionOverride === ''
|
|
2128
|
+
? { state: 'suppress', value: '' }
|
|
2129
|
+
: { state: 'replace', value: instructionOverride },
|
|
1402
2130
|
};
|
|
1403
2131
|
}
|
|
1404
2132
|
const STDIO_TRANSPORT_TYPES = ['stdio'];
|
|
@@ -1413,7 +2141,6 @@ const TRANSPORT_FIELD_DEFINITIONS = [
|
|
|
1413
2141
|
{ key: 'timeout', label: 'Deprecated Timeout', control: 'number' },
|
|
1414
2142
|
{ key: 'connectionTimeout', label: 'Connection Timeout', control: 'number' },
|
|
1415
2143
|
{ key: 'requestTimeout', label: 'Request Timeout', control: 'number' },
|
|
1416
|
-
{ key: 'disabledTools', label: 'Disabled Tools', control: 'string-list', defaultValue: [] },
|
|
1417
2144
|
{ key: 'template', label: 'Template', control: 'record', defaultValue: {} },
|
|
1418
2145
|
{
|
|
1419
2146
|
key: 'url',
|