@1mcp/agent 0.35.0-beta.4 → 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.
Files changed (204) hide show
  1. package/build/admin/assets/{admin-AboutRuntimeWorkspace-CM2ZafTJ.js → admin-AboutRuntimeWorkspace-DH2QC2nJ.js} +1 -1
  2. package/build/admin/assets/admin-BackendLogsWorkspace-BKavXT9B.js +1 -0
  3. package/build/admin/assets/admin-InstructionTemplatesWorkspace-CvG4EPdZ.js +1 -0
  4. package/build/admin/assets/admin-OAuthServicesWorkspace-B8y2weAX.js +1 -0
  5. package/build/admin/assets/admin-PresetAuthoringWorkspace-BboJVBFl.js +1 -0
  6. package/build/admin/assets/admin-console-D6jyI73o.css +1 -0
  7. package/build/admin/assets/admin-console-HJH52xGP.js +3 -0
  8. package/build/admin/assets/admin-icons-CYGTDZA5.js +1 -0
  9. package/build/admin/assets/admin-mantine-xwbfu5Gd.js +53 -0
  10. package/build/admin/index.html +4 -4
  11. package/build/auth/sdkOAuthClientProvider.d.ts +4 -0
  12. package/build/auth/sdkOAuthClientProvider.d.ts.map +1 -1
  13. package/build/auth/sdkOAuthClientProvider.js +29 -0
  14. package/build/auth/sdkOAuthClientProvider.js.map +1 -1
  15. package/build/commands/instructions/instructions.d.ts.map +1 -1
  16. package/build/commands/instructions/instructions.js +56 -1
  17. package/build/commands/instructions/instructions.js.map +1 -1
  18. package/build/commands/mcp/add.js +2 -2
  19. package/build/commands/mcp/add.js.map +1 -1
  20. package/build/commands/mcp/list.js +1 -1
  21. package/build/commands/mcp/list.js.map +1 -1
  22. package/build/commands/mcp/update.js +1 -1
  23. package/build/commands/mcp/update.js.map +1 -1
  24. package/build/commands/shared/serveTargetResolver.d.ts.map +1 -1
  25. package/build/commands/shared/serveTargetResolver.js +17 -22
  26. package/build/commands/shared/serveTargetResolver.js.map +1 -1
  27. package/build/config/configManager.d.ts +11 -1
  28. package/build/config/configManager.d.ts.map +1 -1
  29. package/build/config/configManager.js +13 -0
  30. package/build/config/configManager.js.map +1 -1
  31. package/build/config/configuredServerTargets.d.ts +3 -0
  32. package/build/config/configuredServerTargets.d.ts.map +1 -0
  33. package/build/config/configuredServerTargets.js +8 -0
  34. package/build/config/configuredServerTargets.js.map +1 -0
  35. package/build/config/mcpConfigManager.d.ts +3 -0
  36. package/build/config/mcpConfigManager.d.ts.map +1 -1
  37. package/build/config/mcpConfigManager.js +15 -0
  38. package/build/config/mcpConfigManager.js.map +1 -1
  39. package/build/constants/api.d.ts +1 -0
  40. package/build/constants/api.d.ts.map +1 -1
  41. package/build/constants/api.js +1 -0
  42. package/build/constants/api.js.map +1 -1
  43. package/build/constants/mcp.d.ts +1 -1
  44. package/build/constants/mcp.d.ts.map +1 -1
  45. package/build/constants/mcp.js +1 -1
  46. package/build/constants/mcp.js.map +1 -1
  47. package/build/core/capabilities/capabilityAggregator.d.ts.map +1 -1
  48. package/build/core/capabilities/capabilityAggregator.js +33 -14
  49. package/build/core/capabilities/capabilityAggregator.js.map +1 -1
  50. package/build/core/capabilities/capabilityCatalog.d.ts +1 -1
  51. package/build/core/capabilities/capabilityCatalog.d.ts.map +1 -1
  52. package/build/core/capabilities/capabilityCatalog.js +19 -7
  53. package/build/core/capabilities/capabilityCatalog.js.map +1 -1
  54. package/build/core/capabilities/capabilityPagination.d.ts.map +1 -1
  55. package/build/core/capabilities/capabilityPagination.js +2 -0
  56. package/build/core/capabilities/capabilityPagination.js.map +1 -1
  57. package/build/core/capabilities/configuredToolSnapshot.d.ts +12 -0
  58. package/build/core/capabilities/configuredToolSnapshot.d.ts.map +1 -0
  59. package/build/core/capabilities/configuredToolSnapshot.js +98 -0
  60. package/build/core/capabilities/configuredToolSnapshot.js.map +1 -0
  61. package/build/core/capabilities/lazyLoadingOrchestrator.d.ts.map +1 -1
  62. package/build/core/capabilities/lazyLoadingOrchestrator.js +8 -4
  63. package/build/core/capabilities/lazyLoadingOrchestrator.js.map +1 -1
  64. package/build/core/capabilities/metaToolProvider.js +3 -3
  65. package/build/core/capabilities/metaToolProvider.js.map +1 -1
  66. package/build/core/client/clientManager.js +6 -6
  67. package/build/core/client/clientManager.js.map +1 -1
  68. package/build/core/client/postAuthOAuthRecovery.d.ts +6 -0
  69. package/build/core/client/postAuthOAuthRecovery.d.ts.map +1 -0
  70. package/build/core/client/postAuthOAuthRecovery.js +74 -0
  71. package/build/core/client/postAuthOAuthRecovery.js.map +1 -0
  72. package/build/core/configChangeHandler.d.ts +2 -0
  73. package/build/core/configChangeHandler.d.ts.map +1 -1
  74. package/build/core/configChangeHandler.js +38 -11
  75. package/build/core/configChangeHandler.js.map +1 -1
  76. package/build/core/instructions/effectiveServerInstructions.d.ts +17 -0
  77. package/build/core/instructions/effectiveServerInstructions.d.ts.map +1 -0
  78. package/build/core/instructions/effectiveServerInstructions.js +12 -0
  79. package/build/core/instructions/effectiveServerInstructions.js.map +1 -0
  80. package/build/core/instructions/instructionAggregator.d.ts +51 -2
  81. package/build/core/instructions/instructionAggregator.d.ts.map +1 -1
  82. package/build/core/instructions/instructionAggregator.js +191 -30
  83. package/build/core/instructions/instructionAggregator.js.map +1 -1
  84. package/build/core/instructions/instructionsDistribution.d.ts +53 -0
  85. package/build/core/instructions/instructionsDistribution.d.ts.map +1 -1
  86. package/build/core/instructions/instructionsDistribution.js +25 -0
  87. package/build/core/instructions/instructionsDistribution.js.map +1 -1
  88. package/build/core/instructions/templateTypes.d.ts +25 -3
  89. package/build/core/instructions/templateTypes.d.ts.map +1 -1
  90. package/build/core/instructions/templateTypes.js +45 -0
  91. package/build/core/instructions/templateTypes.js.map +1 -1
  92. package/build/core/protocol/requestHandlerUtils.d.ts.map +1 -1
  93. package/build/core/protocol/requestHandlerUtils.js +3 -3
  94. package/build/core/protocol/requestHandlerUtils.js.map +1 -1
  95. package/build/core/protocol/toolRequestHandlers.d.ts.map +1 -1
  96. package/build/core/protocol/toolRequestHandlers.js +18 -5
  97. package/build/core/protocol/toolRequestHandlers.js.map +1 -1
  98. package/build/core/server/clientInstancePool.js +2 -2
  99. package/build/core/server/clientInstancePool.js.map +1 -1
  100. package/build/core/server/disabledTools.d.ts +2 -0
  101. package/build/core/server/disabledTools.d.ts.map +1 -1
  102. package/build/core/server/disabledTools.js +7 -4
  103. package/build/core/server/disabledTools.js.map +1 -1
  104. package/build/core/server/serverManager.d.ts.map +1 -1
  105. package/build/core/server/serverManager.js +1 -0
  106. package/build/core/server/serverManager.js.map +1 -1
  107. package/build/core/server/templateIdentity.d.ts +1 -0
  108. package/build/core/server/templateIdentity.d.ts.map +1 -1
  109. package/build/core/server/templateIdentity.js +11 -1
  110. package/build/core/server/templateIdentity.js.map +1 -1
  111. package/build/core/server/templateServerManager.d.ts +5 -1
  112. package/build/core/server/templateServerManager.d.ts.map +1 -1
  113. package/build/core/server/templateServerManager.js +26 -10
  114. package/build/core/server/templateServerManager.js.map +1 -1
  115. package/build/core/server/toolDescriptionOverrides.d.ts +9 -0
  116. package/build/core/server/toolDescriptionOverrides.d.ts.map +1 -0
  117. package/build/core/server/toolDescriptionOverrides.js +44 -0
  118. package/build/core/server/toolDescriptionOverrides.js.map +1 -0
  119. package/build/core/tools/handlers/serverManagementHandler.d.ts +15 -1
  120. package/build/core/tools/handlers/serverManagementHandler.d.ts.map +1 -1
  121. package/build/core/tools/internal/adapters/helpers/configTransformers.d.ts +2 -0
  122. package/build/core/tools/internal/adapters/helpers/configTransformers.d.ts.map +1 -1
  123. package/build/core/tools/internal/schemas/discovery.d.ts +1 -1
  124. package/build/core/tools/internal/schemas/index.d.ts +2 -2
  125. package/build/core/tools/internal/schemas/management.d.ts +1 -1
  126. package/build/core/types/transport.d.ts +33 -0
  127. package/build/core/types/transport.d.ts.map +1 -1
  128. package/build/core/types/transport.js +55 -1
  129. package/build/core/types/transport.js.map +1 -1
  130. package/build/domains/admin/adminConfiguredServerService.d.ts +51 -4
  131. package/build/domains/admin/adminConfiguredServerService.d.ts.map +1 -1
  132. package/build/domains/admin/adminConfiguredServerService.js +259 -38
  133. package/build/domains/admin/adminConfiguredServerService.js.map +1 -1
  134. package/build/domains/admin/adminDomain.d.ts +13 -0
  135. package/build/domains/admin/adminDomain.d.ts.map +1 -1
  136. package/build/domains/admin/adminDomain.js +16 -0
  137. package/build/domains/admin/adminDomain.js.map +1 -1
  138. package/build/domains/admin/adminInstructionPreviewRuntime.d.ts +7 -0
  139. package/build/domains/admin/adminInstructionPreviewRuntime.d.ts.map +1 -0
  140. package/build/domains/admin/adminInstructionPreviewRuntime.js +154 -0
  141. package/build/domains/admin/adminInstructionPreviewRuntime.js.map +1 -0
  142. package/build/domains/admin/adminInstructionTemplateService.d.ts +169 -0
  143. package/build/domains/admin/adminInstructionTemplateService.d.ts.map +1 -0
  144. package/build/domains/admin/adminInstructionTemplateService.js +181 -0
  145. package/build/domains/admin/adminInstructionTemplateService.js.map +1 -0
  146. package/build/domains/admin/configuredToolInventory.d.ts +46 -0
  147. package/build/domains/admin/configuredToolInventory.d.ts.map +1 -0
  148. package/build/domains/admin/configuredToolInventory.js +135 -0
  149. package/build/domains/admin/configuredToolInventory.js.map +1 -0
  150. package/build/domains/config-change/configChange.d.ts +1 -1
  151. package/build/domains/config-change/configChange.d.ts.map +1 -1
  152. package/build/domains/config-change/configChange.js +153 -13
  153. package/build/domains/config-change/configChange.js.map +1 -1
  154. package/build/domains/config-change/types.d.ts +30 -2
  155. package/build/domains/config-change/types.d.ts.map +1 -1
  156. package/build/domains/instruction-template/instructionTemplateManager.d.ts +58 -0
  157. package/build/domains/instruction-template/instructionTemplateManager.d.ts.map +1 -0
  158. package/build/domains/instruction-template/instructionTemplateManager.js +167 -0
  159. package/build/domains/instruction-template/instructionTemplateManager.js.map +1 -0
  160. package/build/domains/runtime-targets/runtimeIdentityVerification.d.ts +1 -0
  161. package/build/domains/runtime-targets/runtimeIdentityVerification.d.ts.map +1 -1
  162. package/build/domains/runtime-targets/runtimeIdentityVerification.js +6 -5
  163. package/build/domains/runtime-targets/runtimeIdentityVerification.js.map +1 -1
  164. package/build/transport/http/routes/adminRoutes.d.ts +2 -0
  165. package/build/transport/http/routes/adminRoutes.d.ts.map +1 -1
  166. package/build/transport/http/routes/adminRoutes.js +293 -9
  167. package/build/transport/http/routes/adminRoutes.js.map +1 -1
  168. package/build/transport/http/routes/apiRoutes.d.ts.map +1 -1
  169. package/build/transport/http/routes/apiRoutes.js +2 -0
  170. package/build/transport/http/routes/apiRoutes.js.map +1 -1
  171. package/build/transport/http/routes/inspectRequestContext.d.ts +2 -0
  172. package/build/transport/http/routes/inspectRequestContext.d.ts.map +1 -1
  173. package/build/transport/http/routes/inspectRequestContext.js +2 -2
  174. package/build/transport/http/routes/inspectRequestContext.js.map +1 -1
  175. package/build/transport/http/routes/inspectRoutes.d.ts +9 -0
  176. package/build/transport/http/routes/inspectRoutes.d.ts.map +1 -1
  177. package/build/transport/http/routes/inspectRoutes.js +15 -10
  178. package/build/transport/http/routes/inspectRoutes.js.map +1 -1
  179. package/build/transport/http/routes/instructionsRoutes.d.ts +4 -0
  180. package/build/transport/http/routes/instructionsRoutes.d.ts.map +1 -0
  181. package/build/transport/http/routes/instructionsRoutes.js +147 -0
  182. package/build/transport/http/routes/instructionsRoutes.js.map +1 -0
  183. package/build/transport/http/routes/toolRoutes.d.ts.map +1 -1
  184. package/build/transport/http/routes/toolRoutes.js +5 -4
  185. package/build/transport/http/routes/toolRoutes.js.map +1 -1
  186. package/build/transport/http/server.d.ts.map +1 -1
  187. package/build/transport/http/server.js +17 -0
  188. package/build/transport/http/server.js.map +1 -1
  189. package/build/utils/validation/discoveredServer.d.ts +15 -0
  190. package/build/utils/validation/discoveredServer.d.ts.map +1 -0
  191. package/build/utils/validation/discoveredServer.js +2 -0
  192. package/build/utils/validation/discoveredServer.js.map +1 -0
  193. package/build/utils/validation/urlDetection.d.ts +4 -5
  194. package/build/utils/validation/urlDetection.d.ts.map +1 -1
  195. package/build/utils/validation/urlDetection.js +94 -25
  196. package/build/utils/validation/urlDetection.js.map +1 -1
  197. package/package.json +2 -2
  198. package/build/admin/assets/admin-BackendLogsWorkspace-CvJs1npx.js +0 -1
  199. package/build/admin/assets/admin-OAuthServicesWorkspace-BswG57Gm.js +0 -1
  200. package/build/admin/assets/admin-PresetAuthoringWorkspace-BIIRs_EN.js +0 -1
  201. package/build/admin/assets/admin-console-Brz9bnhv.css +0 -1
  202. package/build/admin/assets/admin-console-MT8QZmIC.js +0 -3
  203. package/build/admin/assets/admin-icons-D-JtMXSq.js +0 -1
  204. package/build/admin/assets/admin-mantine-DSFdoMaN.js +0 -53
