@1mcp/agent 0.35.0-beta.4 → 0.36.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 (262) 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-BCGj2hP4.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-xbgqUjbT.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/configContext.d.ts +2 -0
  28. package/build/config/configContext.d.ts.map +1 -1
  29. package/build/config/configContext.js +5 -0
  30. package/build/config/configContext.js.map +1 -1
  31. package/build/config/configLoader.d.ts +8 -0
  32. package/build/config/configLoader.d.ts.map +1 -1
  33. package/build/config/configLoader.js +35 -11
  34. package/build/config/configLoader.js.map +1 -1
  35. package/build/config/configManager.d.ts +16 -1
  36. package/build/config/configManager.d.ts.map +1 -1
  37. package/build/config/configManager.js +77 -0
  38. package/build/config/configManager.js.map +1 -1
  39. package/build/config/configWatcher.d.ts +2 -0
  40. package/build/config/configWatcher.d.ts.map +1 -1
  41. package/build/config/configWatcher.js +9 -4
  42. package/build/config/configWatcher.js.map +1 -1
  43. package/build/config/configuredServerTargets.d.ts +3 -0
  44. package/build/config/configuredServerTargets.d.ts.map +1 -0
  45. package/build/config/configuredServerTargets.js +8 -0
  46. package/build/config/configuredServerTargets.js.map +1 -0
  47. package/build/config/envProcessor.d.ts +2 -1
  48. package/build/config/envProcessor.d.ts.map +1 -1
  49. package/build/config/envProcessor.js +16 -9
  50. package/build/config/envProcessor.js.map +1 -1
  51. package/build/config/mcpConfigManager.d.ts +3 -0
  52. package/build/config/mcpConfigManager.d.ts.map +1 -1
  53. package/build/config/mcpConfigManager.js +15 -0
  54. package/build/config/mcpConfigManager.js.map +1 -1
  55. package/build/config/runtimeScopeEnv.d.ts +15 -0
  56. package/build/config/runtimeScopeEnv.d.ts.map +1 -0
  57. package/build/config/runtimeScopeEnv.js +231 -0
  58. package/build/config/runtimeScopeEnv.js.map +1 -0
  59. package/build/config/runtimeTargetFingerprint.d.ts +3 -0
  60. package/build/config/runtimeTargetFingerprint.d.ts.map +1 -0
  61. package/build/config/runtimeTargetFingerprint.js +66 -0
  62. package/build/config/runtimeTargetFingerprint.js.map +1 -0
  63. package/build/config/types.d.ts +5 -0
  64. package/build/config/types.d.ts.map +1 -1
  65. package/build/config/types.js +1 -0
  66. package/build/config/types.js.map +1 -1
  67. package/build/constants/api.d.ts +1 -0
  68. package/build/constants/api.d.ts.map +1 -1
  69. package/build/constants/api.js +1 -0
  70. package/build/constants/api.js.map +1 -1
  71. package/build/constants/mcp.d.ts +1 -1
  72. package/build/constants/mcp.d.ts.map +1 -1
  73. package/build/constants/mcp.js +1 -1
  74. package/build/constants/mcp.js.map +1 -1
  75. package/build/core/capabilities/capabilityAggregator.d.ts.map +1 -1
  76. package/build/core/capabilities/capabilityAggregator.js +33 -14
  77. package/build/core/capabilities/capabilityAggregator.js.map +1 -1
  78. package/build/core/capabilities/capabilityCatalog.d.ts +1 -1
  79. package/build/core/capabilities/capabilityCatalog.d.ts.map +1 -1
  80. package/build/core/capabilities/capabilityCatalog.js +19 -7
  81. package/build/core/capabilities/capabilityCatalog.js.map +1 -1
  82. package/build/core/capabilities/capabilityPagination.d.ts.map +1 -1
  83. package/build/core/capabilities/capabilityPagination.js +7 -4
  84. package/build/core/capabilities/capabilityPagination.js.map +1 -1
  85. package/build/core/capabilities/configuredToolSnapshot.d.ts +34 -0
  86. package/build/core/capabilities/configuredToolSnapshot.d.ts.map +1 -0
  87. package/build/core/capabilities/configuredToolSnapshot.js +137 -0
  88. package/build/core/capabilities/configuredToolSnapshot.js.map +1 -0
  89. package/build/core/capabilities/lazyLoadingOrchestrator.d.ts.map +1 -1
  90. package/build/core/capabilities/lazyLoadingOrchestrator.js +8 -4
  91. package/build/core/capabilities/lazyLoadingOrchestrator.js.map +1 -1
  92. package/build/core/capabilities/metaToolProvider.js +3 -3
  93. package/build/core/capabilities/metaToolProvider.js.map +1 -1
  94. package/build/core/client/clientManager.d.ts.map +1 -1
  95. package/build/core/client/clientManager.js +31 -19
  96. package/build/core/client/clientManager.js.map +1 -1
  97. package/build/core/client/connectionHandler.d.ts.map +1 -1
  98. package/build/core/client/connectionHandler.js +9 -6
  99. package/build/core/client/connectionHandler.js.map +1 -1
  100. package/build/core/client/postAuthOAuthRecovery.d.ts +6 -0
  101. package/build/core/client/postAuthOAuthRecovery.d.ts.map +1 -0
  102. package/build/core/client/postAuthOAuthRecovery.js +74 -0
  103. package/build/core/client/postAuthOAuthRecovery.js.map +1 -0
  104. package/build/core/configChangeHandler.d.ts +5 -0
  105. package/build/core/configChangeHandler.d.ts.map +1 -1
  106. package/build/core/configChangeHandler.js +58 -14
  107. package/build/core/configChangeHandler.js.map +1 -1
  108. package/build/core/instructions/effectiveServerInstructions.d.ts +17 -0
  109. package/build/core/instructions/effectiveServerInstructions.d.ts.map +1 -0
  110. package/build/core/instructions/effectiveServerInstructions.js +12 -0
  111. package/build/core/instructions/effectiveServerInstructions.js.map +1 -0
  112. package/build/core/instructions/instructionAggregator.d.ts +51 -2
  113. package/build/core/instructions/instructionAggregator.d.ts.map +1 -1
  114. package/build/core/instructions/instructionAggregator.js +191 -30
  115. package/build/core/instructions/instructionAggregator.js.map +1 -1
  116. package/build/core/instructions/instructionsDistribution.d.ts +53 -0
  117. package/build/core/instructions/instructionsDistribution.d.ts.map +1 -1
  118. package/build/core/instructions/instructionsDistribution.js +25 -0
  119. package/build/core/instructions/instructionsDistribution.js.map +1 -1
  120. package/build/core/instructions/templateTypes.d.ts +25 -3
  121. package/build/core/instructions/templateTypes.d.ts.map +1 -1
  122. package/build/core/instructions/templateTypes.js +45 -0
  123. package/build/core/instructions/templateTypes.js.map +1 -1
  124. package/build/core/loading/mcpLoadingManager.d.ts.map +1 -1
  125. package/build/core/loading/mcpLoadingManager.js +5 -3
  126. package/build/core/loading/mcpLoadingManager.js.map +1 -1
  127. package/build/core/protocol/requestHandlerUtils.d.ts.map +1 -1
  128. package/build/core/protocol/requestHandlerUtils.js +3 -3
  129. package/build/core/protocol/requestHandlerUtils.js.map +1 -1
  130. package/build/core/protocol/toolRequestHandlers.d.ts.map +1 -1
  131. package/build/core/protocol/toolRequestHandlers.js +18 -5
  132. package/build/core/protocol/toolRequestHandlers.js.map +1 -1
  133. package/build/core/server/backendStdioSupervisor.d.ts.map +1 -1
  134. package/build/core/server/backendStdioSupervisor.js +2 -1
  135. package/build/core/server/backendStdioSupervisor.js.map +1 -1
  136. package/build/core/server/clientInstanceFactory.d.ts +2 -1
  137. package/build/core/server/clientInstanceFactory.d.ts.map +1 -1
  138. package/build/core/server/clientInstanceFactory.js +9 -2
  139. package/build/core/server/clientInstanceFactory.js.map +1 -1
  140. package/build/core/server/clientInstancePool.d.ts +1 -0
  141. package/build/core/server/clientInstancePool.d.ts.map +1 -1
  142. package/build/core/server/clientInstancePool.js +92 -55
  143. package/build/core/server/clientInstancePool.js.map +1 -1
  144. package/build/core/server/clientInstancePoolTypes.d.ts +2 -0
  145. package/build/core/server/clientInstancePoolTypes.d.ts.map +1 -1
  146. package/build/core/server/disabledTools.d.ts +2 -0
  147. package/build/core/server/disabledTools.d.ts.map +1 -1
  148. package/build/core/server/disabledTools.js +7 -4
  149. package/build/core/server/disabledTools.js.map +1 -1
  150. package/build/core/server/mcpServerLifecycleManager.d.ts +0 -4
  151. package/build/core/server/mcpServerLifecycleManager.d.ts.map +1 -1
  152. package/build/core/server/mcpServerLifecycleManager.js +18 -40
  153. package/build/core/server/mcpServerLifecycleManager.js.map +1 -1
  154. package/build/core/server/serverManager.d.ts +1 -0
  155. package/build/core/server/serverManager.d.ts.map +1 -1
  156. package/build/core/server/serverManager.js +32 -0
  157. package/build/core/server/serverManager.js.map +1 -1
  158. package/build/core/server/templateIdentity.d.ts +1 -0
  159. package/build/core/server/templateIdentity.d.ts.map +1 -1
  160. package/build/core/server/templateIdentity.js +11 -1
  161. package/build/core/server/templateIdentity.js.map +1 -1
  162. package/build/core/server/templateServerManager.d.ts +13 -1
  163. package/build/core/server/templateServerManager.d.ts.map +1 -1
  164. package/build/core/server/templateServerManager.js +68 -13
  165. package/build/core/server/templateServerManager.js.map +1 -1
  166. package/build/core/server/toolDescriptionOverrides.d.ts +9 -0
  167. package/build/core/server/toolDescriptionOverrides.d.ts.map +1 -0
  168. package/build/core/server/toolDescriptionOverrides.js +44 -0
  169. package/build/core/server/toolDescriptionOverrides.js.map +1 -0
  170. package/build/core/tools/handlers/serverManagementHandler.d.ts +22 -8
  171. package/build/core/tools/handlers/serverManagementHandler.d.ts.map +1 -1
  172. package/build/core/tools/internal/adapters/helpers/configTransformers.d.ts +3 -1
  173. package/build/core/tools/internal/adapters/helpers/configTransformers.d.ts.map +1 -1
  174. package/build/core/tools/internal/schemas/discovery.d.ts +1 -1
  175. package/build/core/tools/internal/schemas/index.d.ts +3 -3
  176. package/build/core/tools/internal/schemas/management.d.ts +2 -2
  177. package/build/core/types/transport.d.ts +39 -6
  178. package/build/core/types/transport.d.ts.map +1 -1
  179. package/build/core/types/transport.js +61 -2
  180. package/build/core/types/transport.js.map +1 -1
  181. package/build/domains/admin/adminConfiguredServerService.d.ts +65 -4
  182. package/build/domains/admin/adminConfiguredServerService.d.ts.map +1 -1
  183. package/build/domains/admin/adminConfiguredServerService.js +292 -49
  184. package/build/domains/admin/adminConfiguredServerService.js.map +1 -1
  185. package/build/domains/admin/adminDomain.d.ts +19 -0
  186. package/build/domains/admin/adminDomain.d.ts.map +1 -1
  187. package/build/domains/admin/adminDomain.js +17 -0
  188. package/build/domains/admin/adminDomain.js.map +1 -1
  189. package/build/domains/admin/adminInstructionPreviewRuntime.d.ts +7 -0
  190. package/build/domains/admin/adminInstructionPreviewRuntime.d.ts.map +1 -0
  191. package/build/domains/admin/adminInstructionPreviewRuntime.js +154 -0
  192. package/build/domains/admin/adminInstructionPreviewRuntime.js.map +1 -0
  193. package/build/domains/admin/adminInstructionTemplateService.d.ts +169 -0
  194. package/build/domains/admin/adminInstructionTemplateService.d.ts.map +1 -0
  195. package/build/domains/admin/adminInstructionTemplateService.js +181 -0
  196. package/build/domains/admin/adminInstructionTemplateService.js.map +1 -0
  197. package/build/domains/admin/configuredToolInventory.d.ts +64 -0
  198. package/build/domains/admin/configuredToolInventory.d.ts.map +1 -0
  199. package/build/domains/admin/configuredToolInventory.js +182 -0
  200. package/build/domains/admin/configuredToolInventory.js.map +1 -0
  201. package/build/domains/admin/runtimeConfiguredToolInspectionService.d.ts +21 -0
  202. package/build/domains/admin/runtimeConfiguredToolInspectionService.d.ts.map +1 -0
  203. package/build/domains/admin/runtimeConfiguredToolInspectionService.js +255 -0
  204. package/build/domains/admin/runtimeConfiguredToolInspectionService.js.map +1 -0
  205. package/build/domains/config-change/configChange.d.ts +1 -1
  206. package/build/domains/config-change/configChange.d.ts.map +1 -1
  207. package/build/domains/config-change/configChange.js +153 -13
  208. package/build/domains/config-change/configChange.js.map +1 -1
  209. package/build/domains/config-change/types.d.ts +30 -2
  210. package/build/domains/config-change/types.d.ts.map +1 -1
  211. package/build/domains/instruction-template/instructionTemplateManager.d.ts +58 -0
  212. package/build/domains/instruction-template/instructionTemplateManager.d.ts.map +1 -0
  213. package/build/domains/instruction-template/instructionTemplateManager.js +167 -0
  214. package/build/domains/instruction-template/instructionTemplateManager.js.map +1 -0
  215. package/build/domains/runtime-targets/runtimeIdentityVerification.d.ts +1 -0
  216. package/build/domains/runtime-targets/runtimeIdentityVerification.d.ts.map +1 -1
  217. package/build/domains/runtime-targets/runtimeIdentityVerification.js +6 -5
  218. package/build/domains/runtime-targets/runtimeIdentityVerification.js.map +1 -1
  219. package/build/transport/http/routes/adminRoutes.d.ts +2 -0
  220. package/build/transport/http/routes/adminRoutes.d.ts.map +1 -1
  221. package/build/transport/http/routes/adminRoutes.js +345 -9
  222. package/build/transport/http/routes/adminRoutes.js.map +1 -1
  223. package/build/transport/http/routes/apiRoutes.d.ts.map +1 -1
  224. package/build/transport/http/routes/apiRoutes.js +2 -0
  225. package/build/transport/http/routes/apiRoutes.js.map +1 -1
  226. package/build/transport/http/routes/inspectRequestContext.d.ts +2 -0
  227. package/build/transport/http/routes/inspectRequestContext.d.ts.map +1 -1
  228. package/build/transport/http/routes/inspectRequestContext.js +2 -2
  229. package/build/transport/http/routes/inspectRequestContext.js.map +1 -1
  230. package/build/transport/http/routes/inspectRoutes.d.ts +9 -0
  231. package/build/transport/http/routes/inspectRoutes.d.ts.map +1 -1
  232. package/build/transport/http/routes/inspectRoutes.js +15 -10
  233. package/build/transport/http/routes/inspectRoutes.js.map +1 -1
  234. package/build/transport/http/routes/instructionsRoutes.d.ts +4 -0
  235. package/build/transport/http/routes/instructionsRoutes.d.ts.map +1 -0
  236. package/build/transport/http/routes/instructionsRoutes.js +147 -0
  237. package/build/transport/http/routes/instructionsRoutes.js.map +1 -0
  238. package/build/transport/http/routes/toolRoutes.d.ts.map +1 -1
  239. package/build/transport/http/routes/toolRoutes.js +5 -4
  240. package/build/transport/http/routes/toolRoutes.js.map +1 -1
  241. package/build/transport/http/server.d.ts.map +1 -1
  242. package/build/transport/http/server.js +16 -0
  243. package/build/transport/http/server.js.map +1 -1
  244. package/build/transport/transportFactory.d.ts.map +1 -1
  245. package/build/transport/transportFactory.js +34 -12
  246. package/build/transport/transportFactory.js.map +1 -1
  247. package/build/utils/validation/discoveredServer.d.ts +15 -0
  248. package/build/utils/validation/discoveredServer.d.ts.map +1 -0
  249. package/build/utils/validation/discoveredServer.js +2 -0
  250. package/build/utils/validation/discoveredServer.js.map +1 -0
  251. package/build/utils/validation/urlDetection.d.ts +4 -5
  252. package/build/utils/validation/urlDetection.d.ts.map +1 -1
  253. package/build/utils/validation/urlDetection.js +94 -25
  254. package/build/utils/validation/urlDetection.js.map +1 -1
  255. package/package.json +7 -2
  256. package/build/admin/assets/admin-BackendLogsWorkspace-CvJs1npx.js +0 -1
  257. package/build/admin/assets/admin-OAuthServicesWorkspace-BswG57Gm.js +0 -1
  258. package/build/admin/assets/admin-PresetAuthoringWorkspace-BIIRs_EN.js +0 -1
  259. package/build/admin/assets/admin-console-Brz9bnhv.css +0 -1
  260. package/build/admin/assets/admin-console-MT8QZmIC.js +0 -3
  261. package/build/admin/assets/admin-icons-D-JtMXSq.js +0 -1
  262. package/build/admin/assets/admin-mantine-DSFdoMaN.js +0 -53
