@shipfox/api-integration-core 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +264 -0
  5. package/LICENSE +21 -0
  6. package/dist/config.d.ts +9 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +29 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/core/agent-tool-selection.d.ts +15 -0
  11. package/dist/core/agent-tool-selection.d.ts.map +1 -0
  12. package/dist/core/agent-tool-selection.js +48 -0
  13. package/dist/core/agent-tool-selection.js.map +1 -0
  14. package/dist/core/entities/connection.d.ts +2 -0
  15. package/dist/core/entities/connection.d.ts.map +1 -0
  16. package/dist/core/entities/connection.js +3 -0
  17. package/dist/core/entities/connection.js.map +1 -0
  18. package/dist/core/entities/provider.d.ts +6 -0
  19. package/dist/core/entities/provider.d.ts.map +1 -0
  20. package/dist/core/entities/provider.js +3 -0
  21. package/dist/core/entities/provider.js.map +1 -0
  22. package/dist/core/errors.d.ts +29 -0
  23. package/dist/core/errors.d.ts.map +1 -0
  24. package/dist/core/errors.js +40 -0
  25. package/dist/core/errors.js.map +1 -0
  26. package/dist/core/providers/agent-tools.d.ts +2 -0
  27. package/dist/core/providers/agent-tools.d.ts.map +1 -0
  28. package/dist/core/providers/agent-tools.js +3 -0
  29. package/dist/core/providers/agent-tools.js.map +1 -0
  30. package/dist/core/providers/redact-checkout-spec.d.ts +9 -0
  31. package/dist/core/providers/redact-checkout-spec.d.ts.map +1 -0
  32. package/dist/core/providers/redact-checkout-spec.js +25 -0
  33. package/dist/core/providers/redact-checkout-spec.js.map +1 -0
  34. package/dist/core/providers/registry.d.ts +9 -0
  35. package/dist/core/providers/registry.d.ts.map +1 -0
  36. package/dist/core/providers/registry.js +52 -0
  37. package/dist/core/providers/registry.js.map +1 -0
  38. package/dist/core/providers/source-control.d.ts +2 -0
  39. package/dist/core/providers/source-control.d.ts.map +1 -0
  40. package/dist/core/providers/source-control.js +3 -0
  41. package/dist/core/providers/source-control.js.map +1 -0
  42. package/dist/core/source-control-service.d.ts +46 -0
  43. package/dist/core/source-control-service.d.ts.map +1 -0
  44. package/dist/core/source-control-service.js +84 -0
  45. package/dist/core/source-control-service.js.map +1 -0
  46. package/dist/db/connections.d.ts +67 -0
  47. package/dist/db/connections.d.ts.map +1 -0
  48. package/dist/db/connections.js +130 -0
  49. package/dist/db/connections.js.map +1 -0
  50. package/dist/db/db.d.ts +865 -0
  51. package/dist/db/db.d.ts.map +1 -0
  52. package/dist/db/db.js +22 -0
  53. package/dist/db/db.js.map +1 -0
  54. package/dist/db/migrations.d.ts +2 -0
  55. package/dist/db/migrations.d.ts.map +1 -0
  56. package/dist/db/migrations.js +5 -0
  57. package/dist/db/migrations.js.map +1 -0
  58. package/dist/db/schema/common.d.ts +2 -0
  59. package/dist/db/schema/common.d.ts.map +1 -0
  60. package/dist/db/schema/common.js +4 -0
  61. package/dist/db/schema/common.js.map +1 -0
  62. package/dist/db/schema/connections.d.ts +165 -0
  63. package/dist/db/schema/connections.d.ts.map +1 -0
  64. package/dist/db/schema/connections.js +37 -0
  65. package/dist/db/schema/connections.js.map +1 -0
  66. package/dist/db/schema/outbox.d.ts +195 -0
  67. package/dist/db/schema/outbox.d.ts.map +1 -0
  68. package/dist/db/schema/outbox.js +5 -0
  69. package/dist/db/schema/outbox.js.map +1 -0
  70. package/dist/db/schema/webhook-deliveries.d.ts +76 -0
  71. package/dist/db/schema/webhook-deliveries.d.ts.map +1 -0
  72. package/dist/db/schema/webhook-deliveries.js +22 -0
  73. package/dist/db/schema/webhook-deliveries.js.map +1 -0
  74. package/dist/db/webhook-deliveries.d.ts +55 -0
  75. package/dist/db/webhook-deliveries.d.ts.map +1 -0
  76. package/dist/db/webhook-deliveries.js +132 -0
  77. package/dist/db/webhook-deliveries.js.map +1 -0
  78. package/dist/index.d.ts +54 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +105 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/metrics/index.d.ts +2 -0
  83. package/dist/metrics/index.d.ts.map +1 -0
  84. package/dist/metrics/index.js +3 -0
  85. package/dist/metrics/index.js.map +1 -0
  86. package/dist/metrics/instance.d.ts +8 -0
  87. package/dist/metrics/instance.d.ts.map +1 -0
  88. package/dist/metrics/instance.js +17 -0
  89. package/dist/metrics/instance.js.map +1 -0
  90. package/dist/presentation/dto/integrations.d.ts +36 -0
  91. package/dist/presentation/dto/integrations.d.ts.map +1 -0
  92. package/dist/presentation/dto/integrations.js +26 -0
  93. package/dist/presentation/dto/integrations.js.map +1 -0
  94. package/dist/presentation/routes/agent-tools-gateway/audit.d.ts +24 -0
  95. package/dist/presentation/routes/agent-tools-gateway/audit.d.ts.map +1 -0
  96. package/dist/presentation/routes/agent-tools-gateway/audit.js +52 -0
  97. package/dist/presentation/routes/agent-tools-gateway/audit.js.map +1 -0
  98. package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts +7 -0
  99. package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts.map +1 -0
  100. package/dist/presentation/routes/agent-tools-gateway/dispatch.js +108 -0
  101. package/dist/presentation/routes/agent-tools-gateway/dispatch.js.map +1 -0
  102. package/dist/presentation/routes/agent-tools-gateway/index.d.ts +12 -0
  103. package/dist/presentation/routes/agent-tools-gateway/index.d.ts.map +1 -0
  104. package/dist/presentation/routes/agent-tools-gateway/index.js +46 -0
  105. package/dist/presentation/routes/agent-tools-gateway/index.js.map +1 -0
  106. package/dist/presentation/routes/agent-tools-gateway/mcp-server.d.ts +17 -0
  107. package/dist/presentation/routes/agent-tools-gateway/mcp-server.d.ts.map +1 -0
  108. package/dist/presentation/routes/agent-tools-gateway/mcp-server.js +126 -0
  109. package/dist/presentation/routes/agent-tools-gateway/mcp-server.js.map +1 -0
  110. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts +37 -0
  111. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts.map +1 -0
  112. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js +145 -0
  113. package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js.map +1 -0
  114. package/dist/presentation/routes/errors.d.ts +2 -0
  115. package/dist/presentation/routes/errors.d.ts.map +1 -0
  116. package/dist/presentation/routes/errors.js +53 -0
  117. package/dist/presentation/routes/errors.js.map +1 -0
  118. package/dist/presentation/routes/index.d.ts +14 -0
  119. package/dist/presentation/routes/index.d.ts.map +1 -0
  120. package/dist/presentation/routes/index.js +26 -0
  121. package/dist/presentation/routes/index.js.map +1 -0
  122. package/dist/presentation/routes/list-connections.d.ts +3 -0
  123. package/dist/presentation/routes/list-connections.d.ts.map +1 -0
  124. package/dist/presentation/routes/list-connections.js +58 -0
  125. package/dist/presentation/routes/list-connections.js.map +1 -0
  126. package/dist/presentation/routes/list-providers.d.ts +3 -0
  127. package/dist/presentation/routes/list-providers.d.ts.map +1 -0
  128. package/dist/presentation/routes/list-providers.js +27 -0
  129. package/dist/presentation/routes/list-providers.js.map +1 -0
  130. package/dist/presentation/routes/list-repositories.d.ts +3 -0
  131. package/dist/presentation/routes/list-repositories.d.ts.map +1 -0
  132. package/dist/presentation/routes/list-repositories.js +41 -0
  133. package/dist/presentation/routes/list-repositories.js.map +1 -0
  134. package/dist/presentation/routes/manage-connections.d.ts +4 -0
  135. package/dist/presentation/routes/manage-connections.d.ts.map +1 -0
  136. package/dist/presentation/routes/manage-connections.js +81 -0
  137. package/dist/presentation/routes/manage-connections.js.map +1 -0
  138. package/dist/providers/connection-slug.d.ts +2 -0
  139. package/dist/providers/connection-slug.d.ts.map +1 -0
  140. package/dist/providers/connection-slug.js +19 -0
  141. package/dist/providers/connection-slug.js.map +1 -0
  142. package/dist/providers/cron.d.ts +3 -0
  143. package/dist/providers/cron.d.ts.map +1 -0
  144. package/dist/providers/cron.js +13 -0
  145. package/dist/providers/cron.js.map +1 -0
  146. package/dist/providers/gitea.d.ts +3 -0
  147. package/dist/providers/gitea.d.ts.map +1 -0
  148. package/dist/providers/gitea.js +75 -0
  149. package/dist/providers/gitea.js.map +1 -0
  150. package/dist/providers/github.d.ts +3 -0
  151. package/dist/providers/github.d.ts.map +1 -0
  152. package/dist/providers/github.js +109 -0
  153. package/dist/providers/github.js.map +1 -0
  154. package/dist/providers/linear.d.ts +3 -0
  155. package/dist/providers/linear.d.ts.map +1 -0
  156. package/dist/providers/linear.js +134 -0
  157. package/dist/providers/linear.js.map +1 -0
  158. package/dist/providers/modules.d.ts +3 -0
  159. package/dist/providers/modules.d.ts.map +1 -0
  160. package/dist/providers/modules.js +26 -0
  161. package/dist/providers/modules.js.map +1 -0
  162. package/dist/providers/sentry.d.ts +3 -0
  163. package/dist/providers/sentry.d.ts.map +1 -0
  164. package/dist/providers/sentry.js +83 -0
  165. package/dist/providers/sentry.js.map +1 -0
  166. package/dist/providers/types.d.ts +58 -0
  167. package/dist/providers/types.d.ts.map +1 -0
  168. package/dist/providers/types.js +3 -0
  169. package/dist/providers/types.js.map +1 -0
  170. package/dist/providers/webhook.d.ts +3 -0
  171. package/dist/providers/webhook.d.ts.map +1 -0
  172. package/dist/providers/webhook.js +24 -0
  173. package/dist/providers/webhook.js.map +1 -0
  174. package/dist/temporal/activities/index.d.ts +5 -0
  175. package/dist/temporal/activities/index.d.ts.map +1 -0
  176. package/dist/temporal/activities/index.js +8 -0
  177. package/dist/temporal/activities/index.js.map +1 -0
  178. package/dist/temporal/activities/prune-webhook-deliveries.d.ts +4 -0
  179. package/dist/temporal/activities/prune-webhook-deliveries.d.ts.map +1 -0
  180. package/dist/temporal/activities/prune-webhook-deliveries.js +11 -0
  181. package/dist/temporal/activities/prune-webhook-deliveries.js.map +1 -0
  182. package/dist/temporal/constants.d.ts +3 -0
  183. package/dist/temporal/constants.d.ts.map +1 -0
  184. package/dist/temporal/constants.js +4 -0
  185. package/dist/temporal/constants.js.map +1 -0
  186. package/dist/temporal/workflows/index.d.ts +2 -0
  187. package/dist/temporal/workflows/index.d.ts.map +1 -0
  188. package/dist/temporal/workflows/index.js +3 -0
  189. package/dist/temporal/workflows/index.js.map +1 -0
  190. package/dist/temporal/workflows/prune-webhook-deliveries-cron.d.ts +2 -0
  191. package/dist/temporal/workflows/prune-webhook-deliveries-cron.d.ts.map +1 -0
  192. package/dist/temporal/workflows/prune-webhook-deliveries-cron.js +14 -0
  193. package/dist/temporal/workflows/prune-webhook-deliveries-cron.js.map +1 -0
  194. package/dist/tsconfig.test.tsbuildinfo +1 -0
  195. package/drizzle/0000_initial.sql +41 -0
  196. package/drizzle/meta/0001_snapshot.json +341 -0
  197. package/drizzle/meta/_journal.json +13 -0
  198. package/drizzle.config.ts +7 -0
  199. package/package.json +85 -0
  200. package/src/config.test.ts +32 -0
  201. package/src/config.ts +28 -0
  202. package/src/core/agent-tool-selection.test.ts +137 -0
  203. package/src/core/agent-tool-selection.ts +80 -0
  204. package/src/core/entities/connection.ts +4 -0
  205. package/src/core/entities/provider.ts +21 -0
  206. package/src/core/errors.ts +55 -0
  207. package/src/core/providers/agent-tools.ts +10 -0
  208. package/src/core/providers/redact-checkout-spec.test.ts +78 -0
  209. package/src/core/providers/redact-checkout-spec.ts +20 -0
  210. package/src/core/providers/registry.test.ts +138 -0
  211. package/src/core/providers/registry.ts +90 -0
  212. package/src/core/providers/source-control.ts +17 -0
  213. package/src/core/source-control-service.test.ts +230 -0
  214. package/src/core/source-control-service.ts +161 -0
  215. package/src/db/connections.test.ts +296 -0
  216. package/src/db/connections.ts +262 -0
  217. package/src/db/db.ts +22 -0
  218. package/src/db/migrations.ts +4 -0
  219. package/src/db/schema/common.ts +3 -0
  220. package/src/db/schema/connections.ts +49 -0
  221. package/src/db/schema/outbox.ts +4 -0
  222. package/src/db/schema/webhook-deliveries.ts +19 -0
  223. package/src/db/webhook-deliveries.test.ts +267 -0
  224. package/src/db/webhook-deliveries.ts +207 -0
  225. package/src/index.ts +212 -0
  226. package/src/metrics/index.ts +1 -0
  227. package/src/metrics/instance.ts +35 -0
  228. package/src/presentation/dto/integrations.ts +39 -0
  229. package/src/presentation/routes/agent-tools-gateway/agent-tools-gateway.route.test.ts +371 -0
  230. package/src/presentation/routes/agent-tools-gateway/audit.test.ts +86 -0
  231. package/src/presentation/routes/agent-tools-gateway/audit.ts +90 -0
  232. package/src/presentation/routes/agent-tools-gateway/dispatch.ts +139 -0
  233. package/src/presentation/routes/agent-tools-gateway/index.ts +62 -0
  234. package/src/presentation/routes/agent-tools-gateway/mcp-server.test.ts +308 -0
  235. package/src/presentation/routes/agent-tools-gateway/mcp-server.ts +155 -0
  236. package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.test.ts +181 -0
  237. package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.ts +245 -0
  238. package/src/presentation/routes/errors.ts +63 -0
  239. package/src/presentation/routes/index.ts +53 -0
  240. package/src/presentation/routes/list-connections.test.ts +227 -0
  241. package/src/presentation/routes/list-connections.ts +56 -0
  242. package/src/presentation/routes/list-providers.test.ts +113 -0
  243. package/src/presentation/routes/list-providers.ts +28 -0
  244. package/src/presentation/routes/list-repositories.test.ts +150 -0
  245. package/src/presentation/routes/list-repositories.ts +47 -0
  246. package/src/presentation/routes/manage-connections.test.ts +125 -0
  247. package/src/presentation/routes/manage-connections.ts +77 -0
  248. package/src/providers/connection-slug.ts +19 -0
  249. package/src/providers/cron.ts +13 -0
  250. package/src/providers/gitea.ts +103 -0
  251. package/src/providers/github.ts +150 -0
  252. package/src/providers/linear.ts +179 -0
  253. package/src/providers/modules.test.ts +40 -0
  254. package/src/providers/modules.ts +32 -0
  255. package/src/providers/sentry.ts +113 -0
  256. package/src/providers/types.ts +52 -0
  257. package/src/providers/webhook.ts +30 -0
  258. package/src/temporal/activities/index.ts +7 -0
  259. package/src/temporal/activities/prune-webhook-deliveries.test.ts +39 -0
  260. package/src/temporal/activities/prune-webhook-deliveries.ts +9 -0
  261. package/src/temporal/constants.ts +3 -0
  262. package/src/temporal/workflows/index.ts +1 -0
  263. package/src/temporal/workflows/prune-webhook-deliveries-cron.ts +15 -0
  264. package/test/agent-tools-gateway-helpers.ts +210 -0
  265. package/test/env.ts +23 -0
  266. package/test/factories/connection.ts +21 -0
  267. package/test/globalSetup.ts +22 -0
  268. package/test/index.ts +1 -0
  269. package/test/route-utils.ts +104 -0
  270. package/test/setup.ts +19 -0
  271. package/tsconfig.build.json +9 -0
  272. package/tsconfig.build.tsbuildinfo +1 -0
  273. package/tsconfig.json +3 -0
  274. package/tsconfig.test.json +8 -0
  275. package/vitest.config.ts +13 -0