@@ -1,5 +1,6 @@
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
5
  import { fingerprintConfiguredServerConfigDocument, fingerprintConfiguredServerDefaults, fingerprintConfiguredServerSecretValue, fingerprintConfiguredServerTarget, isConfiguredServerTargetDisabled, } from '../../domains/config-change/configChange.js';
5
6
  import { createServerInstallationWorkflow, } from '../../domains/installation/serverInstallationWorkflow.js';
@@ -472,56 +473,71 @@ export class AdminConfiguredServerService {
472
473
  return this.options.operationService.executeReadOnly({
473
474
  context: input.context,
474
475
  operationName: 'listConfiguredServers',
475
- run: async () => ({ servers: this.readConfiguredServers() }),
476
+ run: async () => this.readConfiguredServers(),
476
477
  });
477
478
  }
478
479
  async getConfiguredServerDetail(input) {
480
+ const configuredState = this.readConfiguredServerState(input.targetName, input.targetSource);
481
+ const source = configuredState.source;
479
482
  const context = {
480
483
  ...input.context,
481
- target: { type: 'configured_server', id: input.targetName },
484
+ target: { type: 'configured_server', id: `${source}:${input.targetName}` },
482
485
  };
483
486
  return this.options.operationService.executeReadOnly({
484
487
  context,
485
488
  operationName: 'getConfiguredServerDetail',
486
489
  run: async () => {
487
- const { currentConfig, serverDefaults } = this.readConfiguredServerState(input.targetName);
488
- 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'));
489
492
  return {
490
493
  server,
491
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
+ : {}),
492
505
  };
493
506
  },
494
507
  });