@@ -1,18 +1,10 @@
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';
6
7
  import { z } from 'zod';
7
- const ENV_PLACEHOLDER_PATTERN = /\$\{[^}]+\}|\$[A-Za-z_][A-Za-z0-9_]*/u;
8
- const previewTransportConfigSchema = transportConfigSchema.extend({
9
- url: z
10
- .string()
11
- .refine((value) => ENV_PLACEHOLDER_PATTERN.test(value) || z.string().url().safeParse(value).success, {
12
- message: 'URL must be a valid URL or environment substitution reference.',
13
- })
14
- .optional(),
15
- });
16
8
  const configuredServerCreateSecretSchema = z
17
9
  .object({
18
10
  fieldPath: z.array(z.string()).min(2),
@@ -154,7 +146,7 @@ function createWorkflowValidation(workflow, config) {
154
146
  });
155
147
  }
156
148
  if (config) {
157
- const parsed = previewTransportConfigSchema.safeParse(config);
149
+ const parsed = transportConfigSchema.safeParse(config);
158
150
  if (!parsed.success) {
159
151
  for (const issue of parsed.error.issues) {
160
152
  errors.push({
@@ -472,56 +464,102 @@ export class AdminConfiguredServerService {
472
464
  return this.options.operationService.executeReadOnly({
473
465
  context: input.context,
474
466
  operationName: 'listConfiguredServers',
475
- run: async () => ({ servers: this.readConfiguredServers() }),
467
+ run: async () => this.readConfiguredServers(),
476
468
  });
477
469
  }
478
470
  async getConfiguredServerDetail(input) {
471
+ const configuredState = this.readConfiguredServerState(input.targetName, input.targetSource);
472
+ const source = configuredState.source;
479
473
  const context = {
480
474
  ...input.context,
481
- target: { type: 'configured_server', id: input.targetName },
475
+ target: { type: 'configured_server', id: `${source}:${input.targetName}` },
482
476
  };
483
477
  return this.options.operationService.executeReadOnly({
484
478
  context,
485
479
  operationName: 'getConfiguredServerDetail',
486
480
  run: async () => {
487
- const { currentConfig, serverDefaults } = this.readConfiguredServerState(input.targetName);
488
- const server = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig));
481
+ const { currentConfig, serverDefaults } = configuredState;
482
+ const server = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig), source, fingerprintConfiguredServerTarget(currentConfig), Object.hasOwn(currentConfig, 'instructionOverride') ? currentConfig.instructionOverride : undefined, Object.hasOwn(currentConfig, 'instructionOverride'));
489
483
  return {
490
484
  server,
491
485
  editContract: createConfiguredServerEditContract(server, currentConfig, serverDefaults),
486
+ ...(this.options.readToolInventory
487
+ ? {
488
+ toolInventory: await this.options.readToolInventory({
489
+ targetName: input.targetName,
490
+ source,
491
+ config: currentConfig,
492
+ ...(input.model ? { model: input.model } : {}),
493
+ }),
494
+ }
495
+ : {}),
492
496
  };
493
497
  },
494
498
  });
495
499
  }
500
+ async refreshConfiguredToolInventory(input) {
501
+ const configuredState = this.readConfiguredServerState(input.targetName, input.targetSource);
502
+ const context = {
503
+ ...input.context,
504
+ target: { type: 'configured_server', id: `${configuredState.source}:${input.targetName}` },
505
+ };
506
+ return this.options.operationService.executeReadOnly({
507
+ context,
508
+ operationName: 'refreshConfiguredToolInventory',
509
+ run: async () => {
510
+ const refresh = this.options.refreshToolInventory;
511
+ if (!refresh) {
512
+ if (!this.options.readToolInventory) {
513
+ throw new Error('Configured tool inventory is unavailable');
514
+ }
515
+ return this.options.readToolInventory({
516
+ targetName: input.targetName,
517
+ source: configuredState.source,
518
+ config: configuredState.currentConfig,
519
+ ...(input.model ? { model: input.model } : {}),
520
+ });
521
+ }
522
+ return refresh({
523
+ targetName: input.targetName,
524
+ source: configuredState.source,
525
+ config: configuredState.currentConfig,
526
+ ...(input.model ? { model: input.model } : {}),
527
+ });
528
+ },
529
+ });
530
+ }
496
531
  async previewConfiguredServerEdit(input) {
532
+ const configuredState = this.readConfiguredServerState(input.targetName, input.targetSource);
533
+ const source = configuredState.source;
497
534
  const context = {
498
535
  ...input.context,
499
- target: { type: 'configured_server', id: input.targetName },
536
+ target: { type: 'configured_server', id: `${source}:${input.targetName}` },
500
537
  };
501
- const { currentConfig, serverDefaults } = this.readConfiguredServerState(input.targetName);
538
+ const { currentConfig, serverDefaults } = configuredState;
502
539
  return this.options.operationService.executeDryRun({
503
540
  context,
504
541
  operationName: 'previewConfiguredServerEdit',
505
- run: async () => this.previewConfiguredServerEditResult(input, currentConfig, serverDefaults),
542
+ run: async () => this.previewConfiguredServerEditResult(input, currentConfig, serverDefaults, false, source),
506
543
  });
507
544
  }
508
545
  async applyConfiguredServerEdit(input) {
546
+ const source = this.resolveConfiguredServerSource(input.targetName, input.targetSource);
509
547
  const context = {
510
548
  ...input.context,
511
- target: { type: 'configured_server', id: input.targetName },
549
+ target: { type: 'configured_server', id: `${source}:${input.targetName}` },
512
550
  confirmationFacts: sanitizeApplyConfirmationFacts(input.context.confirmationFacts),
513
551
  };
514
552
  return this.options.operationService.executeMutation({
515
553
  context,
516
554
  operationName: 'applyConfiguredServerEdit',
517
555
  prepare: async () => {
518
- const { currentConfig, serverDefaults } = this.readConfiguredServerState(input.targetName);
519
- const preview = await this.previewConfiguredServerEditResult({ ...input, connectivityCheck: 'auto' }, currentConfig, serverDefaults, true);
556
+ const { currentConfig, serverDefaults } = this.readConfiguredServerState(input.targetName, source);
557
+ const preview = await this.previewConfiguredServerEditResult({ ...input, connectivityCheck: 'auto' }, currentConfig, serverDefaults, true, source);
520
558
  const normalizedEdit = normalizeEditDraft(input.edit);
521
- const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig));
559
+ const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig), source);
522
560
  const applicableEdit = filterApplicableSecretEdits(normalizedEdit.edit, currentReadModel.secretInputs);
523
- const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults);
524
- const proposedReadModel = createConfiguredServerReadModel(preview.proposedTargetName, mergeGlobalAndServerConfig(serverDefaults, proposedConfig));
561
+ const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults, input.targetName);
562
+ const proposedReadModel = createConfiguredServerReadModel(preview.proposedTargetName, mergeGlobalAndServerConfig(serverDefaults, proposedConfig), source);
525
563
  const connectionCritical = preview.diff.some((entry) => entry.riskFlags.includes('connection_critical'));
