@aifabrix/builder 2.53.1 → 2.54.1

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 (321) hide show
  1. package/bin/aifabrix.js +2 -2
  2. package/lib/api/assistants.api.js +8 -5
  3. package/lib/api/channel-identity.api.js +41 -0
  4. package/lib/api/evidence-authoring.api.js +5 -1
  5. package/lib/api/evidence.api.js +2 -0
  6. package/lib/api/types/evidence.types.js +2 -0
  7. package/lib/api/work-search.api.js +136 -0
  8. package/lib/channels/add-channel.js +163 -126
  9. package/lib/channels/approval-guides/chatgpt.js +14 -18
  10. package/lib/channels/approval-guides/claude.js +29 -0
  11. package/lib/channels/approval-guides/index.js +88 -22
  12. package/lib/channels/approval-guides/mcp-client.js +27 -0
  13. package/lib/channels/approval-guides/microsoft-copilot.js +52 -3
  14. package/lib/channels/approval-guides/slack.js +10 -10
  15. package/lib/channels/approval-guides/teams-bot.js +8 -12
  16. package/lib/channels/channel-add-continuations.js +91 -0
  17. package/lib/channels/channel-add-ensure.js +124 -27
  18. package/lib/channels/channel-add-existing.js +160 -28
  19. package/lib/channels/channel-add-failure.js +60 -0
  20. package/lib/channels/channel-add-remaining.js +486 -0
  21. package/lib/channels/channel-add-tty.js +190 -0
  22. package/lib/channels/channel-add-wizard-config.js +138 -0
  23. package/lib/channels/channel-add-wizard.js +250 -0
  24. package/lib/channels/channel-artifact-generator.js +27 -13
  25. package/lib/channels/channel-cardinality.js +83 -0
  26. package/lib/channels/channel-catalog-icon.js +45 -0
  27. package/lib/channels/channel-cli-instructions.js +75 -0
  28. package/lib/channels/channel-configuration-map.js +213 -0
  29. package/lib/channels/channel-credential.js +11 -14
  30. package/lib/channels/channel-definition-project.js +101 -0
  31. package/lib/channels/channel-definition-registry.js +154 -0
  32. package/lib/channels/channel-description.js +59 -2
  33. package/lib/channels/channel-discovery.js +16 -2
  34. package/lib/channels/channel-external-system.js +86 -9
  35. package/lib/channels/channel-install-envelope-actions.js +88 -0
  36. package/lib/channels/channel-install-envelope-microsoft.js +155 -0
  37. package/lib/channels/channel-install-envelope.js +356 -0
  38. package/lib/channels/channel-install-help-md.js +172 -0
  39. package/lib/channels/channel-install-http-payload.js +62 -0
  40. package/lib/channels/channel-install-inventory.js +115 -0
  41. package/lib/channels/channel-install-profiles.js +90 -0
  42. package/lib/channels/channel-instance-collision.js +118 -0
  43. package/lib/channels/channel-instance-key.js +100 -0
  44. package/lib/channels/channel-instance-materialize.js +240 -0
  45. package/lib/channels/channel-integration-generator.js +16 -9
  46. package/lib/channels/channel-integration-template.js +101 -22
  47. package/lib/channels/channel-inventory-fetch.js +79 -0
  48. package/lib/channels/channel-oauth-preflight.js +104 -0
  49. package/lib/channels/channel-package-version.js +76 -0
  50. package/lib/channels/channel-predefined-client.js +154 -0
  51. package/lib/channels/channel-probe.js +7 -2
  52. package/lib/channels/channel-runtime-resolver.js +180 -72
  53. package/lib/channels/channel-status-display.js +86 -16
  54. package/lib/channels/channel-targets.js +106 -92
  55. package/lib/channels/chatgpt-callback.js +318 -0
  56. package/lib/channels/chatgpt-channel-add.js +229 -0
  57. package/lib/channels/chatgpt-preflight.js +396 -0
  58. package/lib/channels/chatgpt-runtime-contract.js +17 -0
  59. package/lib/channels/chatgpt-setup-values.js +124 -0
  60. package/lib/channels/claude-callback.js +67 -0
  61. package/lib/channels/claude-channel-add.js +254 -0
  62. package/lib/channels/claude-runtime-contract.js +40 -0
  63. package/lib/channels/claude-runtime.js +58 -0
  64. package/lib/channels/claude-setup-values.js +89 -0
  65. package/lib/channels/mcp-client/config-merge.js +148 -0
  66. package/lib/channels/mcp-client/ensure-client.js +231 -0
  67. package/lib/channels/mcp-client/paths.js +53 -0
  68. package/lib/channels/mcp-client/profiles.js +122 -0
  69. package/lib/channels/mcp-client/redirect-uris.js +99 -0
  70. package/lib/channels/mcp-client/runtime.js +59 -0
  71. package/lib/channels/mcp-client-channel-add.js +186 -0
  72. package/lib/channels/microsoft-copilot-channel-add.js +117 -7
  73. package/lib/channels/microsoft-copilot-entra-identifier-uris.js +187 -0
  74. package/lib/channels/microsoft-copilot-entra-sso-form.js +106 -18
  75. package/lib/channels/microsoft-copilot-entra-sso.js +31 -2
  76. package/lib/channels/microsoft-copilot-package-prior.js +107 -0
  77. package/lib/channels/microsoft-copilot-package.js +29 -28
  78. package/lib/channels/microsoft-provisioning/a365.js +80 -0
  79. package/lib/channels/microsoft-provisioning/auth-config.js +240 -0
  80. package/lib/channels/microsoft-provisioning/auth.js +179 -0
  81. package/lib/channels/microsoft-provisioning/cli.js +71 -0
  82. package/lib/channels/microsoft-provisioning/constants.js +75 -0
  83. package/lib/channels/microsoft-provisioning/errors.js +117 -0
  84. package/lib/channels/microsoft-provisioning/index.js +55 -0
  85. package/lib/channels/microsoft-provisioning/install-atk.js +158 -0
  86. package/lib/channels/microsoft-provisioning/install-teams.js +115 -0
  87. package/lib/channels/microsoft-provisioning/login-portal-device.js +236 -0
  88. package/lib/channels/microsoft-provisioning/login-portal-parse.js +71 -0
  89. package/lib/channels/microsoft-provisioning/login-portal-teams.js +193 -0
  90. package/lib/channels/microsoft-provisioning/login-portal.js +230 -0
  91. package/lib/channels/microsoft-provisioning/persist.js +110 -0
  92. package/lib/channels/microsoft-provisioning/teams-bot-args.js +119 -0
  93. package/lib/channels/microsoft-provisioning/teams-bot-controller.js +123 -0
  94. package/lib/channels/microsoft-provisioning/teams-bot-parse.js +134 -0
  95. package/lib/channels/microsoft-provisioning/teams-bot-persist.js +113 -0
  96. package/lib/channels/microsoft-provisioning/teams-bot-provision.js +234 -0
  97. package/lib/channels/microsoft-provisioning/teams-bot-secrets.js +45 -0
  98. package/lib/channels/microsoft-provisioning/teams-cli.js +82 -0
  99. package/lib/channels/runtime-tool-catalog.js +5 -2
  100. package/lib/channels/slack-channel-add-portal.js +130 -0
  101. package/lib/channels/slack-channel-add-tty-auth.js +95 -0
  102. package/lib/channels/slack-channel-add.js +258 -0
  103. package/lib/channels/slack-provisioning/apps.js +181 -0
  104. package/lib/channels/slack-provisioning/auth-store.js +110 -0
  105. package/lib/channels/slack-provisioning/auth-ticket.js +185 -0
  106. package/lib/channels/slack-provisioning/auth.js +115 -0
  107. package/lib/channels/slack-provisioning/callbacks.js +41 -0
  108. package/lib/channels/slack-provisioning/cli.js +226 -0
  109. package/lib/channels/slack-provisioning/constants.js +37 -0
  110. package/lib/channels/slack-provisioning/credentials.js +169 -0
  111. package/lib/channels/slack-provisioning/curl-shim.js +132 -0
  112. package/lib/channels/slack-provisioning/developer-install.js +118 -0
  113. package/lib/channels/slack-provisioning/env-file.js +120 -0
  114. package/lib/channels/slack-provisioning/errors.js +47 -0
  115. package/lib/channels/slack-provisioning/go-ca-bundle.js +137 -0
  116. package/lib/channels/slack-provisioning/index.js +250 -0
  117. package/lib/channels/slack-provisioning/install-cli.js +323 -0
  118. package/lib/channels/slack-provisioning/manifest.js +89 -0
  119. package/lib/channels/slack-provisioning/oauth-lock.js +33 -0
  120. package/lib/channels/slack-provisioning/offer-cli.js +113 -0
  121. package/lib/channels/slack-provisioning/onboard-members.js +122 -0
  122. package/lib/channels/slack-provisioning/onboard.js +89 -0
  123. package/lib/channels/slack-provisioning/persist.js +95 -0
  124. package/lib/channels/slack-provisioning/remaining-urls.js +158 -0
  125. package/lib/channels/slack-provisioning/with-curl-on-path.js +48 -0
  126. package/lib/channels/teams-bot-channel-add.js +94 -0
  127. package/lib/channels/vendor-dataplane-url.js +249 -0
  128. package/lib/cli/setup-certification.help.js +2 -0
  129. package/lib/cli/setup-external-system-help.js +2 -0
  130. package/lib/cli/setup-external-system.js +1 -1
  131. package/lib/commands/channel-add-help.js +366 -0
  132. package/lib/commands/channel-add-setup.js +162 -80
  133. package/lib/commands/channel-configure.js +118 -0
  134. package/lib/commands/channel.js +10 -1
  135. package/lib/commands/governance-upload-external.js +6 -3
  136. package/lib/commands/lifecycle-command-action.js +2 -5
  137. package/lib/commands/lifecycle-external.js +93 -21
  138. package/lib/commands/repair-datasource-test-payload.js +4 -2
  139. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +23 -2
  140. package/lib/commands/repair-datasource.js +3 -0
  141. package/lib/commands/repair-external-run.js +59 -25
  142. package/lib/commands/repair-openapi-rest-runtime.js +127 -0
  143. package/lib/commands/repair-orchestration-tag-merge.js +339 -0
  144. package/lib/commands/repair-rbac-catalog-defaults.js +96 -0
  145. package/lib/commands/repair-rbac-platform-preset.js +26 -15
  146. package/lib/commands/repair-rbac-reader-roles.js +8 -21
  147. package/lib/commands/repair-rbac.js +10 -34
  148. package/lib/commands/repair-system-permissions.js +24 -22
  149. package/lib/commands/role-assistant-test-output.js +14 -0
  150. package/lib/commands/role-assistant.js +59 -21
  151. package/lib/commands/test-e2e-external.js +148 -21
  152. package/lib/commands/upload-credential-push-log.js +2 -1
  153. package/lib/commands/upload-publish-context.js +1 -1
  154. package/lib/commands/upload-role-assistant-shell.js +124 -0
  155. package/lib/commands/upload-sidecar-followups.js +6 -3
  156. package/lib/commands/upload.js +18 -3
  157. package/lib/core/secrets-docker-env.js +4 -1
  158. package/lib/core/secrets-env-content.js +17 -3
  159. package/lib/datasource/capability/capability-manifest.js +0 -3
  160. package/lib/datasource/capability/copy-test-payload.js +3 -0
  161. package/lib/external-system/deploy-helpers.js +26 -1
  162. package/lib/external-system/deploy.js +10 -6
  163. package/lib/external-system/download-helpers.js +29 -2
  164. package/lib/external-system/download-role-assistant.js +83 -4
  165. package/lib/external-system/download.js +40 -30
  166. package/lib/external-system/sync-deploy-manifest.js +3 -2
  167. package/lib/external-system/upload-identity.js +42 -0
  168. package/lib/generator/index.js +19 -15
  169. package/lib/generator/split.js +13 -2
  170. package/lib/lifecycle/role-assistant-governance.js +146 -0
  171. package/lib/lifecycle/scenario-upload.js +181 -30
  172. package/lib/programmatic/builder-api-cors.js +143 -0
  173. package/lib/programmatic/builder-api-request.js +6 -0
  174. package/lib/programmatic/builder-api-route-dispatch.js +56 -42
  175. package/lib/programmatic/builder-api-server.js +8 -1
  176. package/lib/programmatic/capability-registry.js +54 -0
  177. package/lib/programmatic/channel-install-help-resolve.js +359 -0
  178. package/lib/programmatic/channel-install-session.js +218 -0
  179. package/lib/programmatic/help-content/channel-onboarding.md +2 -0
  180. package/lib/programmatic/help-content/cip-overview.md +3 -1
  181. package/lib/programmatic/http-raw-response.js +62 -0
  182. package/lib/programmatic/openapi-channel-install-bindings.js +179 -0
  183. package/lib/programmatic/openapi-channel-install-schemas.js +286 -0
  184. package/lib/programmatic/openapi-component-schemas.js +18 -1
  185. package/lib/programmatic/openapi-description-enricher.js +3 -1
  186. package/lib/programmatic/openapi-descriptions.js +74 -3
  187. package/lib/programmatic/openapi-envelope-examples.js +117 -0
  188. package/lib/programmatic/openapi-operation-bindings.js +4 -1
  189. package/lib/programmatic/openapi-schema-descriptions-channel.js +316 -0
  190. package/lib/programmatic/openapi-spec.js +12 -8
  191. package/lib/programmatic/platform-rbac-catalog.js +36 -18
  192. package/lib/programmatic/route-handler-map.js +51 -1
  193. package/lib/programmatic/route-handlers-context.js +2 -1
  194. package/lib/programmatic/run-auth-permissions.js +54 -0
  195. package/lib/programmatic/run-channel-catalog.js +196 -0
  196. package/lib/programmatic/run-channel-install-artifacts.js +137 -0
  197. package/lib/programmatic/run-channel-install-copilot.js +353 -0
  198. package/lib/programmatic/run-channel-install-options.js +232 -0
  199. package/lib/programmatic/run-channel-install-teams-bot.js +204 -0
  200. package/lib/programmatic/run-channel-install.js +346 -0
  201. package/lib/programmatic/run-channel-package.js +87 -7
  202. package/lib/programmatic/workspace-context.js +2 -0
  203. package/lib/role-assistant/evidence-sync.js +4 -2
  204. package/lib/role-assistant/evidence-validation-issues.js +51 -0
  205. package/lib/role-assistant/knowledge-sync.js +10 -4
  206. package/lib/role-assistant/settings.js +49 -2
  207. package/lib/role-assistant/test-cases-evaluate.js +30 -2
  208. package/lib/role-assistant/test-cases-evidence.js +175 -0
  209. package/lib/role-assistant/test-cases-search.js +229 -0
  210. package/lib/role-assistant/test-cases.js +37 -4
  211. package/lib/role-assistant/test-runner-search.js +149 -0
  212. package/lib/role-assistant/test-runner-workhub-helpers.js +40 -2
  213. package/lib/role-assistant/test-runner-workhub-soft.js +9 -3
  214. package/lib/role-assistant/test-runner-workhub.js +29 -11
  215. package/lib/role-assistant/test-runner.js +16 -1
  216. package/lib/schema/external-datasource.schema.json +112 -18
  217. package/lib/schema/external-system.schema.json +159 -2
  218. package/lib/schema/mcp-client-profile.schema.json +103 -0
  219. package/lib/utils/app-path-resolve.js +4 -0
  220. package/lib/utils/authentication-env-template.js +44 -6
  221. package/lib/utils/credential-secrets-env.js +6 -1
  222. package/lib/utils/env-copy.js +19 -7
  223. package/lib/utils/external-env-template.js +20 -2
  224. package/lib/utils/paths.js +18 -1
  225. package/lib/utils/platform-shell-package.js +69 -0
  226. package/lib/utils/port-resolver.js +50 -1
  227. package/lib/utils/rewrite-name-flag.js +20 -0
  228. package/lib/utils/role-assistant-paths.js +13 -10
  229. package/lib/utils/schema-resolver.js +11 -1
  230. package/lib/utils/secrets-helpers.js +12 -21
  231. package/lib/utils/token-manager.js +10 -5
  232. package/lib/validation/validate.js +40 -0
  233. package/package.json +1 -1
  234. package/templates/README.md +1 -1
  235. package/templates/applications/builder-api/Dockerfile +1 -1
  236. package/templates/applications/builder-api/env.template +7 -0
  237. package/templates/applications/dataplane/application.yaml +6 -5
  238. package/templates/applications/dataplane/env.template +2 -2
  239. package/templates/applications/dataplane/rbac.yaml +7 -2
  240. package/templates/applications/keycloak/Dockerfile +30 -4
  241. package/templates/applications/keycloak/application.yaml +2 -2
  242. package/templates/applications/keycloak/cimd-lenient-parse/AbstractClientIdMetadataDocumentExecutor.java +896 -0
  243. package/templates/applications/keycloak/cimd-lenient-parse/SOURCE.md +19 -0
  244. package/templates/applications/keycloak/env.template +3 -0
  245. package/templates/applications/miso-controller/application.yaml +1 -2
  246. package/templates/applications/miso-controller/env.template +2 -0
  247. package/templates/channels/README.md +5 -5
  248. package/templates/channels/chatgpt/TROUBLESHOOTING.md +51 -0
  249. package/templates/channels/chatgpt/channel.json +59 -0
  250. package/templates/channels/chatgpt/icon.svg +1 -0
  251. package/templates/channels/chatgpt/install.template.md.hbs +20 -95
  252. package/templates/channels/chatgpt/integration/README.md +1 -1
  253. package/templates/channels/chatgpt/integration/application.json +2 -2
  254. package/templates/channels/chatgpt/integration/chatgpt-system.json +49 -4
  255. package/templates/channels/chatgpt/integration/env.template +2 -4
  256. package/templates/channels/chatgpt/integration/rbac.json +13 -0
  257. package/templates/channels/claude/TROUBLESHOOTING.md +41 -0
  258. package/templates/channels/claude/channel.json +59 -0
  259. package/templates/channels/claude/icon.svg +1 -0
  260. package/templates/channels/claude/install.template.md.hbs +21 -0
  261. package/templates/channels/claude/integration/README.md +14 -0
  262. package/templates/channels/claude/integration/application.json +15 -0
  263. package/templates/channels/claude/integration/claude-system.json +59 -0
  264. package/templates/channels/claude/integration/env.template +2 -0
  265. package/templates/channels/claude/integration/rbac.json +13 -0
  266. package/templates/channels/claude/manifest.template.json.hbs +40 -0
  267. package/templates/channels/mcp-client/channel.json +72 -0
  268. package/templates/channels/mcp-client/icon.svg +1 -0
  269. package/templates/channels/mcp-client/install.template.md.hbs +24 -0
  270. package/templates/channels/mcp-client/integration/README.md +13 -0
  271. package/templates/channels/mcp-client/integration/application.json +15 -0
  272. package/templates/channels/mcp-client/integration/env.template +2 -0
  273. package/templates/channels/mcp-client/integration/mcp-client-system.json +72 -0
  274. package/templates/channels/mcp-client/integration/rbac.json +13 -0
  275. package/templates/channels/mcp-client/manifest.template.json.hbs +41 -0
  276. package/templates/channels/mcp-client/profiles/cursor.json +26 -0
  277. package/templates/channels/mcp-client/profiles/github-copilot.json +29 -0
  278. package/templates/channels/mcp-client/profiles/vscode.json +29 -0
  279. package/templates/channels/microsoft-copilot/channel.json +73 -0
  280. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +8 -4
  281. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +65 -5
  282. package/templates/channels/microsoft-copilot/icon.svg +1 -0
  283. package/templates/channels/microsoft-copilot/install.template.md.hbs +9 -9
  284. package/templates/channels/microsoft-copilot/integration/README.md +1 -1
  285. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  286. package/templates/channels/microsoft-copilot/integration/env.template +3 -6
  287. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +63 -4
  288. package/templates/channels/microsoft-copilot/integration/rbac.json +13 -0
  289. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +5 -5
  290. package/templates/channels/slack/channel.json +223 -0
  291. package/templates/channels/slack/icon.svg +1 -0
  292. package/templates/channels/slack/install.template.md.hbs +36 -28
  293. package/templates/channels/slack/integration/README.md +245 -22
  294. package/templates/channels/slack/integration/application.json +44 -7
  295. package/templates/channels/slack/integration/env.template +15 -6
  296. package/templates/channels/slack/integration/openapi/openapi.json +126 -0
  297. package/templates/channels/slack/integration/rbac.json +84 -0
  298. package/templates/channels/slack/integration/slack-datasource-commands.json +154 -0
  299. package/templates/channels/slack/integration/slack-datasource-conversations.json +28 -3
  300. package/templates/channels/slack/integration/slack-datasource-events.json +154 -0
  301. package/templates/channels/slack/integration/slack-datasource-interactive.json +154 -0
  302. package/templates/channels/slack/integration/slack-datasource-messages.json +40 -2
  303. package/templates/channels/slack/integration/slack-system.json +155 -77
  304. package/templates/channels/slack/manifest.template.json.hbs +5 -2
  305. package/templates/channels/teams-bot/channel.json +161 -0
  306. package/templates/channels/teams-bot/icon.svg +1 -0
  307. package/templates/channels/teams-bot/install.template.md.hbs +12 -13
  308. package/templates/channels/teams-bot/integration/README.md +5 -4
  309. package/templates/channels/teams-bot/integration/application.json +6 -4
  310. package/templates/channels/teams-bot/integration/env.template +5 -4
  311. package/templates/channels/teams-bot/integration/openapi/openapi.json +68 -0
  312. package/templates/channels/teams-bot/integration/rbac.json +21 -0
  313. package/templates/channels/teams-bot/integration/teams-bot-datasource-activities.json +154 -0
  314. package/templates/channels/teams-bot/integration/teams-bot-system.json +128 -23
  315. package/templates/channels/web-embed/channel.json +60 -0
  316. package/templates/channels/web-embed/install.template.md.hbs +1 -1
  317. package/templates/channels/web-embed/integration/README.md +2 -2
  318. package/templates/channels/web-embed/integration/application.json +2 -2
  319. package/templates/channels/web-embed/integration/env.template +1 -1
  320. package/templates/channels/web-embed/integration/rbac.json +13 -0
  321. package/templates/channels/web-embed/integration/web-embed-system.json +50 -4
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Structural merge / remove for editor MCP config files.
3
+ *
4
+ * @fileoverview Preserve unrelated servers; never log secrets
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ /**
13
+ * @param {string} filePath
14
+ * @returns {string}
15
+ */
16
+ function backupPath(filePath) {
17
+ return `${filePath}.bak`;
18
+ }
19
+
20
+ /**
21
+ * @param {string} filePath
22
+ * @param {string} contents
23
+ * @returns {void}
24
+ */
25
+ function writeBackup(filePath, contents) {
26
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
27
+ fs.writeFileSync(backupPath(filePath), contents, 'utf8');
28
+ }
29
+
30
+ /**
31
+ * @param {string} filePath
32
+ * @param {Object} [deps]
33
+ * @returns {{ config: Object, recovered: boolean, existed: boolean, raw: string }}
34
+ */
35
+ function readMcpConfig(filePath, deps = {}) {
36
+ const readFile = deps.readFileSync || ((p) => fs.readFileSync(p, 'utf8'));
37
+ const exists = deps.existsSync || ((p) => fs.existsSync(p));
38
+ if (!exists(filePath)) {
39
+ return { config: {}, recovered: false, existed: false, raw: '' };
40
+ }
41
+ const raw = readFile(filePath);
42
+ if (!raw || !String(raw).trim()) {
43
+ return { config: {}, recovered: false, existed: true, raw };
44
+ }
45
+ try {
46
+ const parsed = JSON.parse(raw);
47
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
48
+ return { config: parsed, recovered: false, existed: true, raw };
49
+ }
50
+ } catch {
51
+ writeBackup(`${filePath}.invalid`, raw);
52
+ }
53
+ return { config: {}, recovered: true, existed: true, raw };
54
+ }
55
+
56
+ /**
57
+ * @param {Object} config
58
+ * @param {Object} profile
59
+ * @param {Object} entry
60
+ * @returns {Object}
61
+ */
62
+ function mergeServerEntry(config, profile, entry) {
63
+ const serversKey = profile.config.serversKey;
64
+ const serverKey = profile.config.serverKey;
65
+ const next = { ...config };
66
+ const servers = { ...(next[serversKey] && typeof next[serversKey] === 'object' ? next[serversKey] : {}) };
67
+ const previous = servers[serverKey] && typeof servers[serverKey] === 'object' ? servers[serverKey] : {};
68
+ servers[serverKey] = { ...previous, ...entry };
69
+ next[serversKey] = servers;
70
+ return next;
71
+ }
72
+
73
+ /**
74
+ * @param {Object} config
75
+ * @param {Object} profile
76
+ * @returns {Object}
77
+ */
78
+ function removeServerEntry(config, profile) {
79
+ const serversKey = profile.config.serversKey;
80
+ const serverKey = profile.config.serverKey;
81
+ const next = { ...config };
82
+ const servers = { ...(next[serversKey] && typeof next[serversKey] === 'object' ? next[serversKey] : {}) };
83
+ delete servers[serverKey];
84
+ if (Object.keys(servers).length === 0) {
85
+ delete next[serversKey];
86
+ } else {
87
+ next[serversKey] = servers;
88
+ }
89
+ return next;
90
+ }
91
+
92
+ /**
93
+ * @param {string} filePath
94
+ * @param {Object} config
95
+ * @param {Object} [deps]
96
+ * @returns {void}
97
+ */
98
+ function writeMcpConfig(filePath, config, deps = {}) {
99
+ const writeFile = deps.writeFileSync || ((p, contents) => fs.writeFileSync(p, contents, 'utf8'));
100
+ const mkdir = deps.mkdirSync || ((p, opts) => fs.mkdirSync(p, opts));
101
+ mkdir(path.dirname(filePath), { recursive: true });
102
+ writeFile(filePath, `${JSON.stringify(config, null, 2)}\n`);
103
+ }
104
+
105
+ /**
106
+ * @param {Object} params
107
+ * @returns {{ filePath: string, config: Object, recovered: boolean }}
108
+ */
109
+ function mergeMcpClientConfig(params) {
110
+ const { profile, filePath, entry, deps = {} } = params;
111
+ const read = readMcpConfig(filePath, deps);
112
+ if (read.existed && read.raw) {
113
+ writeBackup(filePath, read.raw);
114
+ }
115
+ const config = mergeServerEntry(read.config, profile, entry);
116
+ writeMcpConfig(filePath, config, deps);
117
+ return { filePath, config, recovered: read.recovered };
118
+ }
119
+
120
+ /**
121
+ * Remove only the AI Fabrix server key.
122
+ * @param {Object} params
123
+ * @returns {{ filePath: string, config: Object, removed: boolean }}
124
+ */
125
+ function removeMcpClientConfig(params) {
126
+ const { profile, filePath, deps = {} } = params;
127
+ const read = readMcpConfig(filePath, deps);
128
+ const servers = read.config[profile.config.serversKey] || {};
129
+ const removed = Boolean(servers[profile.config.serverKey]);
130
+ if (!removed) {
131
+ return { filePath, config: read.config, removed: false };
132
+ }
133
+ if (read.existed && read.raw) {
134
+ writeBackup(filePath, read.raw);
135
+ }
136
+ const config = removeServerEntry(read.config, profile);
137
+ writeMcpConfig(filePath, config, deps);
138
+ return { filePath, config, removed: true };
139
+ }
140
+
141
+ module.exports = {
142
+ backupPath,
143
+ readMcpConfig,
144
+ mergeServerEntry,
145
+ removeServerEntry,
146
+ mergeMcpClientConfig,
147
+ removeMcpClientConfig
148
+ };
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Ensure the per-installation Integration Client for an MCP client profile.
3
+ *
4
+ * Create when missing; merge redirect URIs; never rotate the secret.
5
+ *
6
+ * @fileoverview Reuse existing Integration Client APIs only
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const {
12
+ createIntegrationClient,
13
+ listIntegrationClients,
14
+ getIntegrationClient,
15
+ updateIntegrationClientRedirectUris
16
+ } = require('../../api/integration-clients.api');
17
+ const {
18
+ unwrapIntegrationClientList
19
+ } = require('../chatgpt-callback');
20
+ const {
21
+ normalizeMcpClientRedirectUris,
22
+ mergeRedirectUriList
23
+ } = require('./redirect-uris');
24
+
25
+ /**
26
+ * @param {string} message
27
+ * @param {string} [boundary]
28
+ * @returns {Error}
29
+ */
30
+ function createMcpClientEnsureError(message, boundary) {
31
+ const error = new Error(message);
32
+ error.boundary = boundary || 'oauth';
33
+ return error;
34
+ }
35
+
36
+ /**
37
+ * @param {Object} response
38
+ * @returns {Object}
39
+ */
40
+ function unwrapRecord(response) {
41
+ const body = response && response.data !== undefined ? response.data : response || {};
42
+ const inner = body.data !== undefined && body.data !== null ? body.data : body;
43
+ return inner.integrationClient || inner;
44
+ }
45
+
46
+ /**
47
+ * @param {Error|Object} errorOrResponse
48
+ * @returns {boolean}
49
+ */
50
+ function isForbidden(errorOrResponse) {
51
+ const status = errorOrResponse && errorOrResponse.status;
52
+ if (status === 403) {
53
+ return true;
54
+ }
55
+ const message = errorOrResponse && errorOrResponse.message
56
+ ? String(errorOrResponse.message)
57
+ : '';
58
+ return /\b403\b/.test(message) || /forbidden|not allowed|integration-client:/i.test(message);
59
+ }
60
+
61
+ /**
62
+ * @param {Error|Object} errorOrResponse
63
+ * @returns {boolean}
64
+ */
65
+ function isLoopbackRejected(errorOrResponse) {
66
+ const status = errorOrResponse && errorOrResponse.status;
67
+ const message = errorOrResponse && errorOrResponse.message
68
+ ? String(errorOrResponse.message)
69
+ : String(errorOrResponse || '');
70
+ if (status !== 400 && !/\b400\b/.test(message)) {
71
+ return false;
72
+ }
73
+ return /localhost|127\.0\.0\.1|loopback|http:\/\//i.test(message);
74
+ }
75
+
76
+ /**
77
+ * @param {Object} ctx
78
+ * @param {string} key
79
+ * @param {Object} deps
80
+ * @returns {Promise<Object|null>}
81
+ */
82
+ async function findClientByKey(ctx, key, deps) {
83
+ const listFn = deps.listIntegrationClients || listIntegrationClients;
84
+ const response = await listFn(ctx.controllerUrl, ctx.authConfig, { pageSize: 100 });
85
+ if (response && response.success === false) {
86
+ if (response.status === 403 || isForbidden(response)) {
87
+ throw createMcpClientEnsureError(
88
+ 'Missing integration-client:read. Cannot list Integration Clients.',
89
+ 'permissions'
90
+ );
91
+ }
92
+ throw createMcpClientEnsureError(
93
+ response.formattedError || response.error || 'Failed to list integration clients.'
94
+ );
95
+ }
96
+ return unwrapIntegrationClientList(response).find((row) => row && row.key === key) || null;
97
+ }
98
+
99
+ /**
100
+ * @param {Object} ctx
101
+ * @param {Object} match
102
+ * @param {Object} deps
103
+ * @returns {Promise<Object>}
104
+ */
105
+ async function loadClientDetail(ctx, match, deps) {
106
+ const getFn = deps.getIntegrationClient || getIntegrationClient;
107
+ const detail = await getFn(ctx.controllerUrl, ctx.authConfig, match.id);
108
+ const record = unwrapRecord(detail);
109
+ return { ...match, ...record, id: match.id };
110
+ }
111
+
112
+ /**
113
+ * @param {Object} params
114
+ * @returns {Object}
115
+ */
116
+ function createClientBody(params) {
117
+ const { profile, redirectUris } = params;
118
+ return {
119
+ key: profile.oauth.integrationClientKey,
120
+ displayName: profile.displayName,
121
+ redirectUris,
122
+ keycloakClientId: profile.oauth.keycloakClientId,
123
+ description: `Developer MCP client (${profile.key})`
124
+ };
125
+ }
126
+
127
+ /**
128
+ * @param {Object} response
129
+ * @returns {string|undefined}
130
+ */
131
+ function oneTimeSecret(response) {
132
+ const record = unwrapRecord(response);
133
+ const secret = record && record.clientSecret;
134
+ return typeof secret === 'string' && secret.trim() ? secret : undefined;
135
+ }
136
+
137
+ /**
138
+ * @param {Object} ctx
139
+ * @param {Object} profile
140
+ * @param {string[]} redirectUris
141
+ * @param {Object} deps
142
+ * @returns {Promise<{ client: Object, created: boolean, clientSecret?: string }>}
143
+ */
144
+ async function createClient(ctx, profile, redirectUris, deps) {
145
+ const createFn = deps.createIntegrationClient || createIntegrationClient;
146
+ try {
147
+ const response = await createFn(ctx.controllerUrl, ctx.authConfig, createClientBody({
148
+ profile,
149
+ redirectUris
150
+ }));
151
+ if (response && response.success === false) {
152
+ throw Object.assign(new Error(response.formattedError || response.error || 'Create failed'), {
153
+ status: response.status
154
+ });
155
+ }
156
+ const record = unwrapRecord(response);
157
+ return { client: record, created: true, clientSecret: oneTimeSecret(response) };
158
+ } catch (error) {
159
+ if (isForbidden(error)) {
160
+ throw createMcpClientEnsureError(
161
+ 'Missing integration-client:create. Cannot provision the MCP client.',
162
+ 'permissions'
163
+ );
164
+ }
165
+ if (isLoopbackRejected(error)) {
166
+ throw createMcpClientEnsureError(
167
+ 'Keycloak rejected a loopback HTTP redirect URI. Do not fall back to API keys.',
168
+ 'oauth'
169
+ );
170
+ }
171
+ throw error;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * @param {Object} params
177
+ * @returns {Promise<{ ok: boolean, added: boolean }>}
178
+ */
179
+ async function mergeRedirects(params) {
180
+ const { ctx, client, redirectUris, deps } = params;
181
+ const updateFn = deps.updateIntegrationClientRedirectUris || updateIntegrationClientRedirectUris;
182
+ const merged = mergeRedirectUriList(client.redirectUris, redirectUris);
183
+ try {
184
+ const response = await updateFn(ctx.controllerUrl, ctx.authConfig, client.id, {
185
+ redirectUris: merged.uris
186
+ });
187
+ if (response && response.success === false) {
188
+ throw Object.assign(new Error(response.formattedError || response.error || 'Update failed'), {
189
+ status: response.status
190
+ });
191
+ }
192
+ } catch (error) {
193
+ if (isForbidden(error)) {
194
+ throw createMcpClientEnsureError(
195
+ 'Missing integration-client:update. Cannot merge redirect URIs.',
196
+ 'permissions'
197
+ );
198
+ }
199
+ if (isLoopbackRejected(error)) {
200
+ throw createMcpClientEnsureError(
201
+ 'Keycloak rejected a loopback HTTP redirect URI. Do not fall back to API keys.',
202
+ 'oauth'
203
+ );
204
+ }
205
+ throw error;
206
+ }
207
+ return { ok: true, added: merged.added, redirectUris: merged.uris };
208
+ }
209
+
210
+ /**
211
+ * Create or reuse the Integration Client. Never calls regenerate-secret.
212
+ * @param {Object} params
213
+ * @returns {Promise<{ client: Object, created: boolean, added: boolean, clientSecret?: string }>}
214
+ */
215
+ async function ensureMcpClientIntegrationClient(params) {
216
+ const { ctx, profile, deps = {} } = params;
217
+ const redirectUris = normalizeMcpClientRedirectUris(profile.oauth.redirectUris);
218
+ const match = await findClientByKey(ctx, profile.oauth.integrationClientKey, deps);
219
+ if (!match || !match.id) {
220
+ const created = await createClient(ctx, profile, redirectUris, deps);
221
+ return { ...created, added: true };
222
+ }
223
+ const client = await loadClientDetail(ctx, match, deps);
224
+ const merged = await mergeRedirects({ ctx, client, redirectUris, deps });
225
+ return { client: { ...client, redirectUris: merged.redirectUris }, created: false, added: merged.added };
226
+ }
227
+
228
+ module.exports = {
229
+ ensureMcpClientIntegrationClient,
230
+ createMcpClientEnsureError
231
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Resolve editor MCP config paths for installation profiles.
3
+ *
4
+ * @fileoverview User vs project mcp.json locations
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const os = require('os');
10
+ const path = require('path');
11
+
12
+ /**
13
+ * @param {Object} [deps]
14
+ * @returns {string}
15
+ */
16
+ function vscodeUserMcpPath(deps = {}) {
17
+ const homedir = (deps.homedir || os.homedir)();
18
+ const env = deps.env || process.env;
19
+ const platform = deps.platform || process.platform;
20
+ if (platform === 'darwin') {
21
+ return path.join(homedir, 'Library', 'Application Support', 'Code', 'User', 'mcp.json');
22
+ }
23
+ if (platform === 'win32') {
24
+ const appData = env.APPDATA || path.join(homedir, 'AppData', 'Roaming');
25
+ return path.join(appData, 'Code', 'User', 'mcp.json');
26
+ }
27
+ const xdg = env.XDG_CONFIG_HOME || path.join(homedir, '.config');
28
+ return path.join(xdg, 'Code', 'User', 'mcp.json');
29
+ }
30
+
31
+ /**
32
+ * @param {Object} profile
33
+ * @param {Object} [deps]
34
+ * @returns {string}
35
+ */
36
+ function resolveMcpConfigPath(profile, deps = {}) {
37
+ const homedir = deps.homedir || os.homedir;
38
+ const cwd = deps.cwd || process.cwd;
39
+ const relative = profile.config.relativePath;
40
+ if (profile.config.scope === 'project') {
41
+ return path.join(cwd(), relative);
42
+ }
43
+ const key = profile.key;
44
+ if (key === 'vscode' || key === 'github-copilot') {
45
+ return vscodeUserMcpPath(deps);
46
+ }
47
+ return path.join(homedir(), relative);
48
+ }
49
+
50
+ module.exports = {
51
+ vscodeUserMcpPath,
52
+ resolveMcpConfigPath
53
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * MCP client installation profile loader (schema-validated).
3
+ *
4
+ * @fileoverview Load cursor / vscode / github-copilot profiles from channel templates
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const Ajv = require('ajv');
12
+ const schema = require('../../schema/mcp-client-profile.schema.json');
13
+
14
+ const BUILDER_ROOT = path.join(__dirname, '..', '..', '..');
15
+ const PROFILE_DIR = path.join(BUILDER_ROOT, 'templates', 'channels', 'mcp-client', 'profiles');
16
+
17
+ const ajv = new Ajv({ allErrors: true, strict: false });
18
+ const validateProfile = ajv.compile(schema);
19
+
20
+ /**
21
+ * @param {string} message
22
+ * @returns {Error}
23
+ */
24
+ function createMcpClientProfileError(message) {
25
+ const error = new Error(message);
26
+ error.boundary = 'usage';
27
+ return error;
28
+ }
29
+
30
+ /**
31
+ * @returns {string[]}
32
+ */
33
+ function listMcpClientProfileKeys() {
34
+ return fs
35
+ .readdirSync(PROFILE_DIR)
36
+ .filter((name) => name.endsWith('.json'))
37
+ .map((name) => path.basename(name, '.json'))
38
+ .sort();
39
+ }
40
+
41
+ /**
42
+ * @returns {{ name: string, value: string }[]}
43
+ */
44
+ function listMcpClientProfileChoices() {
45
+ return listMcpClientProfileKeys().map((key) => {
46
+ const profile = loadMcpClientProfile(key);
47
+ return { name: profile.displayName, value: profile.key };
48
+ });
49
+ }
50
+
51
+ /**
52
+ * @param {Object} profile
53
+ * @param {string} key
54
+ * @returns {void}
55
+ */
56
+ function assertValidProfile(profile, key) {
57
+ if (validateProfile(profile)) {
58
+ return;
59
+ }
60
+ const details = (validateProfile.errors || [])
61
+ .map((err) => `${err.instancePath || '/'} ${err.message}`)
62
+ .join('; ');
63
+ throw createMcpClientProfileError(`MCP client profile "${key}" is invalid: ${details}`);
64
+ }
65
+
66
+ /**
67
+ * @param {string} key
68
+ * @returns {Object}
69
+ */
70
+ function loadMcpClientProfile(key) {
71
+ const trimmed = typeof key === 'string' ? key.trim() : '';
72
+ if (!trimmed) {
73
+ throw createMcpClientProfileError(
74
+ [
75
+ 'MCP client needs --target <profile> (cursor, vscode, or github-copilot).',
76
+ ' $ aifabrix channel add mcp-client --target cursor --name "Onni"'
77
+ ].join('\n')
78
+ );
79
+ }
80
+ const filePath = path.join(PROFILE_DIR, `${trimmed}.json`);
81
+ if (!fs.existsSync(filePath)) {
82
+ const known = listMcpClientProfileKeys().join(', ');
83
+ throw createMcpClientProfileError(
84
+ `Unknown MCP client profile: ${trimmed}\nSupported profiles: ${known}`
85
+ );
86
+ }
87
+ const profile = JSON.parse(fs.readFileSync(filePath, 'utf8'));
88
+ assertValidProfile(profile, trimmed);
89
+ if (profile.key !== trimmed) {
90
+ throw createMcpClientProfileError(
91
+ `MCP client profile "${trimmed}" key mismatch (found "${profile.key}").`
92
+ );
93
+ }
94
+ return profile;
95
+ }
96
+
97
+ /**
98
+ * @param {Object} [options]
99
+ * @returns {string}
100
+ */
101
+ function resolveMcpClientProfileKey(options = {}) {
102
+ const raw = options.target || options.profile || options.mcpClientProfile;
103
+ return raw && String(raw).trim() ? String(raw).trim() : '';
104
+ }
105
+
106
+ /**
107
+ * @param {Object} [options]
108
+ * @returns {Object}
109
+ */
110
+ function requireMcpClientProfile(options = {}) {
111
+ return loadMcpClientProfile(resolveMcpClientProfileKey(options));
112
+ }
113
+
114
+ module.exports = {
115
+ PROFILE_DIR,
116
+ listMcpClientProfileKeys,
117
+ listMcpClientProfileChoices,
118
+ loadMcpClientProfile,
119
+ resolveMcpClientProfileKey,
120
+ requireMcpClientProfile,
121
+ assertValidProfile
122
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * MCP client redirect URI validation (HTTPS plus loopback HTTP).
3
+ *
4
+ * @fileoverview Do not reuse ChatGPT HTTPS-only callback assert
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { mergeRedirectUris } = require('../chatgpt-callback');
10
+
11
+ const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);
12
+
13
+ /**
14
+ * @param {string} message
15
+ * @returns {Error}
16
+ */
17
+ function createMcpClientRedirectError(message) {
18
+ const error = new Error(`MCP client callback: ${message}`);
19
+ error.boundary = 'callback';
20
+ return error;
21
+ }
22
+
23
+ /**
24
+ * @param {string} hostname
25
+ * @returns {boolean}
26
+ */
27
+ function isLoopbackHost(hostname) {
28
+ const host = String(hostname || '').toLowerCase().replace(/^\[|\]$/g, '');
29
+ return LOOPBACK_HOSTS.has(host) || LOOPBACK_HOSTS.has(hostname);
30
+ }
31
+
32
+ /**
33
+ * Allow HTTPS anywhere and HTTP on loopback only.
34
+ * @param {string|undefined} value
35
+ * @returns {string}
36
+ */
37
+ function assertMcpClientRedirectUri(value) {
38
+ const trimmed = typeof value === 'string' ? value.trim() : '';
39
+ if (!trimmed) {
40
+ throw createMcpClientRedirectError('Redirect URI is required.');
41
+ }
42
+ let parsed;
43
+ try {
44
+ parsed = new URL(trimmed);
45
+ } catch {
46
+ throw createMcpClientRedirectError(`Redirect URI is not a valid URL: ${trimmed}`);
47
+ }
48
+ if (parsed.protocol === 'https:') {
49
+ return trimmed;
50
+ }
51
+ if (parsed.protocol === 'http:' && isLoopbackHost(parsed.hostname)) {
52
+ return trimmed;
53
+ }
54
+ throw createMcpClientRedirectError(
55
+ `Redirect URI must be HTTPS or loopback HTTP: ${trimmed}`
56
+ );
57
+ }
58
+
59
+ /**
60
+ * @param {string[]|undefined} values
61
+ * @returns {string[]}
62
+ */
63
+ function normalizeMcpClientRedirectUris(values) {
64
+ const unique = [];
65
+ for (const value of Array.isArray(values) ? values : []) {
66
+ const uri = assertMcpClientRedirectUri(value);
67
+ if (!unique.includes(uri)) {
68
+ unique.push(uri);
69
+ }
70
+ }
71
+ if (unique.length === 0) {
72
+ throw createMcpClientRedirectError('At least one redirect URI is required.');
73
+ }
74
+ return unique;
75
+ }
76
+
77
+ /**
78
+ * @param {string[]|undefined} existing
79
+ * @param {string[]} nextUris
80
+ * @returns {{ uris: string[], added: boolean }}
81
+ */
82
+ function mergeRedirectUriList(existing, nextUris) {
83
+ let uris = Array.isArray(existing) ? [...existing] : [];
84
+ let added = false;
85
+ for (const nextUri of nextUris) {
86
+ const merged = mergeRedirectUris(uris, nextUri);
87
+ uris = merged.uris;
88
+ added = added || merged.added;
89
+ }
90
+ return { uris, added };
91
+ }
92
+
93
+ module.exports = {
94
+ LOOPBACK_HOSTS,
95
+ assertMcpClientRedirectUri,
96
+ normalizeMcpClientRedirectUris,
97
+ mergeRedirectUris,
98
+ mergeRedirectUriList
99
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Runtime values for the generic developer MCP client channel.
3
+ *
4
+ * @fileoverview Same Runtime MCP as ChatGPT; OAuth profile from the installation profile
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const {
10
+ CHATGPT_RUNTIME_SYSTEM_KEY,
11
+ CHATGPT_RUNTIME_TRANSPORT,
12
+ CHATGPT_RUNTIME_AUTH_MODE
13
+ } = require('../chatgpt-runtime-contract');
14
+
15
+ /**
16
+ * @param {string} controllerUrl
17
+ * @param {string} dataplaneBaseUrl
18
+ * @returns {string}
19
+ */
20
+ function authorizationServerBase(controllerUrl, dataplaneBaseUrl) {
21
+ const candidate = controllerUrl || dataplaneBaseUrl;
22
+ try {
23
+ return `${new URL(candidate).origin}/auth/realms/aifabrix`;
24
+ } catch {
25
+ return '';
26
+ }
27
+ }
28
+
29
+ /**
30
+ * @param {Object} resolved
31
+ * @param {Object} params
32
+ * @returns {void}
33
+ */
34
+ function applyMcpClientRuntimeValues(resolved, params) {
35
+ resolved.runtimeSystemKey = CHATGPT_RUNTIME_SYSTEM_KEY;
36
+ resolved.runtimeTransport = CHATGPT_RUNTIME_TRANSPORT;
37
+ resolved.runtimeAuthMode = CHATGPT_RUNTIME_AUTH_MODE;
38
+ resolved.layerBToolNames = Array.isArray(params.layerBToolNames) ? [...params.layerBToolNames] : [];
39
+ resolved.scopes = Array.isArray(params.scopes) ? [...params.scopes] : [];
40
+ resolved.scopesJson = JSON.stringify(resolved.scopes);
41
+ const profile = params.mcpClientProfile;
42
+ if (!profile) {
43
+ return;
44
+ }
45
+ const clientId = profile.oauth.keycloakClientId;
46
+ const controllerUrl = typeof params.controllerUrl === 'string' ? params.controllerUrl.trim() : '';
47
+ resolved.mcpClientProfileKey = profile.key;
48
+ resolved.mcpClientDisplayName = profile.displayName;
49
+ resolved.oauth2ProfileId = clientId;
50
+ resolved.oauth2ProfilePath = `/external-access/oauth2-profiles/${clientId}`;
51
+ if (controllerUrl) {
52
+ resolved.oauth2ProfileUrl = `${controllerUrl.replace(/\/$/, '')}${resolved.oauth2ProfilePath}`;
53
+ }
54
+ resolved.authorizationServerBase = authorizationServerBase(controllerUrl, resolved.dataplaneBaseUrl);
55
+ }
56
+
57
+ module.exports = {
58
+ applyMcpClientRuntimeValues
59
+ };