@rudderhq/server 0.5.2-canary.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap/register-api-routes.d.ts.map +1 -1
- package/dist/bootstrap/register-api-routes.js +15 -0
- package/dist/bootstrap/register-api-routes.js.map +1 -1
- package/dist/bootstrap/types.d.ts +11 -0
- package/dist/bootstrap/types.d.ts.map +1 -1
- package/dist/bundled-plugins/plugin-linear/dist/worker.js +741 -7
- package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +3 -3
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.d.ts +7 -0
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +15 -1
- package/dist/local-runtime.js.map +1 -1
- package/dist/middleware/auth.js +2 -2
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/error-handler.d.ts.map +1 -1
- package/dist/middleware/error-handler.js +3 -2
- package/dist/middleware/error-handler.js.map +1 -1
- package/dist/middleware/logger.d.ts +4 -0
- package/dist/middleware/logger.d.ts.map +1 -1
- package/dist/middleware/logger.js +82 -7
- package/dist/middleware/logger.js.map +1 -1
- package/dist/middleware/logger.test.js +94 -1
- package/dist/middleware/logger.test.js.map +1 -1
- package/dist/routes/chats.annotation-routes.d.ts +47 -0
- package/dist/routes/chats.annotation-routes.d.ts.map +1 -0
- package/dist/routes/chats.annotation-routes.js +164 -0
- package/dist/routes/chats.annotation-routes.js.map +1 -0
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +467 -165
- package/dist/routes/chats.js.map +1 -1
- package/dist/routes/chats.stream-routes.d.ts +1 -10
- package/dist/routes/chats.stream-routes.d.ts.map +1 -1
- package/dist/routes/chats.stream-routes.js +168 -110
- package/dist/routes/chats.stream-routes.js.map +1 -1
- package/dist/routes/chats.stream-support.d.ts +79 -0
- package/dist/routes/chats.stream-support.d.ts.map +1 -0
- package/dist/routes/chats.stream-support.js +121 -0
- package/dist/routes/chats.stream-support.js.map +1 -0
- package/dist/routes/issues.comments-attachments.d.ts.map +1 -1
- package/dist/routes/issues.comments-attachments.js +16 -2
- package/dist/routes/issues.comments-attachments.js.map +1 -1
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +21 -4
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/issues.mutations.d.ts.map +1 -1
- package/dist/routes/issues.mutations.js +20 -33
- package/dist/routes/issues.mutations.js.map +1 -1
- package/dist/routes/managed-mcp-agent-bindings.d.ts +15 -0
- package/dist/routes/managed-mcp-agent-bindings.d.ts.map +1 -0
- package/dist/routes/managed-mcp-agent-bindings.js +66 -0
- package/dist/routes/managed-mcp-agent-bindings.js.map +1 -0
- package/dist/routes/managed-mcp-connections.d.ts +8 -0
- package/dist/routes/managed-mcp-connections.d.ts.map +1 -0
- package/dist/routes/managed-mcp-connections.js +189 -0
- package/dist/routes/managed-mcp-connections.js.map +1 -0
- package/dist/routes/managed-mcp-runtime.d.ts +5 -0
- package/dist/routes/managed-mcp-runtime.d.ts.map +1 -0
- package/dist/routes/managed-mcp-runtime.js +148 -0
- package/dist/routes/managed-mcp-runtime.js.map +1 -0
- package/dist/routes/messenger.d.ts.map +1 -1
- package/dist/routes/messenger.js +1 -1
- package/dist/routes/messenger.js.map +1 -1
- package/dist/services/access.d.ts.map +1 -1
- package/dist/services/access.js +91 -12
- package/dist/services/access.js.map +1 -1
- package/dist/services/agent-run-context.d.ts +1 -0
- package/dist/services/agent-run-context.d.ts.map +1 -1
- package/dist/services/agent-run-context.js +5 -1
- package/dist/services/agent-run-context.js.map +1 -1
- package/dist/services/board-auth.d.ts +2 -2
- package/dist/services/chat-annotation-copy-lineage.d.ts +32 -0
- package/dist/services/chat-annotation-copy-lineage.d.ts.map +1 -0
- package/dist/services/chat-annotation-copy-lineage.js +110 -0
- package/dist/services/chat-annotation-copy-lineage.js.map +1 -0
- package/dist/services/chat-assistant.annotations.d.ts +16 -0
- package/dist/services/chat-assistant.annotations.d.ts.map +1 -0
- package/dist/services/chat-assistant.annotations.js +108 -0
- package/dist/services/chat-assistant.annotations.js.map +1 -0
- package/dist/services/chat-assistant.annotations.test.d.ts +2 -0
- package/dist/services/chat-assistant.annotations.test.d.ts.map +1 -0
- package/dist/services/chat-assistant.annotations.test.js +257 -0
- package/dist/services/chat-assistant.annotations.test.js.map +1 -0
- package/dist/services/chat-assistant.d.ts.map +1 -1
- package/dist/services/chat-assistant.helpers.d.ts +2 -3
- package/dist/services/chat-assistant.helpers.d.ts.map +1 -1
- package/dist/services/chat-assistant.helpers.js +37 -53
- package/dist/services/chat-assistant.helpers.js.map +1 -1
- package/dist/services/chat-assistant.js +61 -5
- package/dist/services/chat-assistant.js.map +1 -1
- package/dist/services/chat-generation-protocol.d.ts +8 -15
- package/dist/services/chat-generation-protocol.d.ts.map +1 -1
- package/dist/services/chat-generation-protocol.js +19 -59
- package/dist/services/chat-generation-protocol.js.map +1 -1
- package/dist/services/chat-generation-protocol.test.js +168 -6
- package/dist/services/chat-generation-protocol.test.js.map +1 -1
- package/dist/services/chat-generation-provenance.d.ts +14 -0
- package/dist/services/chat-generation-provenance.d.ts.map +1 -0
- package/dist/services/chat-generation-provenance.js +71 -0
- package/dist/services/chat-generation-provenance.js.map +1 -0
- package/dist/services/chat-inline-annotation-rendering.d.ts +2 -0
- package/dist/services/chat-inline-annotation-rendering.d.ts.map +1 -0
- package/dist/services/chat-inline-annotation-rendering.js +265 -0
- package/dist/services/chat-inline-annotation-rendering.js.map +1 -0
- package/dist/services/chat-inline-annotation-resolved-labels.d.ts +11 -0
- package/dist/services/chat-inline-annotation-resolved-labels.d.ts.map +1 -0
- package/dist/services/chat-inline-annotation-resolved-labels.js +490 -0
- package/dist/services/chat-inline-annotation-resolved-labels.js.map +1 -0
- package/dist/services/chat-inline-annotation-validation.d.ts +15 -0
- package/dist/services/chat-inline-annotation-validation.d.ts.map +1 -0
- package/dist/services/chat-inline-annotation-validation.js +430 -0
- package/dist/services/chat-inline-annotation-validation.js.map +1 -0
- package/dist/services/chat-inline-annotations.d.ts +18 -0
- package/dist/services/chat-inline-annotations.d.ts.map +1 -0
- package/dist/services/chat-inline-annotations.js +52 -0
- package/dist/services/chat-inline-annotations.js.map +1 -0
- package/dist/services/chat-inline-annotations.test.d.ts +2 -0
- package/dist/services/chat-inline-annotations.test.d.ts.map +1 -0
- package/dist/services/chat-inline-annotations.test.js +1306 -0
- package/dist/services/chat-inline-annotations.test.js.map +1 -0
- package/dist/services/chat-queued-message-materialization.d.ts +126 -0
- package/dist/services/chat-queued-message-materialization.d.ts.map +1 -0
- package/dist/services/chat-queued-message-materialization.js +327 -0
- package/dist/services/chat-queued-message-materialization.js.map +1 -0
- package/dist/services/chat-steer-messages.d.ts +3 -3
- package/dist/services/chat-steer-messages.d.ts.map +1 -1
- package/dist/services/chat-steer-messages.js +50 -80
- package/dist/services/chat-steer-messages.js.map +1 -1
- package/dist/services/chat-work-manifest.d.ts.map +1 -1
- package/dist/services/chat-work-manifest.js +5 -1
- package/dist/services/chat-work-manifest.js.map +1 -1
- package/dist/services/chats.annotation-persistence.d.ts +20 -0
- package/dist/services/chats.annotation-persistence.d.ts.map +1 -0
- package/dist/services/chats.annotation-persistence.js +206 -0
- package/dist/services/chats.annotation-persistence.js.map +1 -0
- package/dist/services/chats.d.ts +129 -96
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.helpers.d.ts +2 -2
- package/dist/services/chats.inline-visual-persistence.d.ts +3 -3
- package/dist/services/chats.inline-visual-persistence.d.ts.map +1 -1
- package/dist/services/chats.inline-visual-persistence.js +114 -54
- package/dist/services/chats.inline-visual-persistence.js.map +1 -1
- package/dist/services/chats.js +743 -252
- package/dist/services/chats.js.map +1 -1
- package/dist/services/finance.d.ts +1 -1
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +8 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/integrations/agent-integrations.d.ts +2 -2
- package/dist/services/integrations/custom-integrations.d.ts +3 -1
- package/dist/services/integrations/custom-integrations.d.ts.map +1 -1
- package/dist/services/integrations/custom-integrations.js +13 -2
- package/dist/services/integrations/custom-integrations.js.map +1 -1
- package/dist/services/issues.comments-attachments.d.ts +7 -1
- package/dist/services/issues.comments-attachments.d.ts.map +1 -1
- package/dist/services/issues.comments-attachments.js +13 -1
- package/dist/services/issues.comments-attachments.js.map +1 -1
- package/dist/services/issues.d.ts +6 -0
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.js +22 -3
- package/dist/services/issues.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.core.d.ts +4 -1
- package/dist/services/knowledge-portability/organization-portability.core.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.core.js +17 -3
- package/dist/services/knowledge-portability/organization-portability.core.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.export.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.export.js +2 -2
- package/dist/services/knowledge-portability/organization-portability.export.js.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.import.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-portability.import.js +13 -3
- package/dist/services/knowledge-portability/organization-portability.import.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +3 -0
- package/dist/services/knowledge-portability/organization-skills.catalog.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.catalog.js +54 -23
- package/dist/services/knowledge-portability/organization-skills.catalog.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.js +627 -124
- package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.sources.d.ts +1 -0
- package/dist/services/knowledge-portability/organization-skills.sources.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.sources.js +53 -10
- package/dist/services/knowledge-portability/organization-skills.sources.js.map +1 -1
- package/dist/services/mcp/authorizer-lock.d.ts +9 -0
- package/dist/services/mcp/authorizer-lock.d.ts.map +1 -0
- package/dist/services/mcp/authorizer-lock.js +15 -0
- package/dist/services/mcp/authorizer-lock.js.map +1 -0
- package/dist/services/mcp/managed-bindings.d.ts +17 -0
- package/dist/services/mcp/managed-bindings.d.ts.map +1 -0
- package/dist/services/mcp/managed-bindings.js +332 -0
- package/dist/services/mcp/managed-bindings.js.map +1 -0
- package/dist/services/mcp/managed-client.d.ts +64 -0
- package/dist/services/mcp/managed-client.d.ts.map +1 -0
- package/dist/services/mcp/managed-client.js +238 -0
- package/dist/services/mcp/managed-client.js.map +1 -0
- package/dist/services/mcp/managed-client.test.d.ts +2 -0
- package/dist/services/mcp/managed-client.test.d.ts.map +1 -0
- package/dist/services/mcp/managed-client.test.js +398 -0
- package/dist/services/mcp/managed-client.test.js.map +1 -0
- package/dist/services/mcp/managed-connections.d.ts +81 -0
- package/dist/services/mcp/managed-connections.d.ts.map +1 -0
- package/dist/services/mcp/managed-connections.js +920 -0
- package/dist/services/mcp/managed-connections.js.map +1 -0
- package/dist/services/mcp/managed-runtime.d.ts +134 -0
- package/dist/services/mcp/managed-runtime.d.ts.map +1 -0
- package/dist/services/mcp/managed-runtime.js +282 -0
- package/dist/services/mcp/managed-runtime.js.map +1 -0
- package/dist/services/mcp/oauth-provider.d.ts +48 -0
- package/dist/services/mcp/oauth-provider.d.ts.map +1 -0
- package/dist/services/mcp/oauth-provider.js +120 -0
- package/dist/services/mcp/oauth-provider.js.map +1 -0
- package/dist/services/mcp/oauth-scope.test.d.ts +2 -0
- package/dist/services/mcp/oauth-scope.test.d.ts.map +1 -0
- package/dist/services/mcp/oauth-scope.test.js +69 -0
- package/dist/services/mcp/oauth-scope.test.js.map +1 -0
- package/dist/services/mcp/oauth-session-gc.d.ts +14 -0
- package/dist/services/mcp/oauth-session-gc.d.ts.map +1 -0
- package/dist/services/mcp/oauth-session-gc.js +34 -0
- package/dist/services/mcp/oauth-session-gc.js.map +1 -0
- package/dist/services/mcp/oauth-session-gc.test.d.ts +2 -0
- package/dist/services/mcp/oauth-session-gc.test.d.ts.map +1 -0
- package/dist/services/mcp/oauth-session-gc.test.js +66 -0
- package/dist/services/mcp/oauth-session-gc.test.js.map +1 -0
- package/dist/services/mcp/oauth.d.ts +61 -0
- package/dist/services/mcp/oauth.d.ts.map +1 -0
- package/dist/services/mcp/oauth.js +1690 -0
- package/dist/services/mcp/oauth.js.map +1 -0
- package/dist/services/mcp/pinned-fetch.d.ts +15 -0
- package/dist/services/mcp/pinned-fetch.d.ts.map +1 -0
- package/dist/services/mcp/pinned-fetch.js +400 -0
- package/dist/services/mcp/pinned-fetch.js.map +1 -0
- package/dist/services/mcp/pinned-fetch.test.d.ts +2 -0
- package/dist/services/mcp/pinned-fetch.test.d.ts.map +1 -0
- package/dist/services/mcp/pinned-fetch.test.js +190 -0
- package/dist/services/mcp/pinned-fetch.test.js.map +1 -0
- package/dist/services/mcp/provider-registry.d.ts +77 -0
- package/dist/services/mcp/provider-registry.d.ts.map +1 -0
- package/dist/services/mcp/provider-registry.js +80 -0
- package/dist/services/mcp/provider-registry.js.map +1 -0
- package/dist/services/mcp/provider-registry.test.d.ts +2 -0
- package/dist/services/mcp/provider-registry.test.d.ts.map +1 -0
- package/dist/services/mcp/provider-registry.test.js +80 -0
- package/dist/services/mcp/provider-registry.test.js.map +1 -0
- package/dist/services/mcp/security-policy.d.ts +48 -0
- package/dist/services/mcp/security-policy.d.ts.map +1 -0
- package/dist/services/mcp/security-policy.js +344 -0
- package/dist/services/mcp/security-policy.js.map +1 -0
- package/dist/services/mcp/security-policy.test.d.ts +2 -0
- package/dist/services/mcp/security-policy.test.d.ts.map +1 -0
- package/dist/services/mcp/security-policy.test.js +226 -0
- package/dist/services/mcp/security-policy.test.js.map +1 -0
- package/dist/services/mcp/tool-discovery.d.ts +49 -0
- package/dist/services/mcp/tool-discovery.d.ts.map +1 -0
- package/dist/services/mcp/tool-discovery.js +198 -0
- package/dist/services/mcp/tool-discovery.js.map +1 -0
- package/dist/services/mcp/tool-discovery.test.d.ts +2 -0
- package/dist/services/mcp/tool-discovery.test.d.ts.map +1 -0
- package/dist/services/mcp/tool-discovery.test.js +168 -0
- package/dist/services/mcp/tool-discovery.test.js.map +1 -0
- package/dist/services/messenger.d.ts +1 -1
- package/dist/services/messenger.d.ts.map +1 -1
- package/dist/services/messenger.js +21 -3
- package/dist/services/messenger.js.map +1 -1
- package/dist/services/plugin-registry.d.ts +3 -3
- package/dist/services/runtime-kernel/heartbeat.core.d.ts +1 -1
- package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.core.js +30 -4
- package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.wakeup.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.wakeup.js +38 -23
- package/dist/services/runtime-kernel/heartbeat.wakeup.js.map +1 -1
- package/dist/services/secrets.d.ts +64 -6
- package/dist/services/secrets.d.ts.map +1 -1
- package/dist/services/secrets.js +38 -15
- package/dist/services/secrets.js.map +1 -1
- package/dist/services/side-chats.d.ts +1 -1
- package/dist/services/side-chats.d.ts.map +1 -1
- package/dist/services/side-chats.js +56 -14
- package/dist/services/side-chats.js.map +1 -1
- package/dist/services/workspace-runtime.services.d.ts +2 -2
- package/dist/services/workspace-web-preview.js +1 -1
- package/package.json +16 -13
- package/resources/bundled-skills/rudder-docs/references/operating-practices.md +12 -5
- package/skills/rudder-docs/references/operating-practices.md +12 -5
- package/ui-dist/assets/LibraryLiveSurface-CKOthw54.js +1 -0
- package/ui-dist/assets/{abnfDiagram-VRR7QNED-KGtP7zoC.js → abnfDiagram-VRR7QNED-C6a_wMho.js} +1 -1
- package/ui-dist/assets/{arc-JjnKyiOb.js → arc-CP0En6KA.js} +1 -1
- package/ui-dist/assets/architectureDiagram-ZJ3FMSHR-AHfOdcaZ.js +36 -0
- package/ui-dist/assets/{blockDiagram-677ZJIJ3-D7YzMYrz.js → blockDiagram-677ZJIJ3-Bkl9lp63.js} +1 -1
- package/ui-dist/assets/{c4Diagram-LMCZKHZV-Dpf7UQxl.js → c4Diagram-LMCZKHZV-BpZareVt.js} +1 -1
- package/ui-dist/assets/channel-DqxL_Zcy.js +1 -0
- package/ui-dist/assets/{chunk-2Q5K7J3B-UJ3gsGlQ.js → chunk-2Q5K7J3B-BQAwVuXc.js} +1 -1
- package/ui-dist/assets/{chunk-32BRIVSS-BeHl2j2l.js → chunk-32BRIVSS-YkmfA08F.js} +1 -1
- package/ui-dist/assets/{chunk-5VM5RSS4-STE9fJd0.js → chunk-5VM5RSS4-DvvjwTnG.js} +1 -1
- package/ui-dist/assets/{chunk-EX3LRPZG-Bj8g8j4B.js → chunk-EX3LRPZG-TXD_Yhg5.js} +1 -1
- package/ui-dist/assets/{chunk-JWPE2WC7-0V4Pwta-.js → chunk-JWPE2WC7-CgMlj6XI.js} +1 -1
- package/ui-dist/assets/{chunk-MOJQB5TN-DNPn50gm.js → chunk-MOJQB5TN-BnJjU8Rc.js} +1 -1
- package/ui-dist/assets/{chunk-RYQCIY6F-Befmnv8r.js → chunk-RYQCIY6F-D9B64JKM.js} +1 -1
- package/ui-dist/assets/{chunk-V7JOEXUC-Boh8TjHj.js → chunk-V7JOEXUC-PS09QVpl.js} +1 -1
- package/ui-dist/assets/{chunk-VR4S4FIN-B_yEQSo3.js → chunk-VR4S4FIN-DQXb7pRS.js} +1 -1
- package/ui-dist/assets/{chunk-XXDRQBXY-Cf1GnUFx.js → chunk-XXDRQBXY-B1UdLiF2.js} +1 -1
- package/ui-dist/assets/classDiagram-OUVF2IWQ-DMwjzuJE.js +1 -0
- package/ui-dist/assets/classDiagram-v2-EOCWNBFH-DMwjzuJE.js +1 -0
- package/ui-dist/assets/cose-bilkent-JH36ORCC-lqqmMB3m.js +1 -0
- package/ui-dist/assets/{cynefin-VYW2F7L2-Dv1yELYC.js → cynefin-VYW2F7L2-C9hu0XqZ.js} +1 -1
- package/ui-dist/assets/{cynefinDiagram-TSTJHNR4-CeFwjFoZ.js → cynefinDiagram-TSTJHNR4-Uuhz4c-1.js} +1 -1
- package/ui-dist/assets/{dagre-VKFMJZFB-8idd_4sX.js → dagre-VKFMJZFB-ChDrQYse.js} +1 -1
- package/ui-dist/assets/{diagram-FQU43EPY-BBVua6hM.js → diagram-FQU43EPY-DLtjkVSN.js} +1 -1
- package/ui-dist/assets/{diagram-G47NLZAW-D1zLQZlp.js → diagram-G47NLZAW-DCvEm4D0.js} +1 -1
- package/ui-dist/assets/{diagram-NH7WQ7WH-DE_FTSNv.js → diagram-NH7WQ7WH-Dz5jlsPG.js} +1 -1
- package/ui-dist/assets/{diagram-OA4YK3LP-61VSKBEn.js → diagram-OA4YK3LP-BXmQLO-Q.js} +1 -1
- package/ui-dist/assets/{diagram-WEI45ONY-C-tED6BJ.js → diagram-WEI45ONY-JnMU3xrx.js} +1 -1
- package/ui-dist/assets/{ebnfDiagram-CCIWWBDH-BEB8WGt0.js → ebnfDiagram-CCIWWBDH-Kryuqj4r.js} +1 -1
- package/ui-dist/assets/{erDiagram-Q63AITRT-AN_jm1TZ.js → erDiagram-Q63AITRT-Bn7g2mSW.js} +1 -1
- package/ui-dist/assets/{flowDiagram-23GEKE2U-BwLC6tI7.js → flowDiagram-23GEKE2U-DcjqTK91.js} +4 -4
- package/ui-dist/assets/ganttDiagram-NO4QXBWP-BRkw9-GE.js +292 -0
- package/ui-dist/assets/{gitGraphDiagram-IHSO6WYX-D9HCL9rY.js → gitGraphDiagram-IHSO6WYX-Cexcp6kR.js} +1 -1
- package/ui-dist/assets/graph-Cu7OV4Oi.js +1 -0
- package/ui-dist/assets/{index-BQXnpwke.js → index-ASZMPBYm.js} +5 -5
- package/ui-dist/assets/{index-5OM32_rf.js → index-B17BqCTj.js} +1 -1
- package/ui-dist/assets/{index-Cf0gLy6a.js → index-B9MccU1g.js} +1 -1
- package/ui-dist/assets/{index-BoSJ4Rx1.js → index-BPqbtW_X.js} +1 -1
- package/ui-dist/assets/{index-CpIdZ4-7.js → index-C2HjDa98.js} +1 -1
- package/ui-dist/assets/index-CMPtpj0A.js +1 -0
- package/ui-dist/assets/{index-p0ZGU1_7.js → index-CZXgOYtk.js} +1 -1
- package/ui-dist/assets/{index-74cx3ESJ.js → index-CknAauOP.js} +1 -1
- package/ui-dist/assets/index-Cu1GuH0a.js +1 -0
- package/ui-dist/assets/index-D8PdEImm.css +1 -0
- package/ui-dist/assets/{index-DrVDiJhe.js → index-DB1YX9-p.js} +1 -1
- package/ui-dist/assets/{index-vHMnAQSb.js → index-DBxLZXK7.js} +624 -610
- package/ui-dist/assets/{index-CpEfk5zb.js → index-DOCVOD97.js} +1 -1
- package/ui-dist/assets/{index-DMuWG0YN.js → index-DUbakRAC.js} +1 -1
- package/ui-dist/assets/{index-AT8juaIi.js → index-Dbg1ZwO2.js} +1 -1
- package/ui-dist/assets/index-DwbLsoLt.js +1 -0
- package/ui-dist/assets/index-hCLSotmS.js +1 -0
- package/ui-dist/assets/index-jrhEFc-j.js +1 -0
- package/ui-dist/assets/index-kz8ueXgo.js +1 -0
- package/ui-dist/assets/{infoDiagram-FWYZ7A6U-CyO3CAA1.js → infoDiagram-FWYZ7A6U-DugVGI3K.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-FXEZZL3T-BLLZuF1_.js → ishikawaDiagram-FXEZZL3T-CHy06Oev.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-5HDEW3XC-CWzQocvE.js → journeyDiagram-5HDEW3XC-BI0w53rb.js} +1 -1
- package/ui-dist/assets/{kanban-definition-HUTT4EX6-Dz4SFU-I.js → kanban-definition-HUTT4EX6-v_bLwx7B.js} +1 -1
- package/ui-dist/assets/layout-Dwp1s_bo.js +1 -0
- package/ui-dist/assets/{linear-BlzDlAaL.js → linear-DAV4STLZ.js} +1 -1
- package/ui-dist/assets/map-Bs6QpaaQ.js +1 -0
- package/ui-dist/assets/{mermaid.core-BtCydd-w.js → mermaid.core-fVmaL47V.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-LN4V7U3C-BeIkKRBx.js → mindmap-definition-LN4V7U3C-5dWD4dxm.js} +1 -1
- package/ui-dist/assets/{pegDiagram-2B236MQR-BZgpT4TN.js → pegDiagram-2B236MQR-DB2XsDPN.js} +1 -1
- package/ui-dist/assets/{pieDiagram-ENE6RG2P-BRFggcg2.js → pieDiagram-ENE6RG2P-Bc5teLCS.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-ABIIQ3AL-CibUo3gT.js → quadrantDiagram-ABIIQ3AL-BlfNdETt.js} +1 -1
- package/ui-dist/assets/{railroadDiagram-RFXS5EU6-DINJqhgn.js → railroadDiagram-RFXS5EU6-BbXywkq3.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-TGXJPOKE-NegluQIe.js → requirementDiagram-TGXJPOKE-DU-CapdF.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-HTMAVEWB-Kq41yHAd.js → sankeyDiagram-HTMAVEWB-Ftvh3MqA.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-DBY2YBRQ-CngfEI4f.js → sequenceDiagram-DBY2YBRQ-CCtHt9V4.js} +1 -1
- package/ui-dist/assets/{sizeCapture-X5ZJPWSS-DUWRNlme.js → sizeCapture-X5ZJPWSS-x0jnIKHx.js} +1 -1
- package/ui-dist/assets/{stateDiagram-2N3HPSRC-BwHC_Fz4.js → stateDiagram-2N3HPSRC-2qCrydE7.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-CvGRUgRf.js +1 -0
- package/ui-dist/assets/{swimlanes-5IMT3BWC-Cqp2rT1K.js → swimlanes-5IMT3BWC-6kqJq5Xf.js} +2 -2
- package/ui-dist/assets/swimlanesDiagram-G3AALYLV-EBa7-qSc.js +8 -0
- package/ui-dist/assets/{timeline-definition-FHXFAJF6-BG3GYPCu.js → timeline-definition-FHXFAJF6-BuRz8KHZ.js} +1 -1
- package/ui-dist/assets/{vennDiagram-L72KCM5P-D1IFU27N.js → vennDiagram-L72KCM5P-BKzMZ_Rv.js} +1 -1
- package/ui-dist/assets/{wardleyDiagram-EHGQE667-DboO2kYu.js → wardleyDiagram-EHGQE667-8uXlFtBV.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-FW5EYKEG-vI2BGrXP.js → xychartDiagram-FW5EYKEG-CWKGlpF9.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/architectureDiagram-ZJ3FMSHR-DHyfFgK0.js +0 -36
- package/ui-dist/assets/channel-TMGI9Qt9.js +0 -1
- package/ui-dist/assets/classDiagram-OUVF2IWQ-r5zIpY6G.js +0 -1
- package/ui-dist/assets/classDiagram-v2-EOCWNBFH-r5zIpY6G.js +0 -1
- package/ui-dist/assets/cose-bilkent-JH36ORCC-fSNs9447.js +0 -1
- package/ui-dist/assets/ganttDiagram-NO4QXBWP-CJYhBF1C.js +0 -292
- package/ui-dist/assets/graph-DdBT9W3J.js +0 -1
- package/ui-dist/assets/index-6UIO-gg7.js +0 -1
- package/ui-dist/assets/index-BVkQVMFn.js +0 -1
- package/ui-dist/assets/index-BaAPxkqh.css +0 -1
- package/ui-dist/assets/index-CmCERTmS.js +0 -1
- package/ui-dist/assets/index-Cr4lUIaJ.js +0 -1
- package/ui-dist/assets/index-D0fAqlES.js +0 -1
- package/ui-dist/assets/index-DfNcAyZq.js +0 -1
- package/ui-dist/assets/layout-D3tY9q4Q.js +0 -1
- package/ui-dist/assets/map-Ctg1-a9N.js +0 -1
- package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-C6P3NY96.js +0 -1
- package/ui-dist/assets/swimlanesDiagram-G3AALYLV-BNdDt62J.js +0 -8
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
import { activityLog, customIntegrationTools, mcpConnections, mcpOAuthGrants, organizationSecrets, organizationSecretVersions, } from "@rudderhq/db";
|
|
2
|
+
import { createMcpConnectionSchema, MCP_PROVIDER_CATALOG, mcpConnectionMergedConfigSchema, mcpConnectionMutationConfigSchema, updateMcpConnectionSchema, } from "@rudderhq/shared";
|
|
3
|
+
import { and, asc, eq, sql } from "drizzle-orm";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { conflict, HttpError, notFound, unprocessable } from "../../errors.js";
|
|
6
|
+
import { getSecretProvider } from "../../secrets/provider-registry.js";
|
|
7
|
+
import { secretService } from "../secrets.js";
|
|
8
|
+
import { createManagedMcpClient, resolveMcpHttpCredentials, } from "./managed-client.js";
|
|
9
|
+
import { MCP_PROVIDER_REGISTRY, resolveCuratedMcpEndpoint, } from "./provider-registry.js";
|
|
10
|
+
import { assertSafeMcpCredentialHeaders, assertSafeMcpHeaders, resolveMcpHttpTarget, validateMcpStdioPolicy, } from "./security-policy.js";
|
|
11
|
+
import { normalizeMcpDiscoveredTools, reconcileMcpToolCatalog, } from "./tool-discovery.js";
|
|
12
|
+
const EMPTY_ALLOWLISTS = {
|
|
13
|
+
httpOrigins: [],
|
|
14
|
+
stdioCommands: [],
|
|
15
|
+
stdioWorkingDirectories: [],
|
|
16
|
+
stdioEnvironmentNames: [],
|
|
17
|
+
};
|
|
18
|
+
const SENSITIVE_STATIC_NAME_RE = /(api[-_]?key|client[-_]?key|access[-_]?token|auth(?:[-_]?token)?|authorization|bearer|secret|passwd|password|credential|cookie|jwt|private[-_]?key|connectionstring|(?:^|[-_])(?:token|key)(?:$|[-_]))/iu;
|
|
19
|
+
const RECONNECT_REQUIRED_STATUSES = new Set([
|
|
20
|
+
"disabled",
|
|
21
|
+
"revoked",
|
|
22
|
+
"needs_reauth",
|
|
23
|
+
"error",
|
|
24
|
+
]);
|
|
25
|
+
export class ManagedMcpConnectionPolicyError extends HttpError {
|
|
26
|
+
code = "managed_mcp_policy_rejected";
|
|
27
|
+
constructor(message = "Managed MCP connection policy rejected the configuration") {
|
|
28
|
+
super(422, message);
|
|
29
|
+
this.name = "ManagedMcpConnectionPolicyError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class ManagedMcpDiscoveryStaleError extends HttpError {
|
|
33
|
+
code = "managed_mcp_discovery_stale";
|
|
34
|
+
constructor() {
|
|
35
|
+
super(409, "Managed MCP connection changed while tools were being discovered");
|
|
36
|
+
this.name = "ManagedMcpDiscoveryStaleError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function discoverySnapshot(row) {
|
|
40
|
+
return {
|
|
41
|
+
enabled: row.enabled,
|
|
42
|
+
lastDiscoveredAt: row.lastDiscoveredAt?.getTime() ?? null,
|
|
43
|
+
lifecycleRevision: row.lifecycleRevision,
|
|
44
|
+
status: row.status,
|
|
45
|
+
updatedAt: row.updatedAt.getTime(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function nextConnectionMutationTime(row) {
|
|
49
|
+
return new Date(Math.max(Date.now(), row.updatedAt.getTime() + 1));
|
|
50
|
+
}
|
|
51
|
+
function assertDiscoverySnapshot(row, snapshot) {
|
|
52
|
+
if (row.enabled !== snapshot.enabled
|
|
53
|
+
|| row.lifecycleRevision !== snapshot.lifecycleRevision
|
|
54
|
+
|| row.status !== snapshot.status
|
|
55
|
+
|| row.updatedAt.getTime() !== snapshot.updatedAt
|
|
56
|
+
|| (row.lastDiscoveredAt?.getTime() ?? null) !== snapshot.lastDiscoveredAt) {
|
|
57
|
+
throw new ManagedMcpDiscoveryStaleError();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function managedMcpActivityValues(input) {
|
|
61
|
+
const actorType = input.actor.agentId
|
|
62
|
+
? "agent"
|
|
63
|
+
: input.actor.userId
|
|
64
|
+
? "user"
|
|
65
|
+
: "system";
|
|
66
|
+
const actorId = input.actor.agentId ?? input.actor.userId ?? "system";
|
|
67
|
+
return {
|
|
68
|
+
orgId: input.orgId,
|
|
69
|
+
actorType,
|
|
70
|
+
actorId,
|
|
71
|
+
action: input.action,
|
|
72
|
+
entityType: "mcp_connection",
|
|
73
|
+
entityId: input.connectionId,
|
|
74
|
+
agentId: input.actor.agentId ?? null,
|
|
75
|
+
details: input.details,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function isRecord(value) {
|
|
79
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
80
|
+
}
|
|
81
|
+
function asStdioConfig(config) {
|
|
82
|
+
return config;
|
|
83
|
+
}
|
|
84
|
+
function asHttpConfig(config) {
|
|
85
|
+
return config;
|
|
86
|
+
}
|
|
87
|
+
function discoveryFilter(row) {
|
|
88
|
+
if (row.provider !== "custom")
|
|
89
|
+
return {};
|
|
90
|
+
const config = row.transport === "stdio"
|
|
91
|
+
? asStdioConfig(row.safeConfig)
|
|
92
|
+
: asHttpConfig(row.safeConfig);
|
|
93
|
+
return {
|
|
94
|
+
toolAllowlist: config.toolAllowlist,
|
|
95
|
+
toolDenylist: config.toolDenylist,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function declaredSecretShape(transport, safeConfig) {
|
|
99
|
+
if (transport === "stdio") {
|
|
100
|
+
const config = asStdioConfig(safeConfig);
|
|
101
|
+
return {
|
|
102
|
+
env: [...(config.secretEnvNames ?? [])].sort(),
|
|
103
|
+
headers: [],
|
|
104
|
+
bearerToken: false,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (transport === "streamable_http") {
|
|
108
|
+
const config = asHttpConfig(safeConfig);
|
|
109
|
+
return {
|
|
110
|
+
env: [],
|
|
111
|
+
headers: [...(config.secretHeaderNames ?? [])]
|
|
112
|
+
.map((name) => name.toLowerCase())
|
|
113
|
+
.sort(),
|
|
114
|
+
bearerToken: config.hasBearerToken === true,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return { env: [], headers: [], bearerToken: false };
|
|
118
|
+
}
|
|
119
|
+
function equalSecretShape(left, right) {
|
|
120
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
121
|
+
}
|
|
122
|
+
function hasDeclaredSecrets(shape) {
|
|
123
|
+
return shape.env.length > 0 || shape.headers.length > 0 || shape.bearerToken;
|
|
124
|
+
}
|
|
125
|
+
function assertNoSensitiveStaticNames(values, kind) {
|
|
126
|
+
const sensitiveName = Object.keys(values ?? {}).find((name) => (SENSITIVE_STATIC_NAME_RE.test(name)));
|
|
127
|
+
if (sensitiveName) {
|
|
128
|
+
throw new ManagedMcpConnectionPolicyError(`Managed MCP static ${kind} names that look sensitive must use encrypted credentials`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function assertCustomHttpHeaderPolicy(config, credentials) {
|
|
132
|
+
try {
|
|
133
|
+
assertSafeMcpHeaders(config.staticHeaders ?? {});
|
|
134
|
+
assertSafeMcpCredentialHeaders(Object.fromEntries(Object.keys(config.headersFromEnv ?? {}).map((name) => [name, "environment-reference"])));
|
|
135
|
+
assertSafeMcpCredentialHeaders(Object.fromEntries((config.secretHeaderNames ?? []).map((name) => [name, "encrypted-reference"])));
|
|
136
|
+
if (credentials?.headers) {
|
|
137
|
+
assertSafeMcpCredentialHeaders(credentials.headers);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
throw new ManagedMcpConnectionPolicyError("Managed MCP header configuration is rejected by deployment policy");
|
|
142
|
+
}
|
|
143
|
+
assertNoSensitiveStaticNames(config.staticHeaders, "header");
|
|
144
|
+
}
|
|
145
|
+
function reconnectRequiredError() {
|
|
146
|
+
return unprocessable("Reconnect the managed MCP connection before enabling or discovering tools");
|
|
147
|
+
}
|
|
148
|
+
function discoveryNotReadyError() {
|
|
149
|
+
return unprocessable("Managed MCP connection is not ready for tool discovery; reconnect or complete authorization first");
|
|
150
|
+
}
|
|
151
|
+
function canDiscoverTools(row) {
|
|
152
|
+
if (!row.enabled)
|
|
153
|
+
return false;
|
|
154
|
+
if (row.provider === "custom") {
|
|
155
|
+
return row.status === "draft" || row.status === "active";
|
|
156
|
+
}
|
|
157
|
+
return row.status === "active";
|
|
158
|
+
}
|
|
159
|
+
function publicSummary(row) {
|
|
160
|
+
return {
|
|
161
|
+
id: row.id,
|
|
162
|
+
orgId: row.orgId,
|
|
163
|
+
name: row.name,
|
|
164
|
+
displayName: row.displayName,
|
|
165
|
+
provider: row.provider,
|
|
166
|
+
transport: row.transport,
|
|
167
|
+
externalScope: row.externalScope,
|
|
168
|
+
accessMode: row.accessMode,
|
|
169
|
+
status: row.status,
|
|
170
|
+
safeConfig: row.safeConfig,
|
|
171
|
+
startupTimeoutMs: row.startupTimeoutMs,
|
|
172
|
+
toolTimeoutMs: row.toolTimeoutMs,
|
|
173
|
+
enabled: row.enabled,
|
|
174
|
+
required: row.required,
|
|
175
|
+
hasCredentials: Boolean(row.credentialSecretId),
|
|
176
|
+
lastDiscoveredAt: row.lastDiscoveredAt,
|
|
177
|
+
activatedAt: row.activatedAt,
|
|
178
|
+
disabledAt: row.disabledAt,
|
|
179
|
+
revokedAt: row.revokedAt,
|
|
180
|
+
createdAt: row.createdAt,
|
|
181
|
+
updatedAt: row.updatedAt,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function publicTool(row) {
|
|
185
|
+
if (!row.connectionId) {
|
|
186
|
+
throw new Error("Managed MCP tool is missing its connection");
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
id: row.id,
|
|
190
|
+
connectionId: row.connectionId,
|
|
191
|
+
externalToolName: row.externalToolName,
|
|
192
|
+
rudderToolName: row.rudderToolName,
|
|
193
|
+
description: row.description,
|
|
194
|
+
inputSchema: row.inputSchema,
|
|
195
|
+
outputSchema: row.outputSchema,
|
|
196
|
+
enabled: row.enabled,
|
|
197
|
+
removedAt: row.removedAt,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function parseCredentialPayload(value) {
|
|
201
|
+
try {
|
|
202
|
+
const parsed = JSON.parse(value);
|
|
203
|
+
if (!isRecord(parsed))
|
|
204
|
+
throw new Error("invalid");
|
|
205
|
+
return {
|
|
206
|
+
env: isRecord(parsed.env)
|
|
207
|
+
? Object.fromEntries(Object.entries(parsed.env).filter((entry) => (typeof entry[1] === "string")))
|
|
208
|
+
: undefined,
|
|
209
|
+
headers: isRecord(parsed.headers)
|
|
210
|
+
? Object.fromEntries(Object.entries(parsed.headers).filter((entry) => (typeof entry[1] === "string")))
|
|
211
|
+
: undefined,
|
|
212
|
+
bearerToken: typeof parsed.bearerToken === "string" ? parsed.bearerToken : undefined,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
throw unprocessable("Managed MCP credential material is invalid");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function customSecretPayload(input) {
|
|
220
|
+
return {
|
|
221
|
+
...(input.env ? { env: input.env } : {}),
|
|
222
|
+
...(input.headers ? { headers: input.headers } : {}),
|
|
223
|
+
...(input.bearerToken ? { bearerToken: input.bearerToken } : {}),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function assertLegacyMutable(row) {
|
|
227
|
+
if (row.transport === "legacy_manual") {
|
|
228
|
+
throw unprocessable("Legacy manual MCP definitions are read-only; create and reconnect a new managed connection");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function redactedDiscoveryError() {
|
|
232
|
+
return unprocessable("Managed MCP tool discovery failed");
|
|
233
|
+
}
|
|
234
|
+
export function managedMcpConnectionService(db, options) {
|
|
235
|
+
const secrets = secretService(db);
|
|
236
|
+
const createClient = options.createClient ?? createManagedMcpClient;
|
|
237
|
+
const hostEnv = options.hostEnv ?? process.env;
|
|
238
|
+
const allowlists = options.allowlists ?? EMPTY_ALLOWLISTS;
|
|
239
|
+
async function findRow(orgId, connectionId) {
|
|
240
|
+
const row = await db
|
|
241
|
+
.select()
|
|
242
|
+
.from(mcpConnections)
|
|
243
|
+
.where(and(eq(mcpConnections.orgId, orgId), eq(mcpConnections.id, connectionId)))
|
|
244
|
+
.then((rows) => rows[0] ?? null);
|
|
245
|
+
if (!row)
|
|
246
|
+
throw notFound("MCP connection not found");
|
|
247
|
+
return row;
|
|
248
|
+
}
|
|
249
|
+
function assertHttpEnvironmentPolicy(config) {
|
|
250
|
+
if (options.deploymentMode !== "authenticated")
|
|
251
|
+
return;
|
|
252
|
+
const allowed = new Set(allowlists.stdioEnvironmentNames);
|
|
253
|
+
const requested = [
|
|
254
|
+
...Object.values(config.headersFromEnv ?? {}),
|
|
255
|
+
...(config.bearerTokenEnvVar ? [config.bearerTokenEnvVar] : []),
|
|
256
|
+
];
|
|
257
|
+
if (requested.some((name) => !allowed.has(name))) {
|
|
258
|
+
throw unprocessable("Managed MCP HTTP environment name is not allowed by deployment policy");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function validateBoundary(input) {
|
|
262
|
+
try {
|
|
263
|
+
if (input.transport === "legacy_manual")
|
|
264
|
+
return;
|
|
265
|
+
if (input.transport === "stdio") {
|
|
266
|
+
const config = asStdioConfig(input.safeConfig);
|
|
267
|
+
assertNoSensitiveStaticNames(config.staticEnv, "environment variable");
|
|
268
|
+
await validateMcpStdioPolicy({
|
|
269
|
+
command: config.command,
|
|
270
|
+
args: config.args,
|
|
271
|
+
cwd: config.cwd,
|
|
272
|
+
environmentNames: [
|
|
273
|
+
...Object.keys(config.staticEnv ?? {}),
|
|
274
|
+
...(config.forwardedEnv ?? []),
|
|
275
|
+
...(config.secretEnvNames ?? []),
|
|
276
|
+
],
|
|
277
|
+
}, {
|
|
278
|
+
deploymentMode: options.deploymentMode,
|
|
279
|
+
stdioCommands: allowlists.stdioCommands,
|
|
280
|
+
stdioWorkingDirectories: allowlists.stdioWorkingDirectories,
|
|
281
|
+
stdioEnvironmentNames: allowlists.stdioEnvironmentNames,
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
let url;
|
|
286
|
+
let curatedOrigin;
|
|
287
|
+
if (input.provider === "custom") {
|
|
288
|
+
const config = asHttpConfig(input.safeConfig);
|
|
289
|
+
if (!config.url)
|
|
290
|
+
throw unprocessable("Custom Streamable HTTP connection requires a URL");
|
|
291
|
+
assertCustomHttpHeaderPolicy(config, input.credentials);
|
|
292
|
+
assertHttpEnvironmentPolicy(config);
|
|
293
|
+
url = config.url;
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// Supabase is allowed to remain draft before project selection.
|
|
297
|
+
if (input.provider === "supabase" && !input.externalScope) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const endpoint = resolveCuratedMcpEndpoint({
|
|
301
|
+
provider: input.provider,
|
|
302
|
+
accessMode: input.accessMode,
|
|
303
|
+
externalScope: input.externalScope,
|
|
304
|
+
});
|
|
305
|
+
url = endpoint.href;
|
|
306
|
+
curatedOrigin = new URL(MCP_PROVIDER_REGISTRY[input.provider].endpoint).origin;
|
|
307
|
+
}
|
|
308
|
+
await resolveMcpHttpTarget(url, {
|
|
309
|
+
allowedOrigins: allowlists.httpOrigins,
|
|
310
|
+
curatedOrigin,
|
|
311
|
+
lookup: options.dnsLookup,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
if (error instanceof HttpError) {
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
throw new ManagedMcpConnectionPolicyError();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async function prepareCredentialReplacement(connectionName, value) {
|
|
322
|
+
const id = randomUUID();
|
|
323
|
+
const prepared = await getSecretProvider("local_encrypted").createVersion({
|
|
324
|
+
value: JSON.stringify(customSecretPayload(value)),
|
|
325
|
+
externalRef: null,
|
|
326
|
+
});
|
|
327
|
+
return {
|
|
328
|
+
id,
|
|
329
|
+
name: `Managed MCP credentials - ${connectionName} - ${id}`,
|
|
330
|
+
externalRef: prepared.externalRef,
|
|
331
|
+
material: prepared.material,
|
|
332
|
+
valueSha256: prepared.valueSha256,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
async function resolveCredentialPayload(row) {
|
|
336
|
+
if (!row.credentialSecretId)
|
|
337
|
+
return {};
|
|
338
|
+
return parseCredentialPayload(await secrets.resolveSecretValue(row.orgId, row.credentialSecretId, "latest"));
|
|
339
|
+
}
|
|
340
|
+
async function buildClientOptions(row) {
|
|
341
|
+
assertLegacyMutable(row);
|
|
342
|
+
await validateBoundary({
|
|
343
|
+
provider: row.provider,
|
|
344
|
+
transport: row.transport,
|
|
345
|
+
accessMode: row.accessMode,
|
|
346
|
+
externalScope: row.externalScope,
|
|
347
|
+
safeConfig: row.safeConfig,
|
|
348
|
+
});
|
|
349
|
+
if (row.transport === "stdio") {
|
|
350
|
+
const config = asStdioConfig(row.safeConfig);
|
|
351
|
+
const credential = await resolveCredentialPayload(row);
|
|
352
|
+
return {
|
|
353
|
+
transport: "stdio",
|
|
354
|
+
command: config.command,
|
|
355
|
+
args: config.args,
|
|
356
|
+
cwd: config.cwd,
|
|
357
|
+
staticEnv: config.staticEnv ?? {},
|
|
358
|
+
forwardedEnv: config.forwardedEnv ?? [],
|
|
359
|
+
secretEnv: credential.env ?? {},
|
|
360
|
+
hostEnv,
|
|
361
|
+
deploymentPolicy: {
|
|
362
|
+
deploymentMode: options.deploymentMode,
|
|
363
|
+
stdioCommands: allowlists.stdioCommands,
|
|
364
|
+
stdioWorkingDirectories: allowlists.stdioWorkingDirectories,
|
|
365
|
+
stdioEnvironmentNames: allowlists.stdioEnvironmentNames,
|
|
366
|
+
},
|
|
367
|
+
startupTimeoutMs: row.startupTimeoutMs,
|
|
368
|
+
toolTimeoutMs: row.toolTimeoutMs,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
let url;
|
|
372
|
+
let curatedOrigin;
|
|
373
|
+
let credential = {};
|
|
374
|
+
let oauthCredential;
|
|
375
|
+
if (row.provider === "custom") {
|
|
376
|
+
const config = asHttpConfig(row.safeConfig);
|
|
377
|
+
if (!config.url)
|
|
378
|
+
throw unprocessable("Custom Streamable HTTP connection requires a URL");
|
|
379
|
+
url = config.url;
|
|
380
|
+
credential = await resolveCredentialPayload(row);
|
|
381
|
+
for (const [headerName, environmentName] of Object.entries(config.headersFromEnv ?? {})) {
|
|
382
|
+
const value = hostEnv[environmentName];
|
|
383
|
+
if (value !== undefined) {
|
|
384
|
+
credential.headers = { ...(credential.headers ?? {}), [headerName]: value };
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (config.bearerTokenEnvVar) {
|
|
388
|
+
credential.bearerToken = hostEnv[config.bearerTokenEnvVar];
|
|
389
|
+
}
|
|
390
|
+
assertCustomHttpHeaderPolicy(config, credential);
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
const endpoint = resolveCuratedMcpEndpoint({
|
|
394
|
+
provider: row.provider,
|
|
395
|
+
accessMode: row.accessMode,
|
|
396
|
+
externalScope: row.externalScope,
|
|
397
|
+
});
|
|
398
|
+
url = endpoint.href;
|
|
399
|
+
curatedOrigin = new URL(MCP_PROVIDER_REGISTRY[row.provider].endpoint).origin;
|
|
400
|
+
const grant = await db
|
|
401
|
+
.select()
|
|
402
|
+
.from(mcpOAuthGrants)
|
|
403
|
+
.where(and(eq(mcpOAuthGrants.orgId, row.orgId), eq(mcpOAuthGrants.connectionId, row.id), eq(mcpOAuthGrants.status, "active")))
|
|
404
|
+
.then((rows) => rows[0] ?? null);
|
|
405
|
+
if (!grant?.credentialSecretId) {
|
|
406
|
+
throw unprocessable("Managed MCP OAuth authorization is required");
|
|
407
|
+
}
|
|
408
|
+
if (!options.createOAuthCredential) {
|
|
409
|
+
throw unprocessable("Managed MCP OAuth credential factory is not configured");
|
|
410
|
+
}
|
|
411
|
+
oauthCredential = options.createOAuthCredential(row.orgId, row.id);
|
|
412
|
+
}
|
|
413
|
+
const config = asHttpConfig(row.safeConfig);
|
|
414
|
+
return {
|
|
415
|
+
transport: "streamable_http",
|
|
416
|
+
url,
|
|
417
|
+
staticHeaders: row.provider === "custom" ? config.staticHeaders ?? {} : {},
|
|
418
|
+
credentials: resolveMcpHttpCredentials({
|
|
419
|
+
bearerToken: credential.bearerToken,
|
|
420
|
+
secretHeaders: credential.headers,
|
|
421
|
+
oauth: oauthCredential,
|
|
422
|
+
}),
|
|
423
|
+
network: {
|
|
424
|
+
allowedOrigins: allowlists.httpOrigins,
|
|
425
|
+
curatedOrigin,
|
|
426
|
+
lookup: options.dnsLookup,
|
|
427
|
+
},
|
|
428
|
+
startupTimeoutMs: row.startupTimeoutMs,
|
|
429
|
+
toolTimeoutMs: row.toolTimeoutMs,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
async function listTools(orgId, connectionId) {
|
|
433
|
+
await findRow(orgId, connectionId);
|
|
434
|
+
return db
|
|
435
|
+
.select()
|
|
436
|
+
.from(customIntegrationTools)
|
|
437
|
+
.where(and(eq(customIntegrationTools.orgId, orgId), eq(customIntegrationTools.connectionId, connectionId)))
|
|
438
|
+
.orderBy(asc(customIntegrationTools.externalToolName))
|
|
439
|
+
.then((rows) => rows.map(publicTool));
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
catalog: () => MCP_PROVIDER_CATALOG.map((entry) => ({
|
|
443
|
+
...entry,
|
|
444
|
+
transports: [...entry.transports],
|
|
445
|
+
accessModes: [...entry.accessModes],
|
|
446
|
+
})),
|
|
447
|
+
list: (orgId) => db
|
|
448
|
+
.select()
|
|
449
|
+
.from(mcpConnections)
|
|
450
|
+
.where(eq(mcpConnections.orgId, orgId))
|
|
451
|
+
.orderBy(asc(mcpConnections.displayName))
|
|
452
|
+
.then((rows) => rows.map(publicSummary)),
|
|
453
|
+
get: async (orgId, connectionId) => publicSummary(await findRow(orgId, connectionId)),
|
|
454
|
+
openRuntimeClient: async (orgId, connectionId) => {
|
|
455
|
+
const connection = await findRow(orgId, connectionId);
|
|
456
|
+
if (connection.status !== "active"
|
|
457
|
+
|| !connection.enabled
|
|
458
|
+
|| connection.transport === "legacy_manual") {
|
|
459
|
+
throw unprocessable("Managed MCP connection is unavailable");
|
|
460
|
+
}
|
|
461
|
+
return createClient(await buildClientOptions(connection));
|
|
462
|
+
},
|
|
463
|
+
create: async (orgId, rawInput, actor = {}) => {
|
|
464
|
+
const input = createMcpConnectionSchema.parse(rawInput);
|
|
465
|
+
if (input.transport === "legacy_manual") {
|
|
466
|
+
throw unprocessable("Legacy manual MCP definitions cannot be created as managed connections");
|
|
467
|
+
}
|
|
468
|
+
await validateBoundary({
|
|
469
|
+
provider: input.provider,
|
|
470
|
+
transport: input.transport,
|
|
471
|
+
accessMode: input.accessMode,
|
|
472
|
+
externalScope: input.externalScope ?? null,
|
|
473
|
+
safeConfig: input.safeConfig,
|
|
474
|
+
credentials: input.secrets ? customSecretPayload(input.secrets) : undefined,
|
|
475
|
+
});
|
|
476
|
+
const credential = input.secrets
|
|
477
|
+
? await prepareCredentialReplacement(input.name, input.secrets)
|
|
478
|
+
: null;
|
|
479
|
+
try {
|
|
480
|
+
const row = await db.transaction(async (tx) => {
|
|
481
|
+
if (credential) {
|
|
482
|
+
await tx.insert(organizationSecrets).values({
|
|
483
|
+
id: credential.id,
|
|
484
|
+
orgId,
|
|
485
|
+
name: credential.name,
|
|
486
|
+
provider: "local_encrypted",
|
|
487
|
+
purpose: "managed_mcp_connection",
|
|
488
|
+
externalRef: credential.externalRef,
|
|
489
|
+
latestVersion: 1,
|
|
490
|
+
description: "Encrypted managed MCP credentials",
|
|
491
|
+
createdByAgentId: actor.agentId ?? null,
|
|
492
|
+
createdByUserId: actor.userId ?? null,
|
|
493
|
+
});
|
|
494
|
+
await tx.insert(organizationSecretVersions).values({
|
|
495
|
+
secretId: credential.id,
|
|
496
|
+
version: 1,
|
|
497
|
+
material: credential.material,
|
|
498
|
+
valueSha256: credential.valueSha256,
|
|
499
|
+
createdByAgentId: actor.agentId ?? null,
|
|
500
|
+
createdByUserId: actor.userId ?? null,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
const created = await tx.insert(mcpConnections).values({
|
|
504
|
+
orgId,
|
|
505
|
+
credentialSecretId: credential?.id ?? null,
|
|
506
|
+
name: input.name,
|
|
507
|
+
displayName: input.displayName,
|
|
508
|
+
provider: input.provider,
|
|
509
|
+
transport: input.transport,
|
|
510
|
+
externalScope: input.externalScope ?? null,
|
|
511
|
+
accessMode: input.accessMode,
|
|
512
|
+
status: "draft",
|
|
513
|
+
safeConfig: input.safeConfig,
|
|
514
|
+
startupTimeoutMs: input.startupTimeoutMs,
|
|
515
|
+
toolTimeoutMs: input.toolTimeoutMs,
|
|
516
|
+
enabled: input.enabled,
|
|
517
|
+
required: input.required,
|
|
518
|
+
})
|
|
519
|
+
.returning()
|
|
520
|
+
.then((rows) => rows[0]);
|
|
521
|
+
await tx.insert(activityLog).values(managedMcpActivityValues({
|
|
522
|
+
orgId,
|
|
523
|
+
connectionId: created.id,
|
|
524
|
+
action: "mcp_connection.created",
|
|
525
|
+
actor,
|
|
526
|
+
details: {
|
|
527
|
+
provider: created.provider,
|
|
528
|
+
transport: created.transport,
|
|
529
|
+
status: created.status,
|
|
530
|
+
},
|
|
531
|
+
}));
|
|
532
|
+
return created;
|
|
533
|
+
});
|
|
534
|
+
return publicSummary(row);
|
|
535
|
+
}
|
|
536
|
+
catch (error) {
|
|
537
|
+
if (error instanceof Error && /unique|duplicate/iu.test(error.message)) {
|
|
538
|
+
throw conflict("MCP connection name or provider scope already exists");
|
|
539
|
+
}
|
|
540
|
+
throw error;
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
update: async (orgId, connectionId, rawPatch, actor = {}, control = {}) => {
|
|
544
|
+
const existing = await findRow(orgId, connectionId);
|
|
545
|
+
assertLegacyMutable(existing);
|
|
546
|
+
const patch = updateMcpConnectionSchema.parse(rawPatch);
|
|
547
|
+
if (patch.enabled === true
|
|
548
|
+
&& RECONNECT_REQUIRED_STATUSES.has(existing.status)) {
|
|
549
|
+
throw reconnectRequiredError();
|
|
550
|
+
}
|
|
551
|
+
const merged = {
|
|
552
|
+
provider: existing.provider,
|
|
553
|
+
transport: existing.transport,
|
|
554
|
+
accessMode: patch.accessMode ?? existing.accessMode,
|
|
555
|
+
safeConfig: patch.safeConfig ?? existing.safeConfig,
|
|
556
|
+
enabled: patch.enabled ?? existing.enabled,
|
|
557
|
+
};
|
|
558
|
+
mcpConnectionMergedConfigSchema.parse(merged);
|
|
559
|
+
if (patch.accessMode !== undefined
|
|
560
|
+
&& existing.provider !== "custom"
|
|
561
|
+
&& !control.allowCuratedAccessMode) {
|
|
562
|
+
throw unprocessable("Curated MCP access mode must be changed through the dedicated access-mode operation");
|
|
563
|
+
}
|
|
564
|
+
if (existing.provider === "linear"
|
|
565
|
+
&& patch.accessMode !== undefined
|
|
566
|
+
&& patch.accessMode !== existing.accessMode
|
|
567
|
+
&& (existing.status === "active" || existing.status === "authorizing")) {
|
|
568
|
+
throw unprocessable("Linear access-mode changes require reauthorization: disconnect, select the new access mode, then reconnect OAuth");
|
|
569
|
+
}
|
|
570
|
+
const previousShape = declaredSecretShape(existing.transport, existing.safeConfig);
|
|
571
|
+
const nextShape = declaredSecretShape(existing.transport, merged.safeConfig);
|
|
572
|
+
const declarationChanged = !equalSecretShape(previousShape, nextShape);
|
|
573
|
+
if (patch.secrets || declarationChanged) {
|
|
574
|
+
if (hasDeclaredSecrets(nextShape) && !patch.secrets) {
|
|
575
|
+
throw unprocessable("Changed managed MCP secret declarations require exact replacement values");
|
|
576
|
+
}
|
|
577
|
+
mcpConnectionMutationConfigSchema.parse({
|
|
578
|
+
...merged,
|
|
579
|
+
...(patch.secrets ? { secrets: patch.secrets } : {}),
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
await validateBoundary({
|
|
583
|
+
...merged,
|
|
584
|
+
externalScope: patch.externalScope === undefined
|
|
585
|
+
? existing.externalScope
|
|
586
|
+
: patch.externalScope ?? null,
|
|
587
|
+
credentials: patch.secrets ? customSecretPayload(patch.secrets) : undefined,
|
|
588
|
+
});
|
|
589
|
+
let credentialSecretId = existing.credentialSecretId;
|
|
590
|
+
let oldSecretIdToDelete = null;
|
|
591
|
+
const replacement = patch.secrets
|
|
592
|
+
? await prepareCredentialReplacement(existing.name, patch.secrets)
|
|
593
|
+
: null;
|
|
594
|
+
if (replacement) {
|
|
595
|
+
oldSecretIdToDelete = credentialSecretId;
|
|
596
|
+
credentialSecretId = replacement.id;
|
|
597
|
+
}
|
|
598
|
+
else if (declarationChanged && !hasDeclaredSecrets(nextShape) && credentialSecretId) {
|
|
599
|
+
oldSecretIdToDelete = credentialSecretId;
|
|
600
|
+
credentialSecretId = null;
|
|
601
|
+
}
|
|
602
|
+
const row = await db.transaction(async (tx) => {
|
|
603
|
+
const locked = await tx
|
|
604
|
+
.select()
|
|
605
|
+
.from(mcpConnections)
|
|
606
|
+
.where(and(eq(mcpConnections.orgId, orgId), eq(mcpConnections.id, connectionId)))
|
|
607
|
+
.for("update")
|
|
608
|
+
.then((rows) => rows[0] ?? null);
|
|
609
|
+
if (!locked)
|
|
610
|
+
throw notFound("MCP connection not found");
|
|
611
|
+
if (locked.credentialSecretId !== existing.credentialSecretId) {
|
|
612
|
+
throw conflict("Managed MCP credentials changed; retry the connection update");
|
|
613
|
+
}
|
|
614
|
+
if (patch.enabled === true
|
|
615
|
+
&& RECONNECT_REQUIRED_STATUSES.has(locked.status)) {
|
|
616
|
+
throw reconnectRequiredError();
|
|
617
|
+
}
|
|
618
|
+
if (locked.provider === "linear"
|
|
619
|
+
&& patch.accessMode !== undefined
|
|
620
|
+
&& patch.accessMode !== locked.accessMode
|
|
621
|
+
&& (locked.status === "active" || locked.status === "authorizing")) {
|
|
622
|
+
throw unprocessable("Linear access-mode changes require reauthorization: disconnect, select the new access mode, then reconnect OAuth");
|
|
623
|
+
}
|
|
624
|
+
if (replacement) {
|
|
625
|
+
await tx.insert(organizationSecrets).values({
|
|
626
|
+
id: replacement.id,
|
|
627
|
+
orgId,
|
|
628
|
+
name: replacement.name,
|
|
629
|
+
provider: "local_encrypted",
|
|
630
|
+
purpose: "managed_mcp_connection",
|
|
631
|
+
externalRef: replacement.externalRef,
|
|
632
|
+
latestVersion: 1,
|
|
633
|
+
description: "Encrypted managed MCP credentials",
|
|
634
|
+
createdByAgentId: actor.agentId ?? null,
|
|
635
|
+
createdByUserId: actor.userId ?? null,
|
|
636
|
+
});
|
|
637
|
+
await tx.insert(organizationSecretVersions).values({
|
|
638
|
+
secretId: replacement.id,
|
|
639
|
+
version: 1,
|
|
640
|
+
material: replacement.material,
|
|
641
|
+
valueSha256: replacement.valueSha256,
|
|
642
|
+
createdByAgentId: actor.agentId ?? null,
|
|
643
|
+
createdByUserId: actor.userId ?? null,
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
const updated = await tx
|
|
647
|
+
.update(mcpConnections)
|
|
648
|
+
.set({
|
|
649
|
+
credentialSecretId,
|
|
650
|
+
...(patch.displayName !== undefined ? { displayName: patch.displayName } : {}),
|
|
651
|
+
...(patch.externalScope !== undefined ? { externalScope: patch.externalScope ?? null } : {}),
|
|
652
|
+
...(patch.accessMode !== undefined ? { accessMode: patch.accessMode } : {}),
|
|
653
|
+
...(patch.safeConfig !== undefined ? { safeConfig: patch.safeConfig } : {}),
|
|
654
|
+
...(patch.startupTimeoutMs !== undefined ? { startupTimeoutMs: patch.startupTimeoutMs } : {}),
|
|
655
|
+
...(patch.toolTimeoutMs !== undefined ? { toolTimeoutMs: patch.toolTimeoutMs } : {}),
|
|
656
|
+
...(patch.enabled !== undefined ? {
|
|
657
|
+
enabled: patch.enabled,
|
|
658
|
+
disabledAt: patch.enabled ? null : new Date(),
|
|
659
|
+
status: patch.enabled ? locked.status : "disabled",
|
|
660
|
+
} : {}),
|
|
661
|
+
...(patch.required !== undefined ? { required: patch.required } : {}),
|
|
662
|
+
lifecycleRevision: locked.lifecycleRevision + 1,
|
|
663
|
+
updatedAt: nextConnectionMutationTime(locked),
|
|
664
|
+
})
|
|
665
|
+
.where(and(eq(mcpConnections.orgId, orgId), eq(mcpConnections.id, connectionId)))
|
|
666
|
+
.returning()
|
|
667
|
+
.then((rows) => rows[0] ?? null);
|
|
668
|
+
if (!updated)
|
|
669
|
+
throw notFound("MCP connection not found");
|
|
670
|
+
if (oldSecretIdToDelete) {
|
|
671
|
+
await tx
|
|
672
|
+
.delete(organizationSecrets)
|
|
673
|
+
.where(eq(organizationSecrets.id, oldSecretIdToDelete));
|
|
674
|
+
}
|
|
675
|
+
await tx.insert(activityLog).values(managedMcpActivityValues({
|
|
676
|
+
orgId,
|
|
677
|
+
connectionId,
|
|
678
|
+
action: "mcp_connection.updated",
|
|
679
|
+
actor,
|
|
680
|
+
details: {
|
|
681
|
+
provider: updated.provider,
|
|
682
|
+
status: updated.status,
|
|
683
|
+
accessMode: updated.accessMode,
|
|
684
|
+
},
|
|
685
|
+
}));
|
|
686
|
+
return updated;
|
|
687
|
+
});
|
|
688
|
+
return publicSummary(row);
|
|
689
|
+
},
|
|
690
|
+
reconnect: async (orgId, connectionId, actor = {}) => {
|
|
691
|
+
const existing = await findRow(orgId, connectionId);
|
|
692
|
+
assertLegacyMutable(existing);
|
|
693
|
+
const now = nextConnectionMutationTime(existing);
|
|
694
|
+
const row = await db.transaction(async (tx) => {
|
|
695
|
+
const updated = await tx
|
|
696
|
+
.update(mcpConnections)
|
|
697
|
+
.set({
|
|
698
|
+
status: existing.provider === "custom" ? "draft" : "authorizing",
|
|
699
|
+
enabled: true,
|
|
700
|
+
disabledAt: null,
|
|
701
|
+
revokedAt: null,
|
|
702
|
+
lifecycleRevision: sql `${mcpConnections.lifecycleRevision} + 1`,
|
|
703
|
+
updatedAt: now,
|
|
704
|
+
})
|
|
705
|
+
.where(and(eq(mcpConnections.orgId, orgId), eq(mcpConnections.id, connectionId)))
|
|
706
|
+
.returning()
|
|
707
|
+
.then((rows) => rows[0] ?? null);
|
|
708
|
+
if (!updated)
|
|
709
|
+
throw notFound("MCP connection not found");
|
|
710
|
+
if (existing.provider !== "custom") {
|
|
711
|
+
const activeGrant = await tx.select().from(mcpOAuthGrants)
|
|
712
|
+
.where(and(eq(mcpOAuthGrants.orgId, orgId), eq(mcpOAuthGrants.connectionId, connectionId), eq(mcpOAuthGrants.status, "active")))
|
|
713
|
+
.for("update")
|
|
714
|
+
.then((rows) => rows[0] ?? null);
|
|
715
|
+
if (activeGrant) {
|
|
716
|
+
await tx.update(mcpOAuthGrants).set({
|
|
717
|
+
status: "needs_reauth",
|
|
718
|
+
statusMetadata: { reason: "connection_reconnect" },
|
|
719
|
+
credentialSecretId: null,
|
|
720
|
+
refreshLeaseNonce: null,
|
|
721
|
+
refreshLeaseExpiresAt: null,
|
|
722
|
+
updatedAt: now,
|
|
723
|
+
}).where(eq(mcpOAuthGrants.id, activeGrant.id));
|
|
724
|
+
if (activeGrant.credentialSecretId) {
|
|
725
|
+
await tx.delete(organizationSecrets)
|
|
726
|
+
.where(eq(organizationSecrets.id, activeGrant.credentialSecretId));
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
await tx.insert(activityLog).values(managedMcpActivityValues({
|
|
731
|
+
orgId,
|
|
732
|
+
connectionId,
|
|
733
|
+
action: "mcp_connection.reconnect_requested",
|
|
734
|
+
actor,
|
|
735
|
+
details: {
|
|
736
|
+
provider: updated.provider,
|
|
737
|
+
status: updated.status,
|
|
738
|
+
},
|
|
739
|
+
}));
|
|
740
|
+
return updated;
|
|
741
|
+
});
|
|
742
|
+
return publicSummary(row);
|
|
743
|
+
},
|
|
744
|
+
disconnect: async (orgId, connectionId, actor = {}) => {
|
|
745
|
+
const existing = await findRow(orgId, connectionId);
|
|
746
|
+
assertLegacyMutable(existing);
|
|
747
|
+
const now = nextConnectionMutationTime(existing);
|
|
748
|
+
const row = await db.transaction(async (tx) => {
|
|
749
|
+
const updated = await tx
|
|
750
|
+
.update(mcpConnections)
|
|
751
|
+
.set({
|
|
752
|
+
status: "disabled",
|
|
753
|
+
enabled: false,
|
|
754
|
+
disabledAt: now,
|
|
755
|
+
lifecycleRevision: sql `${mcpConnections.lifecycleRevision} + 1`,
|
|
756
|
+
updatedAt: now,
|
|
757
|
+
})
|
|
758
|
+
.where(and(eq(mcpConnections.orgId, orgId), eq(mcpConnections.id, connectionId)))
|
|
759
|
+
.returning()
|
|
760
|
+
.then((rows) => rows[0] ?? null);
|
|
761
|
+
if (!updated)
|
|
762
|
+
throw notFound("MCP connection not found");
|
|
763
|
+
await tx.insert(activityLog).values(managedMcpActivityValues({
|
|
764
|
+
orgId,
|
|
765
|
+
connectionId,
|
|
766
|
+
action: "mcp_connection.disconnected",
|
|
767
|
+
actor,
|
|
768
|
+
details: {
|
|
769
|
+
provider: updated.provider,
|
|
770
|
+
status: updated.status,
|
|
771
|
+
},
|
|
772
|
+
}));
|
|
773
|
+
return updated;
|
|
774
|
+
});
|
|
775
|
+
return publicSummary(row);
|
|
776
|
+
},
|
|
777
|
+
listTools,
|
|
778
|
+
refreshTools: async (orgId, connectionId, actor = {}) => {
|
|
779
|
+
const existing = await findRow(orgId, connectionId);
|
|
780
|
+
assertLegacyMutable(existing);
|
|
781
|
+
if (existing.provider === "supabase" && !existing.externalScope) {
|
|
782
|
+
throw unprocessable("Supabase MCP connections require a selected project before discovery");
|
|
783
|
+
}
|
|
784
|
+
if (!canDiscoverTools(existing)) {
|
|
785
|
+
throw discoveryNotReadyError();
|
|
786
|
+
}
|
|
787
|
+
const snapshot = discoverySnapshot(existing);
|
|
788
|
+
let client = null;
|
|
789
|
+
try {
|
|
790
|
+
client = await createClient(await buildClientOptions(existing));
|
|
791
|
+
const normalized = normalizeMcpDiscoveredTools(existing.name, await client.discoverTools(), discoveryFilter(existing));
|
|
792
|
+
const normalizedByName = new Map(normalized.map((tool) => [tool.externalToolName, tool]));
|
|
793
|
+
await db.transaction(async (tx) => {
|
|
794
|
+
const locked = await tx
|
|
795
|
+
.select()
|
|
796
|
+
.from(mcpConnections)
|
|
797
|
+
.where(and(eq(mcpConnections.orgId, orgId), eq(mcpConnections.id, connectionId)))
|
|
798
|
+
.for("update")
|
|
799
|
+
.then((rows) => rows[0] ?? null);
|
|
800
|
+
if (!locked)
|
|
801
|
+
throw notFound("MCP connection not found");
|
|
802
|
+
assertDiscoverySnapshot(locked, snapshot);
|
|
803
|
+
const now = nextConnectionMutationTime(locked);
|
|
804
|
+
const existingTools = await tx
|
|
805
|
+
.select()
|
|
806
|
+
.from(customIntegrationTools)
|
|
807
|
+
.where(and(eq(customIntegrationTools.orgId, orgId), eq(customIntegrationTools.connectionId, connectionId)));
|
|
808
|
+
const reconciled = reconcileMcpToolCatalog(existingTools, normalized);
|
|
809
|
+
const existingByName = new Map(existingTools.map((tool) => [tool.externalToolName, tool]));
|
|
810
|
+
for (const tool of reconciled) {
|
|
811
|
+
const prior = existingByName.get(tool.externalToolName);
|
|
812
|
+
const current = normalizedByName.get(tool.externalToolName);
|
|
813
|
+
if (prior) {
|
|
814
|
+
await tx
|
|
815
|
+
.update(customIntegrationTools)
|
|
816
|
+
.set({
|
|
817
|
+
...(current ? {
|
|
818
|
+
rudderToolName: current.rudderToolName,
|
|
819
|
+
description: current.description,
|
|
820
|
+
rawInputSchema: current.rawInputSchema,
|
|
821
|
+
inputSchema: current.inputSchema,
|
|
822
|
+
rawOutputSchema: current.rawOutputSchema,
|
|
823
|
+
outputSchema: current.outputSchema,
|
|
824
|
+
discoveredAt: now,
|
|
825
|
+
} : {}),
|
|
826
|
+
status: tool.status,
|
|
827
|
+
enabled: tool.enabled,
|
|
828
|
+
removedAt: tool.status === "removed" ? now : null,
|
|
829
|
+
updatedAt: now,
|
|
830
|
+
})
|
|
831
|
+
.where(eq(customIntegrationTools.id, prior.id));
|
|
832
|
+
}
|
|
833
|
+
else if (current) {
|
|
834
|
+
await tx.insert(customIntegrationTools).values({
|
|
835
|
+
orgId,
|
|
836
|
+
connectionId,
|
|
837
|
+
externalToolName: current.externalToolName,
|
|
838
|
+
rudderToolName: current.rudderToolName,
|
|
839
|
+
description: current.description,
|
|
840
|
+
rawInputSchema: current.rawInputSchema,
|
|
841
|
+
inputSchema: current.inputSchema,
|
|
842
|
+
rawOutputSchema: current.rawOutputSchema,
|
|
843
|
+
outputSchema: current.outputSchema,
|
|
844
|
+
status: "active",
|
|
845
|
+
enabled: true,
|
|
846
|
+
discoveredAt: now,
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
await tx
|
|
851
|
+
.update(mcpConnections)
|
|
852
|
+
.set({
|
|
853
|
+
status: "active",
|
|
854
|
+
enabled: true,
|
|
855
|
+
activatedAt: locked.activatedAt ?? now,
|
|
856
|
+
lastDiscoveredAt: now,
|
|
857
|
+
disabledAt: null,
|
|
858
|
+
lifecycleRevision: locked.lifecycleRevision + 1,
|
|
859
|
+
updatedAt: now,
|
|
860
|
+
})
|
|
861
|
+
.where(and(eq(mcpConnections.orgId, orgId), eq(mcpConnections.id, connectionId)));
|
|
862
|
+
await tx.insert(activityLog).values(managedMcpActivityValues({
|
|
863
|
+
orgId,
|
|
864
|
+
connectionId,
|
|
865
|
+
action: "mcp_connection.tools_refreshed",
|
|
866
|
+
actor,
|
|
867
|
+
details: {
|
|
868
|
+
provider: locked.provider,
|
|
869
|
+
toolCount: normalized.length,
|
|
870
|
+
},
|
|
871
|
+
}));
|
|
872
|
+
});
|
|
873
|
+
return listTools(orgId, connectionId);
|
|
874
|
+
}
|
|
875
|
+
catch (error) {
|
|
876
|
+
if (error instanceof ManagedMcpDiscoveryStaleError) {
|
|
877
|
+
throw error;
|
|
878
|
+
}
|
|
879
|
+
await db.transaction(async (tx) => {
|
|
880
|
+
const locked = await tx
|
|
881
|
+
.select()
|
|
882
|
+
.from(mcpConnections)
|
|
883
|
+
.where(and(eq(mcpConnections.orgId, orgId), eq(mcpConnections.id, connectionId)))
|
|
884
|
+
.for("update")
|
|
885
|
+
.then((rows) => rows[0] ?? null);
|
|
886
|
+
if (!locked)
|
|
887
|
+
throw notFound("MCP connection not found");
|
|
888
|
+
assertDiscoverySnapshot(locked, snapshot);
|
|
889
|
+
const now = nextConnectionMutationTime(locked);
|
|
890
|
+
await tx
|
|
891
|
+
.update(mcpConnections)
|
|
892
|
+
.set({
|
|
893
|
+
status: "error",
|
|
894
|
+
lifecycleRevision: locked.lifecycleRevision + 1,
|
|
895
|
+
updatedAt: now,
|
|
896
|
+
})
|
|
897
|
+
.where(and(eq(mcpConnections.orgId, orgId), eq(mcpConnections.id, connectionId)));
|
|
898
|
+
await tx.insert(activityLog).values(managedMcpActivityValues({
|
|
899
|
+
orgId,
|
|
900
|
+
connectionId,
|
|
901
|
+
action: "mcp_connection.discovery_failed",
|
|
902
|
+
actor,
|
|
903
|
+
details: {
|
|
904
|
+
provider: locked.provider,
|
|
905
|
+
reason: "upstream_discovery_failed",
|
|
906
|
+
},
|
|
907
|
+
}));
|
|
908
|
+
});
|
|
909
|
+
if (error instanceof HttpError) {
|
|
910
|
+
throw error;
|
|
911
|
+
}
|
|
912
|
+
throw redactedDiscoveryError();
|
|
913
|
+
}
|
|
914
|
+
finally {
|
|
915
|
+
await client?.close().catch(() => undefined);
|
|
916
|
+
}
|
|
917
|
+
},
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
//# sourceMappingURL=managed-connections.js.map
|