495
508
  }
496
509
  async previewConfiguredServerEdit(input) {
510
+ const configuredState = this.readConfiguredServerState(input.targetName, input.targetSource);
511
+ const source = configuredState.source;
497
512
  const context = {
498
513
  ...input.context,
499
- target: { type: 'configured_server', id: input.targetName },
514
+ target: { type: 'configured_server', id: `${source}:${input.targetName}` },
500
515
  };
501
- const { currentConfig, serverDefaults } = this.readConfiguredServerState(input.targetName);
516
+ const { currentConfig, serverDefaults } = configuredState;
502
517
  return this.options.operationService.executeDryRun({
503
518
  context,
504
519
  operationName: 'previewConfiguredServerEdit',
505
- run: async () => this.previewConfiguredServerEditResult(input, currentConfig, serverDefaults),
520
+ run: async () => this.previewConfiguredServerEditResult(input, currentConfig, serverDefaults, false, source),
506
521
  });
507
522
  }
508
523
  async applyConfiguredServerEdit(input) {
524
+ const source = this.resolveConfiguredServerSource(input.targetName, input.targetSource);
509
525
  const context = {
510
526
  ...input.context,
511
- target: { type: 'configured_server', id: input.targetName },
527
+ target: { type: 'configured_server', id: `${source}:${input.targetName}` },
512
528
  confirmationFacts: sanitizeApplyConfirmationFacts(input.context.confirmationFacts),
513
529
  };
514
530
  return this.options.operationService.executeMutation({
515
531
  context,
516
532
  operationName: 'applyConfiguredServerEdit',
517
533
  prepare: async () => {
518
- const { currentConfig, serverDefaults } = this.readConfiguredServerState(input.targetName);
519
- 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);
520
536
  const normalizedEdit = normalizeEditDraft(input.edit);
521
- const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig));
537
+ const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig), source);
522
538
  const applicableEdit = filterApplicableSecretEdits(normalizedEdit.edit, currentReadModel.secretInputs);