526
564
  const secretChange = preview.diff.some((entry) => entry.riskFlags.includes('secret'));
527
565
  if (preview.validation.status !== 'valid') {
@@ -549,6 +587,9 @@ export class AdminConfiguredServerService {
549
587
  : []),
550
588
  ...(secretChange ? [{ code: 'secretChangeConfirmed', expected: true }] : []),
551
589
  ...(connectionCritical ? [{ code: 'connectionCriticalConfirmed', expected: true }] : []),
590
+ ...(preview.toolSelection?.requiresZeroEnabledConfirmation
591
+ ? [{ code: 'zeroEnabledToolsConfirmed', expected: true }]
592
+ : []),
552
593
  ...(preview.connectivityCheck.status === 'failed'
553
594
  ? [{ code: 'connectivityFailureOverrideConfirmed', expected: true }]
554
595
  : []),
@@ -556,6 +597,7 @@ export class AdminConfiguredServerService {
556
597
  run: async () => {
557
598
  const configChange = await this.options.configChangeService.editConfiguredServerTarget({
558
599
  sourceName: input.targetName,
600
+ targetSource: source,
559
601
  targetName: preview.proposedTargetName,
560
602
  serverConfig: proposedConfig,
561
603
  expectedSourceFingerprint: fingerprintConfiguredServerTarget(currentConfig),
@@ -584,17 +626,22 @@ export class AdminConfiguredServerService {
584
626
  }
585
627
  async setConfiguredServerEnabledState(input, enabled) {
586
628
  const operationName = enabled ? 'enableConfiguredServer' : 'disableConfiguredServer';
629
+ const source = input.targetSource ?? 'mcpServers';
587
630
  const context = {
588
631
  ...input.context,
589
- target: { type: 'configured_server', id: input.targetName },
632
+ target: { type: 'configured_server', id: `${source}:${input.targetName}` },
590
633
  };
591
634
  if (input.dryRun) {
592
635
  return this.options.operationService.executeDryRun({
593
636
  context,
594
637
  operationName,
595
638
  run: async () => {
639
+ if (source !== 'mcpServers') {
640
+ throw new Error('Template Server definitions do not support enable or disable operations');
641
+ }
596
642
  const configChange = await this.options.configChangeService.previewConfiguredServerTargetEnabledState({
597
643
  targetName: input.targetName,
644
+ targetSource: source,
598
645
  enabled,
599
646
  backup: 'skip',
600
647
  });
@@ -614,8 +661,12 @@ export class AdminConfiguredServerService {
614
661
  operationName,
615
662
  confirmationRequirements: input.confirmationRequirements,
616
663
  run: async () => {
664
+ if (source !== 'mcpServers') {
665
+ throw new Error('Template Server definitions do not support enable or disable operations');
666
+ }
617
667
  const configChange = await this.options.configChangeService.setConfiguredServerTargetEnabledState({
618
668
  targetName: input.targetName,
669
+ targetSource: source,
619
670
  enabled,
620
671
  backup: 'required',
621
672
  });
@@ -632,49 +683,84 @@ export class AdminConfiguredServerService {
632
683
  readConfiguredServers() {
633
684
  const parsed = this.options.readConfigDocument();
634
685
  if (!parsed) {
635
- return [];
686
+ return { servers: [], configFingerprint: fingerprintConfiguredServerConfigDocument({}) };
636
687
  }
637
- return Object.entries(parsed.mcpServers ?? {}).map(([name, serverConfig]) => createConfiguredServerReadModel(name, mergeGlobalAndServerConfig(parsed.serverDefaults, serverConfig)));
688
+ return {
689
+ configFingerprint: fingerprintConfiguredServerConfigDocument(parsed),
690
+ 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')))),
691
+ };
638
692
  }
639
- readConfiguredServerState(targetName) {
693
+ readConfiguredServerState(targetName, targetSource) {
640
694
  const parsed = this.options.readConfigDocument();
641
- const currentConfig = parsed?.mcpServers?.[targetName];
695
+ const source = this.resolveConfiguredServerSource(targetName, targetSource, parsed);
696
+ const currentConfig = parsed?.[source]?.[targetName];
642
697
  if (!currentConfig) {
643
698
  throw new AdminConfiguredServerNotFoundError(targetName);
644
699
  }
645
- return { currentConfig, serverDefaults: parsed?.serverDefaults };
700
+ return { currentConfig, serverDefaults: parsed?.serverDefaults, source };
701
+ }
702
+ resolveConfiguredServerSource(targetName, targetSource, document = this.options.readConfigDocument()) {
703
+ if (targetSource)
704
+ return targetSource;
705
+ return document?.mcpTemplates?.[targetName] ? 'mcpTemplates' : 'mcpServers';
646
706
  }
647
707
  readConfiguredServerConfig(targetName) {
648
708
  const parsed = this.options.readConfigDocument();
649
- const currentConfig = parsed?.mcpServers?.[targetName];
709
+ const currentConfig = parsed?.mcpTemplates?.[targetName] ?? parsed?.mcpServers?.[targetName];
650
710
  if (!currentConfig) {
651
711
  throw new AdminConfiguredServerNotFoundError(targetName);
652
712
  }
653
713
  return currentConfig;
654
714
  }
655
- async previewConfiguredServerEditResult(input, currentConfig, serverDefaults, requireCriticalConnectivity = false) {
715
+ async previewConfiguredServerEditResult(input, currentConfig, serverDefaults, requireCriticalConnectivity = false, source = 'mcpServers') {
656
716
  const normalizedEdit = normalizeEditDraft(input.edit);
657
- const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig));
717
+ const sourceValidation = validateEditForSource(source, normalizedEdit.edit);
718
+ const currentReadModel = createConfiguredServerReadModel(input.targetName, mergeGlobalAndServerConfig(serverDefaults, currentConfig), source);
658
719
  const secretValidation = validateSecretEditCapabilities(normalizedEdit.edit, currentReadModel.secretInputs);
659
720
  const transportApplicabilityValidation = validateTransportFieldApplicability(currentConfig, normalizedEdit.edit);
660
721
  const overrideValidation = validateTransportOverrideClears(currentConfig, normalizedEdit.edit);
661
722
  const applicableEdit = filterApplicableSecretEdits(normalizedEdit.edit, currentReadModel.secretInputs);
662
723
  const proposedTargetName = applicableEdit.id?.trim() || input.targetName;
663
- const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults);
664
- const proposedReadModel = createConfiguredServerReadModel(proposedTargetName, mergeGlobalAndServerConfig(serverDefaults, proposedConfig));
724
+ const proposedConfig = applyEditDraft(currentConfig, applicableEdit, serverDefaults, input.targetName);
725
+ const proposedReadModel = createConfiguredServerReadModel(proposedTargetName, mergeGlobalAndServerConfig(serverDefaults, proposedConfig), source);
665
726
  const proposedTransportType = configuredTransportType(proposedConfig);
666
727
  const serverValidation = validatePreviewServerConfig(proposedConfig);
667
- const validation = mergeValidation(mergeValidation(mergeValidation(mergeValidation(normalizedEdit.validation, secretValidation), transportApplicabilityValidation), overrideValidation), serverValidation);
728
+ const currentToolInventory = this.options.readToolInventory
729
+ ? await this.options.readToolInventory({
730
+ targetName: input.targetName,
731
+ source,
732
+ config: currentConfig,
733
+ ...(input.model ? { model: input.model } : {}),
734
+ })
735
+ : undefined;
736
+ const toolEditValidation = validateConfiguredToolEdit(applicableEdit, currentToolInventory);
737
+ const validation = mergeValidation(mergeValidation(mergeValidation(mergeValidation(normalizedEdit.validation, secretValidation), transportApplicabilityValidation), overrideValidation), mergeValidation(mergeValidation(serverValidation, sourceValidation), toolEditValidation));
668
738
  const diff = createPreviewDiff(currentReadModel, proposedReadModel, applicableEdit);
739
+ pushDiff(diff, ['disabledTools'], currentConfig.disabledTools ?? [], proposedConfig.disabledTools ?? [], [
740
+ 'tool_visibility',
741
+ ]);
742
+ pushDiff(diff, ['toolDescriptionOverrides'], currentConfig.toolDescriptionOverrides ?? {}, proposedConfig.toolDescriptionOverrides ?? {}, ['tool_metadata']);
669
743
  appendClearedOverrideDiff(diff, currentReadModel, proposedReadModel, applicableEdit);
670
744
  const changed = validation.status === 'valid' && diff.length > 0;
671
745
  const endpointChangedWithPreservedSecrets = endpointAuthorityChanged(currentConfig, proposedConfig) &&
672
746
  hasPreservedSecretInputs(currentReadModel.secretInputs.filter((input) => !proposedTransportType || transportFieldAppliesToType(input.fieldPath[0], proposedTransportType)), applicableEdit);
747
+ const proposedToolInventory = this.options.readToolInventory
748
+ ? await this.options.readToolInventory({
749
+ targetName: proposedTargetName,
750
+ source,
751
+ config: proposedConfig,
752
+ ...(input.model ? { model: input.model } : {}),
753
+ })
754
+ : undefined;
755
+ const toolSelection = currentToolInventory && proposedToolInventory
756
+ ? createConfiguredToolSelectionPreview(currentToolInventory, proposedToolInventory)
757
+ : undefined;
673
758
  return {
674
759
  targetName: input.targetName,
675
760
  proposedTargetName,
676
761
  previewFingerprint: previewFingerprint({
677
762
  targetName: input.targetName,
763
+ targetSource: source,
678
764
  sourceFingerprint: fingerprintConfiguredServerTarget(currentConfig),
679
765
  globalConfigFingerprint: fingerprintConfiguredServerDefaults(serverDefaults),
680
766
  edit: normalizedEdit.edit,
@@ -682,10 +768,11 @@ export class AdminConfiguredServerService {
682
768
  proposed: proposedReadModel,
683
769
  diff,
684
770
  validation,
771
+ toolSelection,
685
772
  }),
686
773
  validation,
687
774
  diff,
688
- configChange: previewConfigChange(input.targetName, changed),
775
+ configChange: previewConfigChange(input.targetName, changed, source),
689
776
  connectivityCheck: await this.previewConnectivityCheck({
690
777
  targetName: proposedTargetName,
691
778
  serverConfig: proposedConfig,
@@ -696,6 +783,7 @@ export class AdminConfiguredServerService {
696
783
  (requireCriticalConnectivity && diff.some((entry) => entry.riskFlags.includes('connection_critical'))),
697
784
  endpointChangedWithPreservedSecrets,
698
785
  }),
786
+ ...(toolSelection ? { toolSelection } : {}),
699
787
  };
700
788
  }
701
789
  async previewConnectivityCheck(input) {
@@ -787,6 +875,20 @@ export class AdminConfiguredServerService {
787
875
  };
788
876
  }
789
877
  }
878
+ function validateEditForSource(source, edit) {
879
+ if (source !== 'mcpTemplates')
880
+ return { status: 'valid', errors: [] };
881
+ const supportedTemplateMetadata = new Set(['instructionOverride', 'disabledTools', 'toolDescriptionOverrides']);
882
+ const unsupported = Object.keys(edit).filter((key) => !supportedTemplateMetadata.has(key));
883
+ return {
884
+ status: unsupported.length === 0 ? 'valid' : 'invalid',
885
+ errors: unsupported.map((key) => ({
886
+ fieldPath: [key],
887
+ code: 'template_edit_field_unsupported',
888
+ message: 'Template Server edits may only change instruction and tool metadata.',
889
+ })),
890
+ };
891
+ }
790
892
  function assertSuccessfulConfigChange(configChange) {
791
893
  if (configChange.reload.status === 'failed') {
792
894
  throw new Error(`Config reload observation failed: ${configChange.reload.error ?? 'unknown reload failure'}`);
@@ -831,6 +933,7 @@ function sanitizeApplyConfirmationFacts(facts) {
831
933
  return undefined;
832
934
  const allowed = ['previewConfirmed', 'targetNameConfirmed', 'secretChangeConfirmed', 'connectionCriticalConfirmed'];
833
935
  allowed.push('connectivityFailureOverrideConfirmed');
936
+ allowed.push('zeroEnabledToolsConfirmed');
834
937
  const sanitized = Object.fromEntries(allowed.filter((key) => key in facts).map((key) => [key, facts[key]]));
835
938
  return Object.keys(sanitized).length > 0 ? sanitized : undefined;
836
939
  }
@@ -843,7 +946,17 @@ function mutationOutcome(enabled, changed) {
843
946
  function normalizeEditDraft(value) {
844
947
  const errors = [];
845
948
  const edit = {};
846
- const supportedKeys = new Set(['id', 'enabled', 'tags', 'transport', 'secrets', 'clearTransportOverrides']);
949
+ const supportedKeys = new Set([
950
+ 'id',
951
+ 'enabled',
952
+ 'tags',
953
+ 'transport',
954
+ 'secrets',
955
+ 'clearTransportOverrides',
956
+ 'instructionOverride',
957
+ 'disabledTools',
958
+ 'toolDescriptionOverrides',
959
+ ]);
847
960
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
848
961
  return {
849
962
  edit,
@@ -905,6 +1018,36 @@ function normalizeEditDraft(value) {
905
1018
  });
906
1019
  }
907
1020
  }
1021
+ if (record.disabledTools !== undefined) {
1022
+ if (Array.isArray(record.disabledTools) && record.disabledTools.every((name) => typeof name === 'string')) {
1023
+ edit.disabledTools = Array.from(new Set(record.disabledTools.map((name) => name.trim()).filter((name) => name.length > 0))).sort((left, right) => left.localeCompare(right));
1024
+ }
1025
+ else {
1026
+ errors.push({
1027
+ fieldPath: ['disabledTools'],
1028
+ code: 'invalid_disabled_tools',
1029
+ message: 'Disabled tools must be a list of logical tool names.',
1030
+ });
1031
+ }
1032
+ }
1033
+ if (record.toolDescriptionOverrides !== undefined) {
1034
+ if (record.toolDescriptionOverrides &&
1035
+ typeof record.toolDescriptionOverrides === 'object' &&
1036
+ !Array.isArray(record.toolDescriptionOverrides) &&
1037
+ Object.entries(record.toolDescriptionOverrides).every(([name, description]) => name.length > 0 && name === name.trim() && typeof description === 'string')) {
1038
+ edit.toolDescriptionOverrides = Object.fromEntries(Object.entries(record.toolDescriptionOverrides)
1039
+ .map(([name, description]) => [name, description.trim()])
1040
+ .filter(([name, description]) => name.length > 0 && description.length > 0)
1041
+ .sort(([left], [right]) => left.localeCompare(right)));
1042
+ }
1043
+ else {
1044
+ errors.push({
1045
+ fieldPath: ['toolDescriptionOverrides'],
1046
+ code: 'invalid_tool_description_overrides',
1047
+ message: 'Tool description overrides must map logical tool names to descriptions.',
1048
+ });
1049
+ }
1050
+ }
908
1051
  if (record.transport !== undefined) {
909
1052
  if (record.transport && typeof record.transport === 'object' && !Array.isArray(record.transport)) {
910
1053
  edit.transport = normalizeTransportEditDraft(record.transport, errors);
@@ -943,6 +1086,23 @@ function normalizeEditDraft(value) {
943
1086
  });
944
1087
  }
945
1088
  }
1089
+ if (record.instructionOverride !== undefined) {
1090
+ const parsed = z
1091
+ .discriminatedUnion('action', [
1092
+ z.object({ action: z.literal('set'), value: z.string() }).strict(),
1093
+ z.object({ action: z.literal('remove') }).strict(),
1094
+ ])
1095
+ .safeParse(record.instructionOverride);
1096
+ if (parsed.success)
1097
+ edit.instructionOverride = parsed.data;
1098
+ else {
1099
+ errors.push({
1100
+ fieldPath: ['instructionOverride'],
1101
+ code: 'invalid_instruction_override',
1102
+ message: 'Instruction override must set a string value or remove the configured override.',
1103
+ });
1104
+ }
1105
+ }
946
1106
  return {
947
1107
  edit,
948
1108
  validation: { status: errors.length === 0 ? 'valid' : 'invalid', errors },
@@ -971,6 +1131,20 @@ function normalizeTransportEditDraft(record, errors) {
971
1131
  }
972
1132
  return transport;
973
1133
  }
1134
+ function validateConfiguredToolEdit(edit, inventory) {
1135
+ if (!inventory)
1136
+ return { status: 'valid', errors: [] };
1137
+ const allowedNames = new Set(inventory.rows.map((row) => row.name));
1138
+ const requestedNames = new Set([...(edit.disabledTools ?? []), ...Object.keys(edit.toolDescriptionOverrides ?? {})]);
1139
+ const errors = Array.from(requestedNames)
1140
+ .filter((name) => !allowedNames.has(name))
1141
+ .map((name) => ({
1142
+ fieldPath: ['tools', name],
1143
+ code: 'unknown_configured_tool',
1144
+ message: 'Tool changes must refer to an observed tool or a retained unresolved entry.',
1145
+ }));
1146
+ return { status: errors.length === 0 ? 'valid' : 'invalid', errors };
1147
+ }
974
1148
  function isSecretCapableRawTransportEdit(key, value) {
975
1149
  if (key === 'url') {
976
1150
  return isSecretCapableUrlEdit(value);
@@ -1267,7 +1441,7 @@ function potentialSecretInput(fieldPath) {
1267
1441
  }
1268
1442
  return undefined;
1269
1443
  }
1270
- function applyEditDraft(currentConfig, edit, serverDefaults) {
1444
+ function applyEditDraft(currentConfig, edit, serverDefaults, logicalServerName) {
1271
1445
  const nextConfig = cloneServerConfig(currentConfig);
1272
1446
  for (const key of edit.clearTransportOverrides ?? []) {
1273
1447
  delete nextConfig[key];
@@ -1283,6 +1457,27 @@ function applyEditDraft(currentConfig, edit, serverDefaults) {
1283
1457
  if (Array.isArray(edit.tags)) {
1284
1458
  nextConfig.tags = edit.tags.filter((tag) => typeof tag === 'string');
1285
1459
  }
1460
+ if (edit.instructionOverride?.action === 'set') {
1461
+ nextConfig.instructionOverride = edit.instructionOverride.value;
1462
+ }
1463
+ else if (edit.instructionOverride?.action === 'remove') {
1464
+ delete nextConfig.instructionOverride;
1465
+ }
1466
+ if (Array.isArray(edit.disabledTools)) {
1467
+ const disabledTools = logicalServerName
1468
+ ? normalizeDisabledToolsForServer(logicalServerName, edit.disabledTools)
1469
+ : edit.disabledTools;
1470
+ if (disabledTools.length === 0)
1471
+ delete nextConfig.disabledTools;
1472
+ else
1473
+ nextConfig.disabledTools = disabledTools;
1474
+ }
1475
+ if (edit.toolDescriptionOverrides) {
1476
+ if (Object.keys(edit.toolDescriptionOverrides).length === 0)
1477
+ delete nextConfig.toolDescriptionOverrides;
1478
+ else
1479
+ nextConfig.toolDescriptionOverrides = { ...edit.toolDescriptionOverrides };
1480
+ }
1286
1481
  if (edit.transport && typeof edit.transport === 'object') {
1287
1482
  const currentType = configuredTransportType(currentConfig);
1288
1483
  const proposedType = configuredTransportType({ ...currentConfig, ...edit.transport });
@@ -1584,7 +1779,7 @@ function validatePreviewServerConfig(serverConfig) {
1584
1779
  const type = typeof serverConfig.type === 'string' ? serverConfig.type : undefined;
1585
1780
  const url = typeof serverConfig.url === 'string' ? serverConfig.url : undefined;
1586
1781
  const command = typeof serverConfig.command === 'string' ? serverConfig.command : undefined;
1587
- const schemaResult = previewTransportConfigSchema.safeParse(serverConfig);
1782
+ const schemaResult = transportConfigSchema.safeParse(serverConfig);
1588
1783
  if (!schemaResult.success) {
1589
1784
  for (const issue of schemaResult.error.issues) {
1590
1785
  if ((issue.path[0] === 'type' && typeof serverConfig.type === 'string') ||
@@ -1666,12 +1861,12 @@ function isValidUrlOrEnvReference(value) {
1666
1861
  return false;
1667
1862
  }
1668
1863
  }
1669
- function previewConfigChange(targetName, changed) {
1864
+ function previewConfigChange(targetName, changed, source = 'mcpServers') {
1670
1865
  return {
1671
1866
  status: changed ? 'changed' : 'unchanged',
1672
1867
  operation: 'set_static',
1673
1868
  configPath: '[redacted]',
1674
- target: { name: targetName, source: 'mcpServers' },
1869
+ target: { name: targetName, source },
1675
1870
  changed,
1676
1871
  backup: { created: false },
1677
1872
  retentionCleanup: { attempted: false, deletedPaths: [], warnings: [] },
@@ -1684,6 +1879,7 @@ function previewFingerprint(input) {
1684
1879
  .update(stableStringify({
1685
1880
  schemaVersion: 2,
1686
1881
  targetName: input.targetName,
1882
+ targetSource: input.targetSource,
1687
1883
  sourceFingerprint: input.sourceFingerprint,
1688
1884
  globalConfigFingerprint: input.globalConfigFingerprint,
1689
1885
  edit: redactEditDraftForFingerprint(input.edit),
@@ -1691,9 +1887,39 @@ function previewFingerprint(input) {
1691
1887
  proposed: input.proposed,
1692
1888
  diff: input.diff,
1693
1889
  validation: input.validation,
1890
+ toolSelection: input.toolSelection,
1694
1891
  }))
1695
1892
  .digest('hex')}`;
1696
1893
  }
1894
+ function createConfiguredToolSelectionPreview(current, proposed) {
1895
+ const currentRows = new Map(current.rows.map((row) => [row.name, row]));
1896
+ const proposedRows = new Map(proposed.rows.map((row) => [row.name, row]));
1897
+ const changedTools = Array.from(new Set([...currentRows.keys(), ...proposedRows.keys()]))
1898
+ .filter((name) => {
1899
+ const before = currentRows.get(name);
1900
+ const after = proposedRows.get(name);
1901
+ return (before?.enabled !== after?.enabled ||
1902
+ before?.descriptionOverride !== after?.descriptionOverride ||
1903
+ before?.effectiveDescription !== after?.effectiveDescription);
1904
+ })
1905
+ .sort((left, right) => left.localeCompare(right));
1906
+ return {
1907
+ capabilityGeneration: current.generation,
1908
+ model: proposed.model,
1909
+ changedTools,
1910
+ counts: proposed.counts,
1911
+ approximateTokens: {
1912
+ before: current.approximateTokens.enabled,
1913
+ after: proposed.approximateTokens.enabled,
1914
+ savings: current.approximateTokens.enabled - proposed.approximateTokens.enabled,
1915
+ },
1916
+ targetEnabled: proposed.targetEnabled,
1917
+ effect: proposed.targetEnabled ? 'immediate' : 'deferred_until_target_enabled',
1918
+ requiresZeroEnabledConfirmation: changedTools.length > 0 &&
1919
+ current.rows.some((row) => row.observed && row.enabled) &&
1920
+ !proposed.rows.some((row) => row.observed && row.enabled),
1921
+ };
1922
+ }
1697
1923
  function redactEditDraftForFingerprint(edit) {
1698
1924
  return {
1699
1925
  ...edit,
@@ -1721,6 +1947,9 @@ function createPreviewDiff(current, proposed, edit) {
1721
1947
  pushDiff(diff, ['id'], current.id, proposed.id, proposed.id === current.id ? [] : ['rename']);
1722
1948
  pushDiff(diff, ['enabled'], current.enabled, proposed.enabled, ['connection_critical']);
1723
1949
  pushDiff(diff, ['tags'], current.tags, proposed.tags, []);
1950
+ if (edit.instructionOverride) {
1951
+ pushDiff(diff, ['instructionOverride'], current.instructionOverride, proposed.instructionOverride, []);
1952
+ }
1724
1953
  const secretEditedTopLevelKeys = new Set((edit.secrets ?? []).map((secret) => secret.fieldPath[0]));
1725
1954
  const transportEditedKeys = new Set(Object.keys(edit.transport ?? {}));
1726
1955
  const transportKeys = new Set([...Object.keys(current.transport), ...Object.keys(proposed.transport)]);
@@ -1857,11 +2086,15 @@ function stableStringify(value) {
1857
2086
  }
1858
2087
  return JSON.stringify(value);
1859
2088
  }
1860
- function createConfiguredServerReadModel(name, serverConfig) {
2089
+ function createConfiguredServerReadModel(name, serverConfig, source = 'mcpServers', revision = fingerprintConfiguredServerTarget(serverConfig), instructionOverride = serverConfig.instructionOverride, hasInstructionOverride = Object.hasOwn(serverConfig, 'instructionOverride')) {
1861
2090
  const secretInputs = [];
1862
2091
  const transport = {};
1863
2092
  for (const [key, value] of Object.entries(serverConfig)) {
1864
- if (key === 'disabled' || key === 'tags') {
2093
+ if (key === 'disabled' ||
2094
+ key === 'tags' ||
2095
+ key === 'instructionOverride' ||
2096
+ key === 'disabledTools' ||
2097
+ key === 'toolDescriptionOverrides') {
1865
2098
  continue;
1866
2099
  }
1867
2100
  if (key === 'env') {
@@ -1889,22 +2122,33 @@ function createConfiguredServerReadModel(name, serverConfig) {
1889
2122
  const enabled = !isConfiguredServerTargetDisabled(serverConfig.disabled);
1890
2123
  return {
1891
2124
  id: name,
1892
- source: 'mcpServers',
2125
+ source,
1893
2126
  target: {
1894
2127
  type: 'configured_server',
1895
2128
  id: name,
1896
- source: 'mcpServers',
2129
+ source,
1897
2130
  },
2131
+ revision,
1898
2132
  enabled,
1899
2133
  tags: normalizeTags(serverConfig.tags),
1900
2134
  transportSummary: createTransportSummary(serverConfig, transport),
1901
2135
  mutationAvailability: {
1902
- available: true,
1903
- operations: ['enable', 'disable'],
2136
+ available: source === 'mcpServers',
2137
+ operations: source === 'mcpServers' ? ['enable', 'disable'] : [],
1904
2138
  },
1905
- actionState: createActionState(name, enabled),
2139
+ actionState: source === 'mcpServers'
2140
+ ? createActionState(name, enabled)
2141
+ : {
2142
+ enable: { available: false, label: `Enable ${name}`, disabledReason: 'already_enabled' },
2143
+ disable: { available: false, label: `Disable ${name}`, disabledReason: 'already_disabled' },
2144
+ },
1906
2145
  transport,
1907
2146
  secretInputs,
2147
+ instructionOverride: !hasInstructionOverride
2148
+ ? { state: 'upstream' }
2149
+ : instructionOverride === ''
2150
+ ? { state: 'suppress', value: '' }
2151
+ : { state: 'replace', value: instructionOverride },
1908
2152
  };
1909
2153
  }
1910
2154
  const STDIO_TRANSPORT_TYPES = ['stdio'];
@@ -1919,7 +2163,6 @@ const TRANSPORT_FIELD_DEFINITIONS = [
1919
2163
  { key: 'timeout', label: 'Deprecated Timeout', control: 'number' },
1920
2164
  { key: 'connectionTimeout', label: 'Connection Timeout', control: 'number' },
1921
2165
  { key: 'requestTimeout', label: 'Request Timeout', control: 'number' },
1922
- { key: 'disabledTools', label: 'Disabled Tools', control: 'string-list', defaultValue: [] },
1923
2166
  { key: 'template', label: 'Template', control: 'record', defaultValue: {} },
1924
2167
  {
1925
2168
  key: 'url',