@@ -0,0 +1,108 @@
1
+ import { IntegrationProviderError } from '#core/errors.js';
2
+ const timeoutErrorPattern = /timed?\s*out|timeout/i;
3
+ const credentialErrorNamePattern = /Token|Credential|Secret|AccessToken/;
4
+ export function createIntegrationToolDispatcher(params) {
5
+ return (input)=>dispatchIntegrationToolCall({
6
+ ...input,
7
+ registry: params.registry
8
+ });
9
+ }
10
+ async function dispatchIntegrationToolCall(input) {
11
+ let session;
12
+ try {
13
+ const adapter = input.registry.getAdapter(input.authorizedTool.integration.provider, 'agent_tools');
14
+ session = await adapter.openSession({
15
+ connection: input.authorizedTool.connection,
16
+ tools: [
17
+ agentToolCatalogEntry(input)
18
+ ],
19
+ scope: input.authorizedTool.integration
20
+ });
21
+ return await session.call({
22
+ toolId: input.authorizedTool.tool.id,
23
+ arguments: input.arguments
24
+ });
25
+ } catch (error) {
26
+ return toolError(errorResult(error));
27
+ } finally{
28
+ await closeSession(session);
29
+ }
30
+ }
31
+ function agentToolCatalogEntry(input) {
32
+ const { tool, description, inputSchema, outputSchema } = input.authorizedTool;
33
+ return {
34
+ id: tool.id,
35
+ description,
36
+ sensitivity: tool.sensitivity,
37
+ sensitive: tool.sensitive,
38
+ requiredScope: tool.requiredScope,
39
+ inputSchema,
40
+ ...outputSchema === undefined ? {} : {
41
+ outputSchema
42
+ },
43
+ ...tool.methods === undefined ? {} : {
44
+ methods: tool.methods.map((method)=>({
45
+ id: method.id,
46
+ description: method.description ?? method.token,
47
+ sensitivity: method.sensitivity,
48
+ sensitive: method.sensitive,
49
+ requiredScope: method.requiredScope
50
+ }))
51
+ }
52
+ };
53
+ }
54
+ async function closeSession(session) {
55
+ try {
56
+ await session?.close?.();
57
+ } catch {
58
+ // Cleanup must not mask the tool result returned to the runner.
59
+ }
60
+ }
61
+ function errorResult(error) {
62
+ if (error instanceof IntegrationProviderError) {
63
+ return {
64
+ code: error.reason,
65
+ message: `Integration provider error: ${error.reason}`
66
+ };
67
+ }
68
+ if (isTimeoutError(error)) {
69
+ return {
70
+ code: 'provider-timeout',
71
+ message: 'Integration provider timed out'
72
+ };
73
+ }
74
+ if (isCredentialError(error)) {
75
+ return {
76
+ code: 'credentials-unavailable',
77
+ message: 'Integration provider credentials are unavailable'
78
+ };
79
+ }
80
+ return {
81
+ code: 'provider-unavailable',
82
+ message: 'Integration provider call failed'
83
+ };
84
+ }
85
+ function isTimeoutError(error) {
86
+ if (!(error instanceof Error)) return false;
87
+ return error.name === 'AbortError' || timeoutErrorPattern.test(error.name) || timeoutErrorPattern.test(error.message);
88
+ }
89
+ function isCredentialError(error) {
90
+ if (!(error instanceof Error)) return false;
91
+ return credentialErrorNamePattern.test(error.name);
92
+ }
93
+ function toolError(params) {
94
+ return {
95
+ isError: true,
96
+ content: [
97
+ {
98
+ type: 'text',
99
+ text: params.message
100
+ }
101
+ ],
102
+ structuredContent: {
103
+ code: params.code
104
+ }
105
+ };
106
+ }
107
+
108
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/presentation/routes/agent-tools-gateway/dispatch.ts"],"sourcesContent":["import type {CallToolResult} from '@modelcontextprotocol/sdk/types.js';\nimport {IntegrationProviderError} from '#core/errors.js';\nimport type {\n AgentToolCatalogEntry,\n AgentToolCatalogMethod,\n AgentToolSession,\n AgentToolsProvider,\n} from '#core/providers/agent-tools.js';\nimport type {IntegrationProviderRegistry} from '#core/providers/registry.js';\nimport type {IntegrationToolDispatcher, IntegrationToolDispatchInput} from './mcp-server.js';\n\nexport interface CreateIntegrationToolDispatcherParams {\n registry: IntegrationProviderRegistry;\n}\n\nconst timeoutErrorPattern = /timed?\\s*out|timeout/i;\nconst credentialErrorNamePattern = /Token|Credential|Secret|AccessToken/;\n\nexport function createIntegrationToolDispatcher(\n params: CreateIntegrationToolDispatcherParams,\n): IntegrationToolDispatcher {\n return (input) => dispatchIntegrationToolCall({...input, registry: params.registry});\n}\n\nasync function dispatchIntegrationToolCall(\n input: IntegrationToolDispatchInput & {registry: IntegrationProviderRegistry},\n): Promise<CallToolResult> {\n let session: AgentToolSession<CallToolResult> | undefined;\n\n try {\n const adapter = input.registry.getAdapter(\n input.authorizedTool.integration.provider,\n 'agent_tools',\n ) as AgentToolsProvider<\n typeof input.authorizedTool.connection,\n unknown,\n typeof input.authorizedTool.integration,\n CallToolResult\n >;\n session = await adapter.openSession({\n connection: input.authorizedTool.connection,\n tools: [agentToolCatalogEntry(input)],\n scope: input.authorizedTool.integration,\n });\n\n return await session.call({\n toolId: input.authorizedTool.tool.id,\n arguments: input.arguments,\n });\n } catch (error) {\n return toolError(errorResult(error));\n } finally {\n await closeSession(session);\n }\n}\n\nfunction agentToolCatalogEntry(input: IntegrationToolDispatchInput): AgentToolCatalogEntry {\n const {tool, description, inputSchema, outputSchema} = input.authorizedTool;\n return {\n id: tool.id,\n description,\n sensitivity: tool.sensitivity,\n sensitive: tool.sensitive,\n requiredScope: tool.requiredScope,\n inputSchema,\n ...(outputSchema === undefined ? {} : {outputSchema}),\n ...(tool.methods === undefined\n ? {}\n : {\n methods: tool.methods.map(\n (method): AgentToolCatalogMethod => ({\n id: method.id,\n description: method.description ?? method.token,\n sensitivity: method.sensitivity,\n sensitive: method.sensitive,\n requiredScope: method.requiredScope,\n }),\n ),\n }),\n };\n}\n\nasync function closeSession(session: {close?(): Promise<void>} | undefined): Promise<void> {\n try {\n await session?.close?.();\n } catch {\n // Cleanup must not mask the tool result returned to the runner.\n }\n}\n\nfunction errorResult(error: unknown): {code: string; message: string} {\n if (error instanceof IntegrationProviderError) {\n return {\n code: error.reason,\n message: `Integration provider error: ${error.reason}`,\n };\n }\n\n if (isTimeoutError(error)) {\n return {\n code: 'provider-timeout',\n message: 'Integration provider timed out',\n };\n }\n\n if (isCredentialError(error)) {\n return {\n code: 'credentials-unavailable',\n message: 'Integration provider credentials are unavailable',\n };\n }\n\n return {\n code: 'provider-unavailable',\n message: 'Integration provider call failed',\n };\n}\n\nfunction isTimeoutError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n return (\n error.name === 'AbortError' ||\n timeoutErrorPattern.test(error.name) ||\n timeoutErrorPattern.test(error.message)\n );\n}\n\nfunction isCredentialError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n return credentialErrorNamePattern.test(error.name);\n}\n\nfunction toolError(params: {code: string; message: string}): CallToolResult {\n return {\n isError: true,\n content: [{type: 'text', text: params.message}],\n structuredContent: {code: params.code},\n };\n}\n"],"names":["IntegrationProviderError","timeoutErrorPattern","credentialErrorNamePattern","createIntegrationToolDispatcher","params","input","dispatchIntegrationToolCall","registry","session","adapter","getAdapter","authorizedTool","integration","provider","openSession","connection","tools","agentToolCatalogEntry","scope","call","toolId","tool","id","arguments","error","toolError","errorResult","closeSession","description","inputSchema","outputSchema","sensitivity","sensitive","requiredScope","undefined","methods","map","method","token","close","code","reason","message","isTimeoutError","isCredentialError","Error","name","test","isError","content","type","text","structuredContent"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type RouteGroup } from '@shipfox/node-fastify';
2
+ import type { IntegrationProviderRegistry } from '#core/providers/registry.js';
3
+ import type { GetIntegrationConnectionByIdFn } from '#db/connections.js';
4
+ import { type LeasedAgentStepLoader } from './resolve-authorized-tools.js';
5
+ export type { LeasedAgentStepLoader } from './resolve-authorized-tools.js';
6
+ export interface CreateAgentToolsGatewayRoutesParams {
7
+ loadLeasedAgentStep: LeasedAgentStepLoader;
8
+ registry: IntegrationProviderRegistry;
9
+ getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
10
+ }
11
+ export declare function createAgentToolsGatewayRoutes(params: CreateAgentToolsGatewayRoutesParams): RouteGroup;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/agent-tools-gateway/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,6BAA6B,CAAC;AAC7E,OAAO,KAAK,EAAC,8BAA8B,EAAC,MAAM,oBAAoB,CAAC;AAIvE,OAAO,EACL,KAAK,qBAAqB,EAE3B,MAAM,+BAA+B,CAAC;AAEvC,YAAY,EAAC,qBAAqB,EAAC,MAAM,+BAA+B,CAAC;AAEzE,MAAM,WAAW,mCAAmC;IAClD,mBAAmB,EAAE,qBAAqB,CAAC;IAC3C,QAAQ,EAAE,2BAA2B,CAAC;IACtC,4BAA4B,EAAE,8BAA8B,CAAC;CAC9D;AAED,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,mCAAmC,GAC1C,UAAU,CAqCZ"}
@@ -0,0 +1,46 @@
1
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
2
+ import { AUTH_LEASED_JOB, requireLeasedJobContext } from '@shipfox/api-auth-context';
3
+ import { defineRoute } from '@shipfox/node-fastify';
4
+ import { createIntegrationToolCallRecorder } from './audit.js';
5
+ import { createIntegrationToolDispatcher } from './dispatch.js';
6
+ import { buildAgentToolsMcpServer } from './mcp-server.js';
7
+ import { resolveAuthorizedIntegrationTools } from './resolve-authorized-tools.js';
8
+ export function createAgentToolsGatewayRoutes(params) {
9
+ const dispatchIntegrationToolCall = createIntegrationToolDispatcher({
10
+ registry: params.registry
11
+ });
12
+ return {
13
+ prefix: '/runs/jobs/current/integration-tools',
14
+ auth: AUTH_LEASED_JOB,
15
+ routes: [
16
+ defineRoute({
17
+ method: 'POST',
18
+ path: '/mcp',
19
+ description: 'Gateway MCP endpoint for integration-backed agent tools',
20
+ handler: async (request, reply)=>{
21
+ const authorizedTools = await resolveAuthorizedIntegrationTools({
22
+ request,
23
+ loadLeasedAgentStep: params.loadLeasedAgentStep,
24
+ registry: params.registry,
25
+ getIntegrationConnectionById: params.getIntegrationConnectionById
26
+ });
27
+ const server = buildAgentToolsMcpServer({
28
+ authorizedTools,
29
+ dispatch: dispatchIntegrationToolCall,
30
+ recordCall: createIntegrationToolCallRecorder(requireLeasedJobContext(request))
31
+ });
32
+ const transport = new StreamableHTTPServerTransport();
33
+ await server.connect(transport);
34
+ reply.raw.on('close', ()=>{
35
+ void transport.close();
36
+ void server.close();
37
+ });
38
+ reply.hijack();
39
+ await transport.handleRequest(request.raw, reply.raw, request.body);
40
+ }
41
+ })
42
+ ]
43
+ };
44
+ }
45
+
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/presentation/routes/agent-tools-gateway/index.ts"],"sourcesContent":["import {StreamableHTTPServerTransport} from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport type {Transport} from '@modelcontextprotocol/sdk/shared/transport.js';\nimport {AUTH_LEASED_JOB, requireLeasedJobContext} from '@shipfox/api-auth-context';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {IntegrationProviderRegistry} from '#core/providers/registry.js';\nimport type {GetIntegrationConnectionByIdFn} from '#db/connections.js';\nimport {createIntegrationToolCallRecorder} from './audit.js';\nimport {createIntegrationToolDispatcher} from './dispatch.js';\nimport {buildAgentToolsMcpServer} from './mcp-server.js';\nimport {\n type LeasedAgentStepLoader,\n resolveAuthorizedIntegrationTools,\n} from './resolve-authorized-tools.js';\n\nexport type {LeasedAgentStepLoader} from './resolve-authorized-tools.js';\n\nexport interface CreateAgentToolsGatewayRoutesParams {\n loadLeasedAgentStep: LeasedAgentStepLoader;\n registry: IntegrationProviderRegistry;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\nexport function createAgentToolsGatewayRoutes(\n params: CreateAgentToolsGatewayRoutesParams,\n): RouteGroup {\n const dispatchIntegrationToolCall = createIntegrationToolDispatcher({registry: params.registry});\n\n return {\n prefix: '/runs/jobs/current/integration-tools',\n auth: AUTH_LEASED_JOB,\n routes: [\n defineRoute({\n method: 'POST',\n path: '/mcp',\n description: 'Gateway MCP endpoint for integration-backed agent tools',\n handler: async (request, reply) => {\n const authorizedTools = await resolveAuthorizedIntegrationTools({\n request,\n loadLeasedAgentStep: params.loadLeasedAgentStep,\n registry: params.registry,\n getIntegrationConnectionById: params.getIntegrationConnectionById,\n });\n const server = buildAgentToolsMcpServer({\n authorizedTools,\n dispatch: dispatchIntegrationToolCall,\n recordCall: createIntegrationToolCallRecorder(requireLeasedJobContext(request)),\n });\n const transport = new StreamableHTTPServerTransport();\n\n await server.connect(transport as unknown as Transport);\n reply.raw.on('close', () => {\n void transport.close();\n void server.close();\n });\n\n reply.hijack();\n await transport.handleRequest(request.raw, reply.raw, request.body);\n },\n }),\n ],\n };\n}\n"],"names":["StreamableHTTPServerTransport","AUTH_LEASED_JOB","requireLeasedJobContext","defineRoute","createIntegrationToolCallRecorder","createIntegrationToolDispatcher","buildAgentToolsMcpServer","resolveAuthorizedIntegrationTools","createAgentToolsGatewayRoutes","params","dispatchIntegrationToolCall","registry","prefix","auth","routes","method","path","description","handler","request","reply","authorizedTools","loadLeasedAgentStep","getIntegrationConnectionById","server","dispatch","recordCall","transport","connect","raw","on","close","hijack","handleRequest","body"],"mappings":"AAAA,SAAQA,6BAA6B,QAAO,qDAAqD;AAEjG,SAAQC,eAAe,EAAEC,uBAAuB,QAAO,4BAA4B;AACnF,SAAQC,WAAW,QAAwB,wBAAwB;AAGnE,SAAQC,iCAAiC,QAAO,aAAa;AAC7D,SAAQC,+BAA+B,QAAO,gBAAgB;AAC9D,SAAQC,wBAAwB,QAAO,kBAAkB;AACzD,SAEEC,iCAAiC,QAC5B,gCAAgC;AAUvC,OAAO,SAASC,8BACdC,MAA2C;IAE3C,MAAMC,8BAA8BL,gCAAgC;QAACM,UAAUF,OAAOE,QAAQ;IAAA;IAE9F,OAAO;QACLC,QAAQ;QACRC,MAAMZ;QACNa,QAAQ;YACNX,YAAY;gBACVY,QAAQ;gBACRC,MAAM;gBACNC,aAAa;gBACbC,SAAS,OAAOC,SAASC;oBACvB,MAAMC,kBAAkB,MAAMd,kCAAkC;wBAC9DY;wBACAG,qBAAqBb,OAAOa,mBAAmB;wBAC/CX,UAAUF,OAAOE,QAAQ;wBACzBY,8BAA8Bd,OAAOc,4BAA4B;oBACnE;oBACA,MAAMC,SAASlB,yBAAyB;wBACtCe;wBACAI,UAAUf;wBACVgB,YAAYtB,kCAAkCF,wBAAwBiB;oBACxE;oBACA,MAAMQ,YAAY,IAAI3B;oBAEtB,MAAMwB,OAAOI,OAAO,CAACD;oBACrBP,MAAMS,GAAG,CAACC,EAAE,CAAC,SAAS;wBACpB,KAAKH,UAAUI,KAAK;wBACpB,KAAKP,OAAOO,KAAK;oBACnB;oBAEAX,MAAMY,MAAM;oBACZ,MAAML,UAAUM,aAAa,CAACd,QAAQU,GAAG,EAAET,MAAMS,GAAG,EAAEV,QAAQe,IAAI;gBACpE;YACF;SACD;IACH;AACF"}
@@ -0,0 +1,17 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { type CallToolResult } from '@modelcontextprotocol/sdk/types.js';
3
+ import { type IntegrationToolCallRecorder } from './audit.js';
4
+ import type { AuthorizedIntegrationTool, AuthorizedIntegrationToolMap } from './resolve-authorized-tools.js';
5
+ export interface IntegrationToolDispatchInput {
6
+ authorizedTool: AuthorizedIntegrationTool;
7
+ arguments: Record<string, unknown>;
8
+ method?: string | undefined;
9
+ }
10
+ export type IntegrationToolDispatcher = (input: IntegrationToolDispatchInput) => Promise<CallToolResult>;
11
+ export interface BuildAgentToolsMcpServerParams {
12
+ authorizedTools: AuthorizedIntegrationToolMap;
13
+ dispatch: IntegrationToolDispatcher;
14
+ recordCall?: IntegrationToolCallRecorder | undefined;
15
+ }
16
+ export declare function buildAgentToolsMcpServer(params: BuildAgentToolsMcpServerParams): Server;
17
+ //# sourceMappingURL=mcp-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/agent-tools-gateway/mcp-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,2CAA2C,CAAC;AACjE,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAuB,KAAK,2BAA2B,EAAkB,MAAM,YAAY,CAAC;AACnG,OAAO,KAAK,EACV,yBAAyB,EACzB,4BAA4B,EAC7B,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,4BAA4B;IAC3C,cAAc,EAAE,yBAAyB,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,MAAM,yBAAyB,GAAG,CACtC,KAAK,EAAE,4BAA4B,KAChC,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,MAAM,WAAW,8BAA8B;IAC7C,eAAe,EAAE,4BAA4B,CAAC;IAC9C,QAAQ,EAAE,yBAAyB,CAAC;IACpC,UAAU,CAAC,EAAE,2BAA2B,GAAG,SAAS,CAAC;CACtD;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,8BAA8B,GAAG,MAAM,CAqFvF"}
@@ -0,0 +1,126 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
3
+ import { INVALID_METHOD_LABEL, NO_METHOD_LABEL } from './audit.js';
4
+ export function buildAgentToolsMcpServer(params) {
5
+ const server = new Server({
6
+ name: 'shipfox-integration-tools',
7
+ version: '0.0.0'
8
+ }, {
9
+ capabilities: {
10
+ tools: {}
11
+ }
12
+ });
13
+ server.setRequestHandler(ListToolsRequestSchema, ()=>({
14
+ tools: [
15
+ ...params.authorizedTools.values()
16
+ ].map((authorizedTool)=>({
17
+ name: authorizedTool.mcpName,
18
+ description: authorizedTool.description,
19
+ inputSchema: authorizedTool.inputSchema,
20
+ ...authorizedTool.outputSchema ? {
21
+ outputSchema: authorizedTool.outputSchema
22
+ } : {}
23
+ }))
24
+ }));
25
+ server.setRequestHandler(CallToolRequestSchema, async (request)=>{
26
+ const authorizedTool = params.authorizedTools.get(request.params.name);
27
+ if (!authorizedTool) {
28
+ recordToolCall(params.recordCall, {
29
+ arguments: request.params.arguments ?? {},
30
+ method: NO_METHOD_LABEL,
31
+ outcome: 'invalid-request'
32
+ });
33
+ return toolError(`Unknown integration tool: ${request.params.name}`);
34
+ }
35
+ const args = request.params.arguments ?? {};
36
+ if (!isRecord(args)) {
37
+ recordToolCall(params.recordCall, {
38
+ authorizedTool,
39
+ arguments: args,
40
+ method: NO_METHOD_LABEL,
41
+ outcome: 'invalid-request'
42
+ });
43
+ return toolError('Tool arguments must be an object');
44
+ }
45
+ const methodValidation = validateMethod(authorizedTool, args);
46
+ if (methodValidation.kind === 'error') {
47
+ recordToolCall(params.recordCall, {
48
+ authorizedTool,
49
+ arguments: args,
50
+ method: INVALID_METHOD_LABEL,
51
+ outcome: 'invalid-request'
52
+ });
53
+ return toolError(methodValidation.message);
54
+ }
55
+ const method = methodValidation.method ?? NO_METHOD_LABEL;
56
+ try {
57
+ const result = await params.dispatch({
58
+ authorizedTool,
59
+ arguments: args,
60
+ method: methodValidation.method
61
+ });
62
+ recordToolCall(params.recordCall, {
63
+ authorizedTool,
64
+ arguments: args,
65
+ method,
66
+ outcome: result.isError === true ? 'tool-error' : 'success'
67
+ });
68
+ return result;
69
+ } catch (error) {
70
+ recordToolCall(params.recordCall, {
71
+ authorizedTool,
72
+ arguments: args,
73
+ method,
74
+ outcome: 'exception'
75
+ });
76
+ throw error;
77
+ }
78
+ });
79
+ return server;
80
+ }
81
+ function recordToolCall(recordCall, record) {
82
+ try {
83
+ recordCall?.(record);
84
+ } catch {
85
+ // Audit and metrics must not affect MCP responses.
86
+ }
87
+ }
88
+ function validateMethod(authorizedTool, args) {
89
+ if (!authorizedTool.tool.methods) return {
90
+ kind: 'ok'
91
+ };
92
+ const method = args.method;
93
+ if (typeof method !== 'string') {
94
+ return {
95
+ kind: 'error',
96
+ message: 'Method-family tools require a string method argument'
97
+ };
98
+ }
99
+ const allowedMethods = new Set(authorizedTool.tool.methods.map((candidate)=>candidate.id));
100
+ if (!allowedMethods.has(method)) {
101
+ return {
102
+ kind: 'error',
103
+ message: `Unauthorized integration tool method: ${method}`
104
+ };
105
+ }
106
+ return {
107
+ kind: 'ok',
108
+ method
109
+ };
110
+ }
111
+ function toolError(message) {
112
+ return {
113
+ isError: true,
114
+ content: [
115
+ {
116
+ type: 'text',
117
+ text: message
118
+ }
119
+ ]
120
+ };
121
+ }
122
+ function isRecord(value) {
123
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
124
+ }
125
+
126
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/presentation/routes/agent-tools-gateway/mcp-server.ts"],"sourcesContent":["import {Server} from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n type CallToolResult,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport {INVALID_METHOD_LABEL, type IntegrationToolCallRecorder, NO_METHOD_LABEL} from './audit.js';\nimport type {\n AuthorizedIntegrationTool,\n AuthorizedIntegrationToolMap,\n} from './resolve-authorized-tools.js';\n\nexport interface IntegrationToolDispatchInput {\n authorizedTool: AuthorizedIntegrationTool;\n arguments: Record<string, unknown>;\n method?: string | undefined;\n}\n\nexport type IntegrationToolDispatcher = (\n input: IntegrationToolDispatchInput,\n) => Promise<CallToolResult>;\n\nexport interface BuildAgentToolsMcpServerParams {\n authorizedTools: AuthorizedIntegrationToolMap;\n dispatch: IntegrationToolDispatcher;\n recordCall?: IntegrationToolCallRecorder | undefined;\n}\n\nexport function buildAgentToolsMcpServer(params: BuildAgentToolsMcpServerParams): Server {\n const server = new Server(\n {name: 'shipfox-integration-tools', version: '0.0.0'},\n {capabilities: {tools: {}}},\n );\n server.setRequestHandler(ListToolsRequestSchema, () => ({\n tools: [...params.authorizedTools.values()].map((authorizedTool) => ({\n name: authorizedTool.mcpName,\n description: authorizedTool.description,\n inputSchema: authorizedTool.inputSchema as {\n type: 'object';\n properties?: Record<string, object> | undefined;\n required?: string[] | undefined;\n },\n ...(authorizedTool.outputSchema\n ? {\n outputSchema: authorizedTool.outputSchema as {\n type: 'object';\n properties?: Record<string, object> | undefined;\n required?: string[] | undefined;\n },\n }\n : {}),\n })),\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const authorizedTool = params.authorizedTools.get(request.params.name);\n if (!authorizedTool) {\n recordToolCall(params.recordCall, {\n arguments: request.params.arguments ?? {},\n method: NO_METHOD_LABEL,\n outcome: 'invalid-request',\n });\n return toolError(`Unknown integration tool: ${request.params.name}`);\n }\n\n const args = request.params.arguments ?? {};\n if (!isRecord(args)) {\n recordToolCall(params.recordCall, {\n authorizedTool,\n arguments: args,\n method: NO_METHOD_LABEL,\n outcome: 'invalid-request',\n });\n return toolError('Tool arguments must be an object');\n }\n\n const methodValidation = validateMethod(authorizedTool, args);\n if (methodValidation.kind === 'error') {\n recordToolCall(params.recordCall, {\n authorizedTool,\n arguments: args,\n method: INVALID_METHOD_LABEL,\n outcome: 'invalid-request',\n });\n return toolError(methodValidation.message);\n }\n const method = methodValidation.method ?? NO_METHOD_LABEL;\n\n try {\n const result = await params.dispatch({\n authorizedTool,\n arguments: args,\n method: methodValidation.method,\n });\n recordToolCall(params.recordCall, {\n authorizedTool,\n arguments: args,\n method,\n outcome: result.isError === true ? 'tool-error' : 'success',\n });\n return result;\n } catch (error) {\n recordToolCall(params.recordCall, {\n authorizedTool,\n arguments: args,\n method,\n outcome: 'exception',\n });\n throw error;\n }\n });\n\n return server;\n}\n\nfunction recordToolCall(\n recordCall: IntegrationToolCallRecorder | undefined,\n record: Parameters<IntegrationToolCallRecorder>[0],\n): void {\n try {\n recordCall?.(record);\n } catch {\n // Audit and metrics must not affect MCP responses.\n }\n}\n\nfunction validateMethod(\n authorizedTool: AuthorizedIntegrationTool,\n args: Record<string, unknown>,\n): {kind: 'ok'; method?: string | undefined} | {kind: 'error'; message: string} {\n if (!authorizedTool.tool.methods) return {kind: 'ok'};\n\n const method = args.method;\n if (typeof method !== 'string') {\n return {kind: 'error', message: 'Method-family tools require a string method argument'};\n }\n\n const allowedMethods = new Set(authorizedTool.tool.methods.map((candidate) => candidate.id));\n if (!allowedMethods.has(method)) {\n return {kind: 'error', message: `Unauthorized integration tool method: ${method}`};\n }\n\n return {kind: 'ok', method};\n}\n\nfunction toolError(message: string): CallToolResult {\n return {\n isError: true,\n content: [{type: 'text', text: message}],\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"],"names":["Server","CallToolRequestSchema","ListToolsRequestSchema","INVALID_METHOD_LABEL","NO_METHOD_LABEL","buildAgentToolsMcpServer","params","server","name","version","capabilities","tools","setRequestHandler","authorizedTools","values","map","authorizedTool","mcpName","description","inputSchema","outputSchema","request","get","recordToolCall","recordCall","arguments","method","outcome","toolError","args","isRecord","methodValidation","validateMethod","kind","message","result","dispatch","isError","error","record","tool","methods","allowedMethods","Set","candidate","id","has","content","type","text","value","Array","isArray"],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { type MaterializedAgentIntegrationConfigDto, type MaterializedAgentIntegrationToolConfigDto } from '@shipfox/api-agent-dto';
2
+ import type { IntegrationConnection } from '#core/entities/connection.js';
3
+ import type { AgentToolJsonSchema } from '#core/providers/agent-tools.js';
4
+ import type { IntegrationProviderRegistry } from '#core/providers/registry.js';
5
+ import type { GetIntegrationConnectionByIdFn } from '#db/connections.js';
6
+ export type LeasedAgentStepLoader = (params: {
7
+ request: object;
8
+ stepId: string;
9
+ attempt: number;
10
+ }) => Promise<{
11
+ step: {
12
+ type: string;
13
+ config: Record<string, unknown>;
14
+ };
15
+ workspaceId: string;
16
+ }>;
17
+ export interface AuthorizedIntegrationTool {
18
+ mcpName: string;
19
+ integration: MaterializedAgentIntegrationConfigDto;
20
+ tool: MaterializedAgentIntegrationToolConfigDto;
21
+ connection: IntegrationConnection;
22
+ description: string;
23
+ inputSchema: AgentToolJsonSchema;
24
+ outputSchema?: AgentToolJsonSchema | undefined;
25
+ }
26
+ export type AuthorizedIntegrationToolMap = Map<string, AuthorizedIntegrationTool>;
27
+ export interface ResolveAuthorizedToolsParams {
28
+ request: object;
29
+ loadLeasedAgentStep: LeasedAgentStepLoader;
30
+ registry: IntegrationProviderRegistry;
31
+ getIntegrationConnectionById: GetIntegrationConnectionByIdFn;
32
+ }
33
+ export declare function resolveAuthorizedIntegrationTools(params: ResolveAuthorizedToolsParams): Promise<AuthorizedIntegrationToolMap>;
34
+ export declare function sanitizeSlug(slug: string): string;
35
+ export declare function mcpToolName(connectionSlug: string, toolId: string): string;
36
+ export declare function narrowMethodEnum(inputSchema: AgentToolJsonSchema, authorizedMethods: readonly string[]): AgentToolJsonSchema;
37
+ //# sourceMappingURL=resolve-authorized-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-authorized-tools.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qCAAqC,EAC1C,KAAK,yCAAyC,EAE/C,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AAExE,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AACxE,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,6BAA6B,CAAC;AAC7E,OAAO,KAAK,EAAC,8BAA8B,EAAC,MAAM,oBAAoB,CAAC;AAEvE,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,KAAK,OAAO,CAAC;IACZ,IAAI,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAC,CAAC;IACtD,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAAC;AAEH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,qCAAqC,CAAC;IACnD,IAAI,EAAE,yCAAyC,CAAC;IAChD,UAAU,EAAE,qBAAqB,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,mBAAmB,CAAC;IACjC,YAAY,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;CAChD;AAED,MAAM,MAAM,4BAA4B,GAAG,GAAG,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;AAElF,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,qBAAqB,CAAC;IAC3C,QAAQ,EAAE,2BAA2B,CAAC;IACtC,4BAA4B,EAAE,8BAA8B,CAAC;CAC9D;AAED,wBAAsB,iCAAiC,CACrD,MAAM,EAAE,4BAA4B,GACnC,OAAO,CAAC,4BAA4B,CAAC,CA2DvC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,mBAAmB,EAChC,iBAAiB,EAAE,SAAS,MAAM,EAAE,GACnC,mBAAmB,CAYrB"}
@@ -0,0 +1,145 @@
1
+ import { materializedAgentStepConfigSchema } from '@shipfox/api-agent-dto';
2
+ import { requireLeasedJobContext } from '@shipfox/api-auth-context';
3
+ import { ClientError } from '@shipfox/node-fastify';
4
+ export async function resolveAuthorizedIntegrationTools(params) {
5
+ const leasedJob = requireLeasedJobContext(params.request);
6
+ if (!leasedJob.currentStepId || leasedJob.currentStepAttempt === undefined) {
7
+ throw new ClientError('Lease does not identify a current step', 'lease-missing-step', {
8
+ status: 409
9
+ });
10
+ }
11
+ const { step, workspaceId } = await params.loadLeasedAgentStep({
12
+ request: params.request,
13
+ stepId: leasedJob.currentStepId,
14
+ attempt: leasedJob.currentStepAttempt
15
+ });
16
+ if (step.type !== 'agent') {
17
+ throw new ClientError('Current leased step is not an agent step', 'leased-step-not-agent', {
18
+ status: 409
19
+ });
20
+ }
21
+ const integrations = parseAgentIntegrations(step.config);
22
+ const authorizedTools = new Map();
23
+ for (const integration of integrations){
24
+ const connection = await loadAuthorizedConnection({
25
+ integration,
26
+ workspaceId,
27
+ registry: params.registry,
28
+ getIntegrationConnectionById: params.getIntegrationConnectionById
29
+ });
30
+ const catalog = await params.registry.getAdapter(integration.provider, 'agent_tools').catalog();
31
+ const catalogByToolId = new Map(catalog.map((entry)=>[
32
+ entry.id,
33
+ entry
34
+ ]));
35
+ for (const tool of integration.tools){
36
+ const catalogTool = catalogByToolId.get(tool.id);
37
+ const mcpName = mcpToolName(integration.connectionSlug, tool.id);
38
+ if (authorizedTools.has(mcpName)) {
39
+ throw new ClientError('Integration tool names collide after MCP namespacing', 'integration-tool-name-collision', {
40
+ status: 409
41
+ });
42
+ }
43
+ authorizedTools.set(mcpName, {
44
+ mcpName,
45
+ integration,
46
+ tool,
47
+ connection,
48
+ // The live catalog enriches display metadata only. Frozen step config remains the allowlist.
49
+ description: catalogTool?.description ?? tool.id,
50
+ inputSchema: tool.methods ? toolInputSchema(tool) : tool.inputSchema,
51
+ outputSchema: tool.outputSchema ?? catalogTool?.outputSchema
52
+ });
53
+ }
54
+ }
55
+ return authorizedTools;
56
+ }
57
+ export function sanitizeSlug(slug) {
58
+ return slug.replaceAll('-', '_');
59
+ }
60
+ export function mcpToolName(connectionSlug, toolId) {
61
+ return `${sanitizeSlug(connectionSlug)}__${toolId}`;
62
+ }
63
+ export function narrowMethodEnum(inputSchema, authorizedMethods) {
64
+ const schema = cloneSchema(inputSchema);
65
+ const methodSchema = getObjectProperty(schema, 'method');
66
+ if (methodSchema) {
67
+ narrowEnumOrConst(methodSchema, authorizedMethods);
68
+ }
69
+ // Claude rejects MCP tools with a top-level oneOf. The narrowed method enum remains the
70
+ // authority boundary, while provider validation enforces method-specific argument shapes.
71
+ delete schema.oneOf;
72
+ return schema;
73
+ }
74
+ function toolInputSchema(tool) {
75
+ const authorizedMethods = tool.methods?.map((method)=>method.id) ?? [];
76
+ return narrowMethodEnum(tool.inputSchema, authorizedMethods);
77
+ }
78
+ function parseAgentIntegrations(config) {
79
+ try {
80
+ return materializedAgentStepConfigSchema.parse(config).integrations ?? [];
81
+ } catch (error) {
82
+ throw new ClientError('Agent step config is invalid', 'agent-step-config-invalid', {
83
+ status: 409,
84
+ cause: error
85
+ });
86
+ }
87
+ }
88
+ async function loadAuthorizedConnection(params) {
89
+ const connection = await params.getIntegrationConnectionById(params.integration.connectionId);
90
+ if (!connection) {
91
+ throw new ClientError('Integration connection is no longer available', 'integration-tool-connection-unavailable', {
92
+ status: 409
93
+ });
94
+ }
95
+ if (connection.workspaceId !== params.workspaceId) {
96
+ throw new ClientError('Integration connection does not belong to the leased workspace', 'integration-tool-connection-unavailable', {
97
+ status: 409
98
+ });
99
+ }
100
+ if (connection.lifecycleStatus !== 'active') {
101
+ throw new ClientError('Integration connection is not active', 'integration-tool-connection-unavailable', {
102
+ status: 409
103
+ });
104
+ }
105
+ if (connection.provider !== params.integration.provider) {
106
+ throw new ClientError('Integration connection provider changed', 'integration-tool-connection-unavailable', {
107
+ status: 409
108
+ });
109
+ }
110
+ if (!providerSupportsAgentTools(params.registry, params.integration.provider)) {
111
+ throw new ClientError('Integration provider no longer exposes agent tools', 'integration-tool-connection-unavailable', {
112
+ status: 409
113
+ });
114
+ }
115
+ return connection;
116
+ }
117
+ function providerSupportsAgentTools(registry, provider) {
118
+ try {
119
+ return registry.get(provider).capabilities.includes('agent_tools');
120
+ } catch {
121
+ return false;
122
+ }
123
+ }
124
+ function cloneSchema(schema) {
125
+ return structuredClone(schema);
126
+ }
127
+ function getObjectProperty(schema, property) {
128
+ const properties = schema.properties;
129
+ if (!isRecord(properties)) return null;
130
+ const value = properties[property];
131
+ return isRecord(value) ? value : null;
132
+ }
133
+ function narrowEnumOrConst(schema, authorizedMethods) {
134
+ if (Array.isArray(schema.enum)) {
135
+ schema.enum = schema.enum.filter((value)=>typeof value === 'string' && authorizedMethods.includes(value));
136
+ }
137
+ if (typeof schema.const === 'string' && !authorizedMethods.includes(schema.const)) {
138
+ delete schema.const;
139
+ }
140
+ }
141
+ function isRecord(value) {
142
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
143
+ }
144
+
145
+ //# sourceMappingURL=resolve-authorized-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.ts"],"sourcesContent":["import {\n type MaterializedAgentIntegrationConfigDto,\n type MaterializedAgentIntegrationToolConfigDto,\n materializedAgentStepConfigSchema,\n} from '@shipfox/api-agent-dto';\nimport {requireLeasedJobContext} from '@shipfox/api-auth-context';\nimport {ClientError} from '@shipfox/node-fastify';\nimport type {IntegrationConnection} from '#core/entities/connection.js';\nimport type {IntegrationProviderKind} from '#core/entities/provider.js';\nimport type {AgentToolJsonSchema} from '#core/providers/agent-tools.js';\nimport type {IntegrationProviderRegistry} from '#core/providers/registry.js';\nimport type {GetIntegrationConnectionByIdFn} from '#db/connections.js';\n\nexport type LeasedAgentStepLoader = (params: {\n request: object;\n stepId: string;\n attempt: number;\n}) => Promise<{\n step: {type: string; config: Record<string, unknown>};\n workspaceId: string;\n}>;\n\nexport interface AuthorizedIntegrationTool {\n mcpName: string;\n integration: MaterializedAgentIntegrationConfigDto;\n tool: MaterializedAgentIntegrationToolConfigDto;\n connection: IntegrationConnection;\n description: string;\n inputSchema: AgentToolJsonSchema;\n outputSchema?: AgentToolJsonSchema | undefined;\n}\n\nexport type AuthorizedIntegrationToolMap = Map<string, AuthorizedIntegrationTool>;\n\nexport interface ResolveAuthorizedToolsParams {\n request: object;\n loadLeasedAgentStep: LeasedAgentStepLoader;\n registry: IntegrationProviderRegistry;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}\n\nexport async function resolveAuthorizedIntegrationTools(\n params: ResolveAuthorizedToolsParams,\n): Promise<AuthorizedIntegrationToolMap> {\n const leasedJob = requireLeasedJobContext(params.request);\n if (!leasedJob.currentStepId || leasedJob.currentStepAttempt === undefined) {\n throw new ClientError('Lease does not identify a current step', 'lease-missing-step', {\n status: 409,\n });\n }\n\n const {step, workspaceId} = await params.loadLeasedAgentStep({\n request: params.request,\n stepId: leasedJob.currentStepId,\n attempt: leasedJob.currentStepAttempt,\n });\n if (step.type !== 'agent') {\n throw new ClientError('Current leased step is not an agent step', 'leased-step-not-agent', {\n status: 409,\n });\n }\n\n const integrations = parseAgentIntegrations(step.config);\n const authorizedTools: AuthorizedIntegrationToolMap = new Map();\n\n for (const integration of integrations) {\n const connection = await loadAuthorizedConnection({\n integration,\n workspaceId,\n registry: params.registry,\n getIntegrationConnectionById: params.getIntegrationConnectionById,\n });\n const catalog = await params.registry.getAdapter(integration.provider, 'agent_tools').catalog();\n const catalogByToolId = new Map(catalog.map((entry) => [entry.id, entry]));\n\n for (const tool of integration.tools) {\n const catalogTool = catalogByToolId.get(tool.id);\n const mcpName = mcpToolName(integration.connectionSlug, tool.id);\n if (authorizedTools.has(mcpName)) {\n throw new ClientError(\n 'Integration tool names collide after MCP namespacing',\n 'integration-tool-name-collision',\n {\n status: 409,\n },\n );\n }\n\n authorizedTools.set(mcpName, {\n mcpName,\n integration,\n tool,\n connection,\n // The live catalog enriches display metadata only. Frozen step config remains the allowlist.\n description: catalogTool?.description ?? tool.id,\n inputSchema: tool.methods ? toolInputSchema(tool) : tool.inputSchema,\n outputSchema: tool.outputSchema ?? catalogTool?.outputSchema,\n });\n }\n }\n\n return authorizedTools;\n}\n\nexport function sanitizeSlug(slug: string): string {\n return slug.replaceAll('-', '_');\n}\n\nexport function mcpToolName(connectionSlug: string, toolId: string): string {\n return `${sanitizeSlug(connectionSlug)}__${toolId}`;\n}\n\nexport function narrowMethodEnum(\n inputSchema: AgentToolJsonSchema,\n authorizedMethods: readonly string[],\n): AgentToolJsonSchema {\n const schema = cloneSchema(inputSchema);\n const methodSchema = getObjectProperty(schema, 'method');\n if (methodSchema) {\n narrowEnumOrConst(methodSchema, authorizedMethods);\n }\n\n // Claude rejects MCP tools with a top-level oneOf. The narrowed method enum remains the\n // authority boundary, while provider validation enforces method-specific argument shapes.\n delete schema.oneOf;\n\n return schema;\n}\n\nfunction toolInputSchema(tool: MaterializedAgentIntegrationToolConfigDto): AgentToolJsonSchema {\n const authorizedMethods = tool.methods?.map((method) => method.id) ?? [];\n return narrowMethodEnum(tool.inputSchema, authorizedMethods);\n}\n\nfunction parseAgentIntegrations(\n config: Record<string, unknown>,\n): MaterializedAgentIntegrationConfigDto[] {\n try {\n return materializedAgentStepConfigSchema.parse(config).integrations ?? [];\n } catch (error) {\n throw new ClientError('Agent step config is invalid', 'agent-step-config-invalid', {\n status: 409,\n cause: error,\n });\n }\n}\n\nasync function loadAuthorizedConnection(params: {\n integration: MaterializedAgentIntegrationConfigDto;\n workspaceId: string;\n registry: IntegrationProviderRegistry;\n getIntegrationConnectionById: GetIntegrationConnectionByIdFn;\n}): Promise<IntegrationConnection> {\n const connection = await params.getIntegrationConnectionById(params.integration.connectionId);\n if (!connection) {\n throw new ClientError(\n 'Integration connection is no longer available',\n 'integration-tool-connection-unavailable',\n {\n status: 409,\n },\n );\n }\n if (connection.workspaceId !== params.workspaceId) {\n throw new ClientError(\n 'Integration connection does not belong to the leased workspace',\n 'integration-tool-connection-unavailable',\n {\n status: 409,\n },\n );\n }\n if (connection.lifecycleStatus !== 'active') {\n throw new ClientError(\n 'Integration connection is not active',\n 'integration-tool-connection-unavailable',\n {\n status: 409,\n },\n );\n }\n if (connection.provider !== params.integration.provider) {\n throw new ClientError(\n 'Integration connection provider changed',\n 'integration-tool-connection-unavailable',\n {\n status: 409,\n },\n );\n }\n if (!providerSupportsAgentTools(params.registry, params.integration.provider)) {\n throw new ClientError(\n 'Integration provider no longer exposes agent tools',\n 'integration-tool-connection-unavailable',\n {\n status: 409,\n },\n );\n }\n\n return connection;\n}\n\nfunction providerSupportsAgentTools(\n registry: IntegrationProviderRegistry,\n provider: IntegrationProviderKind,\n): boolean {\n try {\n return registry.get(provider).capabilities.includes('agent_tools');\n } catch {\n return false;\n }\n}\n\nfunction cloneSchema(schema: AgentToolJsonSchema): AgentToolJsonSchema {\n return structuredClone(schema);\n}\n\nfunction getObjectProperty(\n schema: AgentToolJsonSchema,\n property: string,\n): AgentToolJsonSchema | null {\n const properties = schema.properties;\n if (!isRecord(properties)) return null;\n const value = properties[property];\n return isRecord(value) ? value : null;\n}\n\nfunction narrowEnumOrConst(\n schema: AgentToolJsonSchema,\n authorizedMethods: readonly string[],\n): void {\n if (Array.isArray(schema.enum)) {\n schema.enum = schema.enum.filter(\n (value): value is string => typeof value === 'string' && authorizedMethods.includes(value),\n );\n }\n if (typeof schema.const === 'string' && !authorizedMethods.includes(schema.const)) {\n delete schema.const;\n }\n}\n\nfunction isRecord(value: unknown): value is AgentToolJsonSchema {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"],"names":["materializedAgentStepConfigSchema","requireLeasedJobContext","ClientError","resolveAuthorizedIntegrationTools","params","leasedJob","request","currentStepId","currentStepAttempt","undefined","status","step","workspaceId","loadLeasedAgentStep","stepId","attempt","type","integrations","parseAgentIntegrations","config","authorizedTools","Map","integration","connection","loadAuthorizedConnection","registry","getIntegrationConnectionById","catalog","getAdapter","provider","catalogByToolId","map","entry","id","tool","tools","catalogTool","get","mcpName","mcpToolName","connectionSlug","has","set","description","inputSchema","methods","toolInputSchema","outputSchema","sanitizeSlug","slug","replaceAll","toolId","narrowMethodEnum","authorizedMethods","schema","cloneSchema","methodSchema","getObjectProperty","narrowEnumOrConst","oneOf","method","parse","error","cause","connectionId","lifecycleStatus","providerSupportsAgentTools","capabilities","includes","structuredClone","property","properties","isRecord","value","Array","isArray","enum","filter","const"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function integrationRouteErrorHandler(error: unknown): never;
2
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/errors.ts"],"names":[],"mappings":"AAoCA,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CA0BlE"}