523
- const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults);
524
- 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);
525
541
  const connectionCritical = preview.diff.some((entry) => entry.riskFlags.includes('connection_critical'));
526
542
  const secretChange = preview.diff.some((entry) => entry.riskFlags.includes('secret'));
527
543
  if (preview.validation.status !== 'valid') {
@@ -549,6 +565,9 @@ export class AdminConfiguredServerService {
549
565
  : []),
550
566
  ...(secretChange ? [{ code: 'secretChangeConfirmed', expected: true }] : []),
551
567
  ...(connectionCritical ? [{ code: 'connectionCriticalConfirmed', expected: true }] : []),
568
+ ...(preview.toolSelection?.requiresZeroEnabledConfirmation
569
+ ? [{ code: 'zeroEnabledToolsConfirmed', expected: true }]
570
+ : []),
552
571
  ...(preview.connectivityCheck.status === 'failed'
553
572
  ? [{ code: 'connectivityFailureOverrideConfirmed', expected: true }]
554
573
  : []),
@@ -556,6 +575,7 @@ export class AdminConfiguredServerService {
556
575
  run: async () => {
557
576
  const configChange = await this.options.configChangeService.editConfiguredServerTarget({
558
577
  sourceName: input.targetName,
578
+ targetSource: source,
559
579
  targetName: preview.proposedTargetName,
560
580
  serverConfig: proposedConfig,
561
581
  expectedSourceFingerprint: fingerprintConfiguredServerTarget(currentConfig),
@@ -584,17 +604,22 @@ export class AdminConfiguredServerService {
584
604
  }
585
605
  async setConfiguredServerEnabledState(input, enabled) {
586
606
  const operationName = enabled ? 'enableConfiguredServer' : 'disableConfiguredServer';
607
+ const source = input.targetSource ?? 'mcpServers';
587
608
  const context = {
588
609
  ...input.context,
589
- target: { type: 'configured_server', id: input.targetName },
610
+ target: { type: 'configured_server', id: `${source}:${input.targetName}` },
590
611
  };
591
612
  if (input.dryRun) {
592
613
  return this.options.operationService.executeDryRun({
593
614
  context,
594
615
  operationName,
595
616
  run: async () => {
617
+ if (source !== 'mcpServers') {
618
+ throw new Error('Template Server definitions do not support enable or disable operations');
619
+ }
596
620
  const configChange = await this.options.configChangeService.previewConfiguredServerTargetEnabledState({
597
621
  targetName: input.targetName,
622
+ targetSource: source,
598
623
  enabled,
599
624
  backup: 'skip',
600
625
  });
@@ -614,8 +639,12 @@ export class AdminConfiguredServerService {
614
639
  operationName,
615
640
  confirmationRequirements: input.confirmationRequirements,
616
641
  run: async () => {
642
+ if (source !== 'mcpServers') {
643
+ throw new Error('Template Server definitions do not support enable or disable operations');
644
+ }
617
645
  const configChange = await this.options.configChangeService.setConfiguredServerTargetEnabledState({
618
646
  targetName: input.targetName,
647
+ targetSource: source,
619
648
  enabled,
620
649
  backup: 'required',
621
650
  });
@@ -632,49 +661,84 @@ export class AdminConfiguredServerService {
632
661
  readConfiguredServers() {
633
662
  const parsed = this.options.readConfigDocument();
634
663
  if (!parsed) {
635
- return [];
664
+ return { servers: [], configFingerprint: fingerprintConfiguredServerConfigDocument({}) };
636
665
  }
637
- return Object.entries(parsed.mcpServers ?? {}).map(([name, serverConfig]) => createConfiguredServerReadModel(name, mergeGlobalAndServerConfig(parsed.serverDefaults, serverConfig)));
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
+ };
638
670
  }
639
- readConfiguredServerState(targetName) {
671
+ readConfiguredServerState(targetName, targetSource) {
640
672
  const parsed = this.options.readConfigDocument();
641
- const currentConfig = parsed?.mcpServers?.[targetName];
673
+ const source = this.resolveConfiguredServerSource(targetName, targetSource, parsed);
674
+ const currentConfig = parsed?.[source]?.[targetName];
642
675
  if (!currentConfig) {
643
676
  throw new AdminConfiguredServerNotFoundError(targetName);
644
677
  }
645
- 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';
646
684
  }
647
685
  readConfiguredServerConfig(targetName) {
648
686
  const parsed = this.options.readConfigDocument();
649
- const currentConfig = parsed?.mcpServers?.[targetName];
687
+ const currentConfig = parsed?.mcpTemplates?.[targetName] ?? parsed?.mcpServers?.[targetName];
650
688
  if (!currentConfig) {
651
689
  throw new AdminConfiguredServerNotFoundError(targetName);
652
690
  }
653
691
  return currentConfig;
654
692
  }
655
- async previewConfiguredServerEditResult(input, currentConfig, serverDefaults, requireCriticalConnectivity = false) {
693
+ async previewConfiguredServerEditResult(input, currentConfig, serverDefaults, requireCriticalConnectivity = false, source = 'mcpServers') {
656
694
  const normalizedEdit = normalizeEditDraft(input.edit);
657
- const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig));
695
+ const sourceValidation = validateEditForSource(source, normalizedEdit.edit);
696
+ const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig), source);
658
697
  const secretValidation = validateSecretEditCapabilities(normalizedEdit.edit, currentReadModel.secretInputs);
659
698
  const transportApplicabilityValidation = validateTransportFieldApplicability(currentConfig, normalizedEdit.edit);
660
699
  const overrideValidation = validateTransportOverrideClears(currentConfig, normalizedEdit.edit);
661
700
  const applicableEdit = filterApplicableSecretEdits(normalizedEdit.edit, currentReadModel.secretInputs);
662
701
  const proposedTargetName = applicableEdit.id?.trim() || input.targetName;
663
- const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults);
664
- 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);
665
704
  const proposedTransportType = configuredTransportType(proposedConfig);
666
705
  const serverValidation = validatePreviewServerConfig(proposedConfig);
667
- const validation = mergeValidation(mergeValidation(mergeValidation(mergeValidation(normalizedEdit.validation, secretValidation), transportApplicabilityValidation), overrideValidation), serverValidation);
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));
668
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']);
669
721
  appendClearedOverrideDiff(diff, currentReadModel, proposedReadModel, applicableEdit);
670
722
  const changed = validation.status === 'valid' && diff.length > 0;
671
723
  const endpointChangedWithPreservedSecrets = endpointAuthorityChanged(currentConfig, proposedConfig) &&
672
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;
673
736
  return {
674
737
  targetName: input.targetName,
675
738
  proposedTargetName,
676
739
  previewFingerprint: previewFingerprint({
677
740
  targetName: input.targetName,
741
+ targetSource: source,
678
742
  sourceFingerprint: fingerprintConfiguredServerTarget(currentConfig),
679
743
  globalConfigFingerprint: fingerprintConfiguredServerDefaults(serverDefaults),
680
744
  edit: normalizedEdit.edit,
@@ -682,10 +746,11 @@ export class AdminConfiguredServerService {
682
746
  proposed: proposedReadModel,
683
747
  diff,
684
748
  validation,
749
+ toolSelection,
685
750
  }),
686
751
  validation,
687
752
  diff,
688
- configChange: previewConfigChange(input.targetName, changed),
753
+ configChange: previewConfigChange(input.targetName, changed, source),
689
754
  connectivityCheck: await this.previewConnectivityCheck({
690
755
  targetName: proposedTargetName,
691
756
  serverConfig: proposedConfig,
@@ -696,6 +761,7 @@ export class AdminConfiguredServerService {
696
761
  (requireCriticalConnectivity && diff.some((entry) => entry.riskFlags.includes('connection_critical'))),
697
762
  endpointChangedWithPreservedSecrets,
698
763
  }),
764
+ ...(toolSelection ? { toolSelection } : {}),
699
765
  };
700
766
  }
701
767
  async previewConnectivityCheck(input) {
@@ -787,6 +853,20 @@ export class AdminConfiguredServerService {
787
853
  };
788
854
  }
789
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
+ };
869
+ }
790
870
  function assertSuccessfulConfigChange(configChange) {
791
871
  if (configChange.reload.status === 'failed') {
792
872
  throw new Error(`Config reload observation failed: ${configChange.reload.error ?? 'unknown reload failure'}`);
@@ -831,6 +911,7 @@ function sanitizeApplyConfirmationFacts(facts) {
831
911
  return undefined;
832
912
  const allowed = ['previewConfirmed', 'targetNameConfirmed', 'secretChangeConfirmed', 'connectionCriticalConfirmed'];
833
913
  allowed.push('connectivityFailureOverrideConfirmed');
914
+ allowed.push('zeroEnabledToolsConfirmed');
834
915
  const sanitized = Object.fromEntries(allowed.filter((key) => key in facts).map((key) => [key, facts[key]]));
835
916
  return Object.keys(sanitized).length > 0 ? sanitized : undefined;
836
917
  }
@@ -843,7 +924,17 @@ function mutationOutcome(enabled, changed) {
843
924
  function normalizeEditDraft(value) {
844
925
  const errors = [];
845
926
  const edit = {};
846
- const supportedKeys = new Set(['id', 'enabled', 'tags', 'transport', 'secrets', 'clearTransportOverrides']);
927
+ const supportedKeys = new Set([
928
+ 'id',
929
+ 'enabled',
930
+ 'tags',
931
+ 'transport',
932
+ 'secrets',
933
+ 'clearTransportOverrides',
934
+ 'instructionOverride',
935
+ 'disabledTools',
936
+ 'toolDescriptionOverrides',
937
+ ]);
847
938
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
848
939
  return {
849
940
  edit,
@@ -905,6 +996,36 @@ function normalizeEditDraft(value) {
905
996
  });
906
997
  }
907
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
+ }
908
1029
  if (record.transport !== undefined) {
909
1030
  if (record.transport && typeof record.transport === 'object' && !Array.isArray(record.transport)) {
910
1031
  edit.transport = normalizeTransportEditDraft(record.transport, errors);
@@ -943,6 +1064,23 @@ function normalizeEditDraft(value) {
943
1064
  });
944
1065
  }
945
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
+ }
946
1084
  return {
947
1085
  edit,
948
1086
  validation: { status: errors.length === 0 ? 'valid' : 'invalid', errors },
@@ -971,6 +1109,20 @@ function normalizeTransportEditDraft(record, errors) {
971
1109
  }
972
1110
  return transport;
973
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
+ }
974
1126
  function isSecretCapableRawTransportEdit(key, value) {
975
1127
  if (key === 'url') {
976
1128
  return isSecretCapableUrlEdit(value);
@@ -1267,7 +1419,7 @@ function potentialSecretInput(fieldPath) {
1267
1419
  }
1268
1420
  return undefined;
1269
1421
  }
1270
- function applyEditDraft(currentConfig, edit, serverDefaults) {
1422
+ function applyEditDraft(currentConfig, edit, serverDefaults, logicalServerName) {
1271
1423
  const nextConfig = cloneServerConfig(currentConfig);
1272
1424
  for (const key of edit.clearTransportOverrides ?? []) {
1273
1425
  delete nextConfig[key];
@@ -1283,6 +1435,27 @@ function applyEditDraft(currentConfig, edit, serverDefaults) {
1283
1435
  if (Array.isArray(edit.tags)) {
1284
1436
  nextConfig.tags = edit.tags.filter((tag) => typeof tag === 'string');
1285
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
+ }
1286
1459
  if (edit.transport && typeof edit.transport === 'object') {
1287
1460
  const currentType = configuredTransportType(currentConfig);
1288
1461
  const proposedType = configuredTransportType({ ...currentConfig, ...edit.transport });
@@ -1666,12 +1839,12 @@ function isValidUrlOrEnvReference(value) {
1666
1839
  return false;
1667
1840
  }
1668
1841
  }
1669
- function previewConfigChange(targetName, changed) {
1842
+ function previewConfigChange(targetName, changed, source = 'mcpServers') {
1670
1843
  return {
1671
1844
  status: changed ? 'changed' : 'unchanged',
1672
1845
  operation: 'set_static',
1673
1846
  configPath: '[redacted]',
1674
- target: { name: targetName, source: 'mcpServers' },
1847
+ target: { name: targetName, source },
1675
1848
  changed,
1676
1849
  backup: { created: false },
1677
1850
  retentionCleanup: { attempted: false, deletedPaths: [], warnings: [] },
@@ -1684,6 +1857,7 @@ function previewFingerprint(input) {
1684
1857
  .update(stableStringify({
1685
1858
  schemaVersion: 2,
1686
1859
  targetName: input.targetName,
1860
+ targetSource: input.targetSource,
1687
1861
  sourceFingerprint: input.sourceFingerprint,
1688
1862
  globalConfigFingerprint: input.globalConfigFingerprint,
1689
1863
  edit: redactEditDraftForFingerprint(input.edit),
@@ -1691,9 +1865,39 @@ function previewFingerprint(input) {
1691
1865
  proposed: input.proposed,
1692
1866
  diff: input.diff,
1693
1867
  validation: input.validation,
1868
+ toolSelection: input.toolSelection,
1694
1869
  }))
1695
1870
  .digest('hex')}`;
1696
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
+ }
1697
1901
  function redactEditDraftForFingerprint(edit) {
1698
1902
  return {
1699
1903
  ...edit,
@@ -1721,6 +1925,9 @@ function createPreviewDiff(current, proposed, edit) {
1721
1925
  pushDiff(diff, ['id'], current.id, proposed.id, proposed.id === current.id ? [] : ['rename']);
1722
1926
  pushDiff(diff, ['enabled'], current.enabled, proposed.enabled, ['connection_critical']);
1723
1927
  pushDiff(diff, ['tags'], current.tags, proposed.tags, []);
1928
+ if (edit.instructionOverride) {
1929
+ pushDiff(diff, ['instructionOverride'], current.instructionOverride, proposed.instructionOverride, []);
1930
+ }
1724
1931
  const secretEditedTopLevelKeys = new Set((edit.secrets ?? []).map((secret) => secret.fieldPath[0]));
1725
1932
  const transportEditedKeys = new Set(Object.keys(edit.transport ?? {}));
1726
1933
  const transportKeys = new Set([...Object.keys(current.transport), ...Object.keys(proposed.transport)]);
@@ -1857,11 +2064,15 @@ function stableStringify(value) {
1857
2064
  }
1858
2065
  return JSON.stringify(value);
1859
2066
  }
1860
- function createConfiguredServerReadModel(name, serverConfig) {
2067
+ function createConfiguredServerReadModel(name, serverConfig, source = 'mcpServers', revision = fingerprintConfiguredServerTarget(serverConfig), instructionOverride = serverConfig.instructionOverride, hasInstructionOverride = Object.hasOwn(serverConfig, 'instructionOverride')) {
1861
2068
  const secretInputs = [];
1862
2069
  const transport = {};
1863
2070
  for (const [key, value] of Object.entries(serverConfig)) {
1864
- if (key === 'disabled' || key === 'tags') {
2071
+ if (key === 'disabled' ||
2072
+ key === 'tags' ||
2073
+ key === 'instructionOverride' ||
2074
+ key === 'disabledTools' ||
2075
+ key === 'toolDescriptionOverrides') {
1865
2076
  continue;
1866
2077
  }
1867
2078
  if (key === 'env') {
@@ -1889,22 +2100,33 @@ function createConfiguredServerReadModel(name, serverConfig) {
1889
2100
  const enabled = !isConfiguredServerTargetDisabled(serverConfig.disabled);
1890
2101
  return {
1891
2102
  id: name,
1892
- source: 'mcpServers',
2103
+ source,
1893
2104
  target: {
1894
2105
  type: 'configured_server',
1895
2106
  id: name,
1896
- source: 'mcpServers',
2107
+ source,
1897
2108
  },
2109
+ revision,
1898
2110
  enabled,
1899
2111
  tags: normalizeTags(serverConfig.tags),
1900
2112
  transportSummary: createTransportSummary(serverConfig, transport),
1901
2113
  mutationAvailability: {
1902
- available: true,
1903
- operations: ['enable', 'disable'],
2114
+ available: source === 'mcpServers',
2115
+ operations: source === 'mcpServers' ? ['enable', 'disable'] : [],
1904
2116
  },
1905
- actionState: createActionState(name, enabled),
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
+ },
1906
2123
  transport,
1907
2124
  secretInputs,
2125
+ instructionOverride: !hasInstructionOverride
2126
+ ? { state: 'upstream' }
2127
+ : instructionOverride === ''
2128
+ ? { state: 'suppress', value: '' }
2129
+ : { state: 'replace', value: instructionOverride },
1908
2130
  };
1909
2131
  }
1910
2132
  const STDIO_TRANSPORT_TYPES = ['stdio'];
@@ -1919,7 +2141,6 @@ const TRANSPORT_FIELD_DEFINITIONS = [
1919
2141
  { key: 'timeout', label: 'Deprecated Timeout', control: 'number' },
1920
2142
  { key: 'connectionTimeout', label: 'Connection Timeout', control: 'number' },
1921
2143
  { key: 'requestTimeout', label: 'Request Timeout', control: 'number' },
1922
- { key: 'disabledTools', label: 'Disabled Tools', control: 'string-list', defaultValue: [] },
1923
2144
  { key: 'template', label: 'Template', control: 'record', defaultValue: {} },
1924
2145
  {
1925
2146
  key: 'url',