@shipfox/api-integration-core 12.4.0 → 12.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.
Files changed (36) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +32 -0
  3. package/dist/metrics/instance.d.ts +5 -0
  4. package/dist/metrics/instance.d.ts.map +1 -1
  5. package/dist/metrics/instance.js +21 -1
  6. package/dist/metrics/instance.js.map +1 -1
  7. package/dist/presentation/routes/agent-tools-gateway/audit.d.ts +4 -1
  8. package/dist/presentation/routes/agent-tools-gateway/audit.d.ts.map +1 -1
  9. package/dist/presentation/routes/agent-tools-gateway/audit.js +6 -1
  10. package/dist/presentation/routes/agent-tools-gateway/audit.js.map +1 -1
  11. package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts +9 -1
  12. package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts.map +1 -1
  13. package/dist/presentation/routes/agent-tools-gateway/dispatch.js +55 -17
  14. package/dist/presentation/routes/agent-tools-gateway/dispatch.js.map +1 -1
  15. package/dist/presentation/routes/agent-tools-gateway/index.d.ts.map +1 -1
  16. package/dist/presentation/routes/agent-tools-gateway/index.js +6 -5
  17. package/dist/presentation/routes/agent-tools-gateway/index.js.map +1 -1
  18. package/dist/presentation/routes/agent-tools-gateway/mcp-server.d.ts.map +1 -1
  19. package/dist/presentation/routes/agent-tools-gateway/mcp-server.js +27 -5
  20. package/dist/presentation/routes/agent-tools-gateway/mcp-server.js.map +1 -1
  21. package/dist/presentation/routes/errors.d.ts.map +1 -1
  22. package/dist/presentation/routes/errors.js +1 -1
  23. package/dist/presentation/routes/errors.js.map +1 -1
  24. package/dist/tsconfig.test.tsbuildinfo +1 -1
  25. package/package.json +10 -10
  26. package/src/metrics/instance.ts +40 -1
  27. package/src/presentation/routes/agent-tools-gateway/agent-tools-gateway.route.test.ts +11 -4
  28. package/src/presentation/routes/agent-tools-gateway/audit.test.ts +51 -0
  29. package/src/presentation/routes/agent-tools-gateway/audit.ts +8 -0
  30. package/src/presentation/routes/agent-tools-gateway/dispatch.test.ts +167 -0
  31. package/src/presentation/routes/agent-tools-gateway/dispatch.ts +93 -20
  32. package/src/presentation/routes/agent-tools-gateway/index.ts +3 -4
  33. package/src/presentation/routes/agent-tools-gateway/mcp-server.test.ts +41 -4
  34. package/src/presentation/routes/agent-tools-gateway/mcp-server.ts +35 -1
  35. package/src/presentation/routes/errors.ts +1 -0
  36. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,6 +1,6 @@
1
1
  $ shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js
2
- Successfully compiled: 54 files with swc (614.81ms)
3
- 2026-08-08T09:17:35.462Z [INFO] asset workflow-bundle-e0d869d865fd59433325.js 3.17 MiB [emitted] [immutable] (name: main)
2
+ Successfully compiled: 54 files with swc (470.87ms)
3
+ 2026-08-10T11:40:57.271Z [INFO] asset workflow-bundle-e0d869d865fd59433325.js 3.17 MiB [emitted] [immutable] (name: main)
4
4
  orphan modules 33.5 KiB [orphan] 21 modules
5
5
  runtime modules 1.13 KiB 5 modules
6
6
  modules by path ../../../../node_modules/.pnpm/ 970 KiB 196 modules
@@ -15,6 +15,6 @@ optional modules 30 bytes [optional]
15
15
  __temporal_custom_payload_converter (ignored) 15 bytes [optional] [built] [code generated]
16
16
  __temporal_custom_failure_converter (ignored) 15 bytes [optional] [built] [code generated]
17
17
  ../../../shared/node/temporal/dist/workflow-error-interceptor.js 1.21 KiB [built] [code generated]
18
- webpack 5.107.2 compiled successfully in 9013 ms
19
- 2026-08-08T09:17:35.479Z [INFO] Workflow bundle created { size: '3.17MB' }
18
+ webpack 5.107.2 compiled successfully in 7525 ms
19
+ 2026-08-10T11:40:57.305Z [INFO] Workflow bundle created { size: '3.17MB' }
20
20
  /home/runner/work/shipfox/shipfox/libs/api/integration/core/dist/temporal/workflows/index.js: 3328998 bytes
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @shipfox/api-integration-core
2
2
 
3
+ ## 12.6.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 8aaed9e: Record bounded integration tool error details in audit logs and metrics.
8
+ - Updated dependencies [8aaed9e]
9
+ - Updated dependencies [4848ad7]
10
+ - Updated dependencies [594cdbe]
11
+ - Updated dependencies [53b87f0]
12
+ - @shipfox/api-integration-github@12.6.0
13
+ - @shipfox/api-workflows-dto@12.6.0
14
+
15
+ ## 12.5.0
16
+
17
+ ### Patch Changes
18
+
19
+ - f2b20af: Preserve GitHub provider rejection details and classify terminal agent-tool failures without reporting them as provider outages.
20
+ - Updated dependencies [420ef97]
21
+ - Updated dependencies [f2b20af]
22
+ - Updated dependencies [da02bd8]
23
+ - Updated dependencies [4fa3667]
24
+ - Updated dependencies [5b1838c]
25
+ - @shipfox/api-integration-github@12.5.0
26
+ - @shipfox/api-integration-spi@1.1.1
27
+ - @shipfox/api-workflows-dto@12.5.0
28
+ - @shipfox/api-integration-gitea@12.5.0
29
+ - @shipfox/api-integration-jira@12.5.0
30
+ - @shipfox/api-integration-linear@12.5.0
31
+ - @shipfox/api-integration-sentry@12.5.0
32
+ - @shipfox/api-integration-slack@12.5.0
33
+ - @shipfox/api-integration-webhook@12.5.0
34
+
3
35
  ## 12.4.0
4
36
 
5
37
  ### Patch Changes
@@ -1,8 +1,13 @@
1
+ import type { IntegrationProviderErrorReason } from '@shipfox/api-integration-spi';
1
2
  export type IntegrationAgentToolCallOutcome = 'success' | 'tool-error' | 'invalid-request' | 'exception';
3
+ export type IntegrationAgentToolCallErrorCode = 'invalid-request' | 'unknown' | 'provider-timeout' | 'credentials-unavailable' | IntegrationProviderErrorReason;
4
+ export type IntegrationAgentToolCallErrorLabel = IntegrationAgentToolCallErrorCode | 'none';
2
5
  export declare function recordIntegrationAgentToolCall(params: {
3
6
  provider: string;
4
7
  tool: string;
5
8
  method: string;
6
9
  outcome: IntegrationAgentToolCallOutcome;
10
+ error_code: IntegrationAgentToolCallErrorLabel;
7
11
  }): void;
12
+ export declare function normalizeIntegrationAgentToolCallErrorCode(value: unknown): IntegrationAgentToolCallErrorCode;
8
13
  //# sourceMappingURL=instance.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/metrics/instance.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,+BAA+B,GACvC,SAAS,GACT,YAAY,GACZ,iBAAiB,GACjB,WAAW,CAAC;AAmBhB,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,+BAA+B,CAAC;CAC1C,GAAG,IAAI,CAEP"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/metrics/instance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,8BAA8B,EAAC,MAAM,8BAA8B,CAAC;AAKjF,MAAM,MAAM,+BAA+B,GACvC,SAAS,GACT,YAAY,GACZ,iBAAiB,GACjB,WAAW,CAAC;AAEhB,MAAM,MAAM,iCAAiC,GACzC,iBAAiB,GACjB,SAAS,GACT,kBAAkB,GAClB,yBAAyB,GACzB,8BAA8B,CAAC;AAEnC,MAAM,MAAM,kCAAkC,GAAG,iCAAiC,GAAG,MAAM,CAAC;AAuC5F,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,+BAA+B,CAAC;IACzC,UAAU,EAAE,kCAAkC,CAAC;CAChD,GAAG,IAAI,CAEP;AAED,wBAAgB,0CAA0C,CACxD,KAAK,EAAE,OAAO,GACb,iCAAiC,CAInC"}
@@ -1,7 +1,24 @@
1
1
  import { instanceMetrics } from '@shipfox/node-opentelemetry';
2
2
  const meter = instanceMetrics.getMeter('integrations');
3
+ const integrationAgentToolCallErrorCodes = new Set([
4
+ 'invalid-request',
5
+ 'unknown',
6
+ 'provider-timeout',
7
+ 'credentials-unavailable',
8
+ 'repository-not-found',
9
+ 'installation-not-found',
10
+ 'file-not-found',
11
+ 'access-denied',
12
+ 'rate-limited',
13
+ 'timeout',
14
+ 'provider-unavailable',
15
+ 'provider-rejected',
16
+ 'malformed-provider-response',
17
+ 'content-too-large',
18
+ 'too-many-files'
19
+ ]);
3
20
  const agentToolCallCount = meter.createCounter('integrations_agent_tool_call', {
4
- description: 'Integration agent tool calls by provider, tool, method, and outcome'
21
+ description: 'Integration agent tool calls by provider, tool, method, outcome, and bounded error code'
5
22
  });
6
23
  function recordMetric(record) {
7
24
  try {
@@ -13,5 +30,8 @@ function recordMetric(record) {
13
30
  export function recordIntegrationAgentToolCall(params) {
14
31
  recordMetric(()=>agentToolCallCount.add(1, params));
15
32
  }
33
+ export function normalizeIntegrationAgentToolCallErrorCode(value) {
34
+ return typeof value === 'string' && integrationAgentToolCallErrorCodes.has(value) ? value : 'unknown';
35
+ }
16
36
 
17
37
  //# sourceMappingURL=instance.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/metrics/instance.ts"],"sourcesContent":["import {instanceMetrics} from '@shipfox/node-opentelemetry';\n\nconst meter = instanceMetrics.getMeter('integrations');\n\nexport type IntegrationAgentToolCallOutcome =\n | 'success'\n | 'tool-error'\n | 'invalid-request'\n | 'exception';\n\nconst agentToolCallCount = meter.createCounter<{\n provider: string;\n tool: string;\n method: string;\n outcome: IntegrationAgentToolCallOutcome;\n}>('integrations_agent_tool_call', {\n description: 'Integration agent tool calls by provider, tool, method, and outcome',\n});\n\nfunction recordMetric(record: () => void): void {\n try {\n record();\n } catch {\n // Metrics must not affect integration tool call outcomes.\n }\n}\n\nexport function recordIntegrationAgentToolCall(params: {\n provider: string;\n tool: string;\n method: string;\n outcome: IntegrationAgentToolCallOutcome;\n}): void {\n recordMetric(() => agentToolCallCount.add(1, params));\n}\n"],"names":["instanceMetrics","meter","getMeter","agentToolCallCount","createCounter","description","recordMetric","record","recordIntegrationAgentToolCall","params","add"],"mappings":"AAAA,SAAQA,eAAe,QAAO,8BAA8B;AAE5D,MAAMC,QAAQD,gBAAgBE,QAAQ,CAAC;AAQvC,MAAMC,qBAAqBF,MAAMG,aAAa,CAK3C,gCAAgC;IACjCC,aAAa;AACf;AAEA,SAASC,aAAaC,MAAkB;IACtC,IAAI;QACFA;IACF,EAAE,OAAM;IACN,0DAA0D;IAC5D;AACF;AAEA,OAAO,SAASC,+BAA+BC,MAK9C;IACCH,aAAa,IAAMH,mBAAmBO,GAAG,CAAC,GAAGD;AAC/C"}
1
+ {"version":3,"sources":["../../src/metrics/instance.ts"],"sourcesContent":["import type {IntegrationProviderErrorReason} from '@shipfox/api-integration-spi';\nimport {instanceMetrics} from '@shipfox/node-opentelemetry';\n\nconst meter = instanceMetrics.getMeter('integrations');\n\nexport type IntegrationAgentToolCallOutcome =\n | 'success'\n | 'tool-error'\n | 'invalid-request'\n | 'exception';\n\nexport type IntegrationAgentToolCallErrorCode =\n | 'invalid-request'\n | 'unknown'\n | 'provider-timeout'\n | 'credentials-unavailable'\n | IntegrationProviderErrorReason;\n\nexport type IntegrationAgentToolCallErrorLabel = IntegrationAgentToolCallErrorCode | 'none';\n\nconst integrationAgentToolCallErrorCodes = new Set<string>([\n 'invalid-request',\n 'unknown',\n 'provider-timeout',\n 'credentials-unavailable',\n 'repository-not-found',\n 'installation-not-found',\n 'file-not-found',\n 'access-denied',\n 'rate-limited',\n 'timeout',\n 'provider-unavailable',\n 'provider-rejected',\n 'malformed-provider-response',\n 'content-too-large',\n 'too-many-files',\n]);\n\nconst agentToolCallCount = meter.createCounter<{\n provider: string;\n tool: string;\n method: string;\n outcome: IntegrationAgentToolCallOutcome;\n error_code: IntegrationAgentToolCallErrorLabel;\n}>('integrations_agent_tool_call', {\n description:\n 'Integration agent tool calls by provider, tool, method, outcome, and bounded error code',\n});\n\nfunction recordMetric(record: () => void): void {\n try {\n record();\n } catch {\n // Metrics must not affect integration tool call outcomes.\n }\n}\n\nexport function recordIntegrationAgentToolCall(params: {\n provider: string;\n tool: string;\n method: string;\n outcome: IntegrationAgentToolCallOutcome;\n error_code: IntegrationAgentToolCallErrorLabel;\n}): void {\n recordMetric(() => agentToolCallCount.add(1, params));\n}\n\nexport function normalizeIntegrationAgentToolCallErrorCode(\n value: unknown,\n): IntegrationAgentToolCallErrorCode {\n return typeof value === 'string' && integrationAgentToolCallErrorCodes.has(value)\n ? (value as IntegrationAgentToolCallErrorCode)\n : 'unknown';\n}\n"],"names":["instanceMetrics","meter","getMeter","integrationAgentToolCallErrorCodes","Set","agentToolCallCount","createCounter","description","recordMetric","record","recordIntegrationAgentToolCall","params","add","normalizeIntegrationAgentToolCallErrorCode","value","has"],"mappings":"AACA,SAAQA,eAAe,QAAO,8BAA8B;AAE5D,MAAMC,QAAQD,gBAAgBE,QAAQ,CAAC;AAiBvC,MAAMC,qCAAqC,IAAIC,IAAY;IACzD;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,MAAMC,qBAAqBJ,MAAMK,aAAa,CAM3C,gCAAgC;IACjCC,aACE;AACJ;AAEA,SAASC,aAAaC,MAAkB;IACtC,IAAI;QACFA;IACF,EAAE,OAAM;IACN,0DAA0D;IAC5D;AACF;AAEA,OAAO,SAASC,+BAA+BC,MAM9C;IACCH,aAAa,IAAMH,mBAAmBO,GAAG,CAAC,GAAGD;AAC/C;AAEA,OAAO,SAASE,2CACdC,KAAc;IAEd,OAAO,OAAOA,UAAU,YAAYX,mCAAmCY,GAAG,CAACD,SACtEA,QACD;AACN"}
@@ -1,6 +1,7 @@
1
1
  import type { LeasedJobContext } from '@shipfox/api-auth-context';
2
- import { type IntegrationAgentToolCallOutcome, recordIntegrationAgentToolCall } from '#metrics/index.js';
2
+ import { type IntegrationAgentToolCallErrorLabel, type IntegrationAgentToolCallOutcome, recordIntegrationAgentToolCall } from '#metrics/index.js';
3
3
  import type { AuthorizedIntegrationTool } from './resolve-authorized-tools.js';
4
+ export type { IntegrationAgentToolCallErrorCode } from '#metrics/index.js';
4
5
  export declare const UNKNOWN_TOOL_LABEL = "unknown";
5
6
  export declare const NO_METHOD_LABEL = "none";
6
7
  export declare const INVALID_METHOD_LABEL = "invalid";
@@ -13,6 +14,8 @@ export interface IntegrationToolCallAuditRecord {
13
14
  arguments: unknown;
14
15
  method: string;
15
16
  outcome: IntegrationAgentToolCallOutcome;
17
+ errorCode: IntegrationAgentToolCallErrorLabel;
18
+ providerStatus?: number | undefined;
16
19
  }
17
20
  export type IntegrationToolCallRecorder = (record: IntegrationToolCallAuditRecord) => void;
18
21
  export interface CreateIntegrationToolCallRecorderOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/agent-tools-gateway/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAEhE,OAAO,EACL,KAAK,+BAA+B,EACpC,8BAA8B,EAC/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,+BAA+B,CAAC;AAE7E,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAC5C,eAAO,MAAM,eAAe,SAAS,CAAC;AACtC,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAE9C,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,8BAA8B;IAC7C,cAAc,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IACvD,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,+BAA+B,CAAC;CAC1C;AAED,MAAM,MAAM,2BAA2B,GAAG,CAAC,MAAM,EAAE,8BAA8B,KAAK,IAAI,CAAC;AAE3F,MAAM,WAAW,wCAAwC;IACvD,YAAY,CAAC,EAAE,OAAO,8BAA8B,GAAG,SAAS,CAAC;IACjE,OAAO,CAAC,EACJ,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,+BAA+B,KAAK,IAAI,CAAC,GACtF,SAAS,CAAC;CACf;AAED,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,gBAAgB,EACvB,OAAO,GAAE,wCAA6C,GACrD,2BAA2B,CAkC7B;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,8BAA8B,CAKhG"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/agent-tools-gateway/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAEhE,OAAO,EACL,KAAK,kCAAkC,EACvC,KAAK,+BAA+B,EACpC,8BAA8B,EAC/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,+BAA+B,CAAC;AAE7E,YAAY,EAAC,iCAAiC,EAAC,MAAM,mBAAmB,CAAC;AAEzE,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAC5C,eAAO,MAAM,eAAe,SAAS,CAAC;AACtC,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAE9C,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,8BAA8B;IAC7C,cAAc,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IACvD,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,+BAA+B,CAAC;IACzC,SAAS,EAAE,kCAAkC,CAAC;IAC9C,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,MAAM,2BAA2B,GAAG,CAAC,MAAM,EAAE,8BAA8B,KAAK,IAAI,CAAC;AAE3F,MAAM,WAAW,wCAAwC;IACvD,YAAY,CAAC,EAAE,OAAO,8BAA8B,GAAG,SAAS,CAAC;IACjE,OAAO,CAAC,EACJ,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,+BAA+B,KAAK,IAAI,CAAC,GACtF,SAAS,CAAC;CACf;AAED,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,gBAAgB,EACvB,OAAO,GAAE,wCAA6C,GACrD,2BAA2B,CAqC7B;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,OAAO,GAAG,8BAA8B,CAKhG"}
@@ -13,7 +13,8 @@ export function createIntegrationToolCallRecorder(lease, options = {}) {
13
13
  provider,
14
14
  tool: toolId,
15
15
  method: record.method,
16
- outcome: record.outcome
16
+ outcome: record.outcome,
17
+ error_code: record.errorCode
17
18
  });
18
19
  logInfo({
19
20
  jobId: lease.jobId,
@@ -28,6 +29,10 @@ export function createIntegrationToolCallRecorder(lease, options = {}) {
28
29
  toolId,
29
30
  method: record.method,
30
31
  outcome: record.outcome,
32
+ errorCode: record.errorCode,
33
+ ...record.providerStatus === undefined ? {} : {
34
+ providerStatus: record.providerStatus
35
+ },
31
36
  argumentSummary: summarizeIntegrationToolArguments(record.arguments)
32
37
  }, 'integration tool call audited');
33
38
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/presentation/routes/agent-tools-gateway/audit.ts"],"sourcesContent":["import type {LeasedJobContext} from '@shipfox/api-auth-context';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {\n type IntegrationAgentToolCallOutcome,\n recordIntegrationAgentToolCall,\n} from '#metrics/index.js';\nimport type {AuthorizedIntegrationTool} from './resolve-authorized-tools.js';\n\nexport const UNKNOWN_TOOL_LABEL = 'unknown';\nexport const NO_METHOD_LABEL = 'none';\nexport const INVALID_METHOD_LABEL = 'invalid';\n\nexport interface IntegrationToolArgumentSummary {\n keys: string[];\n serializedSizeBytes: number;\n}\n\nexport interface IntegrationToolCallAuditRecord {\n authorizedTool?: AuthorizedIntegrationTool | undefined;\n arguments: unknown;\n method: string;\n outcome: IntegrationAgentToolCallOutcome;\n}\n\nexport type IntegrationToolCallRecorder = (record: IntegrationToolCallAuditRecord) => void;\n\nexport interface CreateIntegrationToolCallRecorderOptions {\n recordMetric?: typeof recordIntegrationAgentToolCall | undefined;\n logInfo?:\n | ((context: Record<string, unknown>, message: 'integration tool call audited') => void)\n | undefined;\n}\n\nexport function createIntegrationToolCallRecorder(\n lease: LeasedJobContext,\n options: CreateIntegrationToolCallRecorderOptions = {},\n): IntegrationToolCallRecorder {\n const recordMetric = options.recordMetric ?? recordIntegrationAgentToolCall;\n const logInfo = options.logInfo ?? ((context, message) => logger().info(context, message));\n\n return (record) => {\n const provider = record.authorizedTool?.integration.provider ?? UNKNOWN_TOOL_LABEL;\n const toolId = record.authorizedTool?.tool.id ?? UNKNOWN_TOOL_LABEL;\n\n recordMetric({\n provider,\n tool: toolId,\n method: record.method,\n outcome: record.outcome,\n });\n\n logInfo(\n {\n jobId: lease.jobId,\n jobExecutionId: lease.jobExecutionId,\n workflowRunId: lease.workflowRunId,\n workflowRunAttemptId: lease.workflowRunAttemptId,\n workspaceId: lease.workspaceId,\n currentStepId: lease.currentStepId,\n currentStepAttempt: lease.currentStepAttempt,\n connectionId: record.authorizedTool?.connection.id,\n provider,\n toolId,\n method: record.method,\n outcome: record.outcome,\n argumentSummary: summarizeIntegrationToolArguments(record.arguments),\n },\n 'integration tool call audited',\n );\n };\n}\n\nexport function summarizeIntegrationToolArguments(value: unknown): IntegrationToolArgumentSummary {\n return {\n keys: isRecord(value) ? Object.keys(value).sort() : [],\n serializedSizeBytes: serializedSize(value),\n };\n}\n\nfunction serializedSize(value: unknown): number {\n try {\n return Buffer.byteLength(JSON.stringify(value) ?? '', 'utf8');\n } catch {\n return 0;\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":["logger","recordIntegrationAgentToolCall","UNKNOWN_TOOL_LABEL","NO_METHOD_LABEL","INVALID_METHOD_LABEL","createIntegrationToolCallRecorder","lease","options","recordMetric","logInfo","context","message","info","record","provider","authorizedTool","integration","toolId","tool","id","method","outcome","jobId","jobExecutionId","workflowRunId","workflowRunAttemptId","workspaceId","currentStepId","currentStepAttempt","connectionId","connection","argumentSummary","summarizeIntegrationToolArguments","arguments","value","keys","isRecord","Object","sort","serializedSizeBytes","serializedSize","Buffer","byteLength","JSON","stringify","Array","isArray"],"mappings":"AACA,SAAQA,MAAM,QAAO,8BAA8B;AACnD,SAEEC,8BAA8B,QACzB,oBAAoB;AAG3B,OAAO,MAAMC,qBAAqB,UAAU;AAC5C,OAAO,MAAMC,kBAAkB,OAAO;AACtC,OAAO,MAAMC,uBAAuB,UAAU;AAuB9C,OAAO,SAASC,kCACdC,KAAuB,EACvBC,UAAoD,CAAC,CAAC;IAEtD,MAAMC,eAAeD,QAAQC,YAAY,IAAIP;IAC7C,MAAMQ,UAAUF,QAAQE,OAAO,IAAK,CAAA,CAACC,SAASC,UAAYX,SAASY,IAAI,CAACF,SAASC,QAAO;IAExF,OAAO,CAACE;QACN,MAAMC,WAAWD,OAAOE,cAAc,EAAEC,YAAYF,YAAYZ;QAChE,MAAMe,SAASJ,OAAOE,cAAc,EAAEG,KAAKC,MAAMjB;QAEjDM,aAAa;YACXM;YACAI,MAAMD;YACNG,QAAQP,OAAOO,MAAM;YACrBC,SAASR,OAAOQ,OAAO;QACzB;QAEAZ,QACE;YACEa,OAAOhB,MAAMgB,KAAK;YAClBC,gBAAgBjB,MAAMiB,cAAc;YACpCC,eAAelB,MAAMkB,aAAa;YAClCC,sBAAsBnB,MAAMmB,oBAAoB;YAChDC,aAAapB,MAAMoB,WAAW;YAC9BC,eAAerB,MAAMqB,aAAa;YAClCC,oBAAoBtB,MAAMsB,kBAAkB;YAC5CC,cAAchB,OAAOE,cAAc,EAAEe,WAAWX;YAChDL;YACAG;YACAG,QAAQP,OAAOO,MAAM;YACrBC,SAASR,OAAOQ,OAAO;YACvBU,iBAAiBC,kCAAkCnB,OAAOoB,SAAS;QACrE,GACA;IAEJ;AACF;AAEA,OAAO,SAASD,kCAAkCE,KAAc;IAC9D,OAAO;QACLC,MAAMC,SAASF,SAASG,OAAOF,IAAI,CAACD,OAAOI,IAAI,KAAK,EAAE;QACtDC,qBAAqBC,eAAeN;IACtC;AACF;AAEA,SAASM,eAAeN,KAAc;IACpC,IAAI;QACF,OAAOO,OAAOC,UAAU,CAACC,KAAKC,SAAS,CAACV,UAAU,IAAI;IACxD,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,SAASE,SAASF,KAAc;IAC9B,OAAO,OAAOA,UAAU,YAAYA,UAAU,QAAQ,CAACW,MAAMC,OAAO,CAACZ;AACvE"}
1
+ {"version":3,"sources":["../../../../src/presentation/routes/agent-tools-gateway/audit.ts"],"sourcesContent":["import type {LeasedJobContext} from '@shipfox/api-auth-context';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport {\n type IntegrationAgentToolCallErrorLabel,\n type IntegrationAgentToolCallOutcome,\n recordIntegrationAgentToolCall,\n} from '#metrics/index.js';\nimport type {AuthorizedIntegrationTool} from './resolve-authorized-tools.js';\n\nexport type {IntegrationAgentToolCallErrorCode} from '#metrics/index.js';\n\nexport const UNKNOWN_TOOL_LABEL = 'unknown';\nexport const NO_METHOD_LABEL = 'none';\nexport const INVALID_METHOD_LABEL = 'invalid';\n\nexport interface IntegrationToolArgumentSummary {\n keys: string[];\n serializedSizeBytes: number;\n}\n\nexport interface IntegrationToolCallAuditRecord {\n authorizedTool?: AuthorizedIntegrationTool | undefined;\n arguments: unknown;\n method: string;\n outcome: IntegrationAgentToolCallOutcome;\n errorCode: IntegrationAgentToolCallErrorLabel;\n providerStatus?: number | undefined;\n}\n\nexport type IntegrationToolCallRecorder = (record: IntegrationToolCallAuditRecord) => void;\n\nexport interface CreateIntegrationToolCallRecorderOptions {\n recordMetric?: typeof recordIntegrationAgentToolCall | undefined;\n logInfo?:\n | ((context: Record<string, unknown>, message: 'integration tool call audited') => void)\n | undefined;\n}\n\nexport function createIntegrationToolCallRecorder(\n lease: LeasedJobContext,\n options: CreateIntegrationToolCallRecorderOptions = {},\n): IntegrationToolCallRecorder {\n const recordMetric = options.recordMetric ?? recordIntegrationAgentToolCall;\n const logInfo = options.logInfo ?? ((context, message) => logger().info(context, message));\n\n return (record) => {\n const provider = record.authorizedTool?.integration.provider ?? UNKNOWN_TOOL_LABEL;\n const toolId = record.authorizedTool?.tool.id ?? UNKNOWN_TOOL_LABEL;\n\n recordMetric({\n provider,\n tool: toolId,\n method: record.method,\n outcome: record.outcome,\n error_code: record.errorCode,\n });\n\n logInfo(\n {\n jobId: lease.jobId,\n jobExecutionId: lease.jobExecutionId,\n workflowRunId: lease.workflowRunId,\n workflowRunAttemptId: lease.workflowRunAttemptId,\n workspaceId: lease.workspaceId,\n currentStepId: lease.currentStepId,\n currentStepAttempt: lease.currentStepAttempt,\n connectionId: record.authorizedTool?.connection.id,\n provider,\n toolId,\n method: record.method,\n outcome: record.outcome,\n errorCode: record.errorCode,\n ...(record.providerStatus === undefined ? {} : {providerStatus: record.providerStatus}),\n argumentSummary: summarizeIntegrationToolArguments(record.arguments),\n },\n 'integration tool call audited',\n );\n };\n}\n\nexport function summarizeIntegrationToolArguments(value: unknown): IntegrationToolArgumentSummary {\n return {\n keys: isRecord(value) ? Object.keys(value).sort() : [],\n serializedSizeBytes: serializedSize(value),\n };\n}\n\nfunction serializedSize(value: unknown): number {\n try {\n return Buffer.byteLength(JSON.stringify(value) ?? '', 'utf8');\n } catch {\n return 0;\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":["logger","recordIntegrationAgentToolCall","UNKNOWN_TOOL_LABEL","NO_METHOD_LABEL","INVALID_METHOD_LABEL","createIntegrationToolCallRecorder","lease","options","recordMetric","logInfo","context","message","info","record","provider","authorizedTool","integration","toolId","tool","id","method","outcome","error_code","errorCode","jobId","jobExecutionId","workflowRunId","workflowRunAttemptId","workspaceId","currentStepId","currentStepAttempt","connectionId","connection","providerStatus","undefined","argumentSummary","summarizeIntegrationToolArguments","arguments","value","keys","isRecord","Object","sort","serializedSizeBytes","serializedSize","Buffer","byteLength","JSON","stringify","Array","isArray"],"mappings":"AACA,SAAQA,MAAM,QAAO,8BAA8B;AACnD,SAGEC,8BAA8B,QACzB,oBAAoB;AAK3B,OAAO,MAAMC,qBAAqB,UAAU;AAC5C,OAAO,MAAMC,kBAAkB,OAAO;AACtC,OAAO,MAAMC,uBAAuB,UAAU;AAyB9C,OAAO,SAASC,kCACdC,KAAuB,EACvBC,UAAoD,CAAC,CAAC;IAEtD,MAAMC,eAAeD,QAAQC,YAAY,IAAIP;IAC7C,MAAMQ,UAAUF,QAAQE,OAAO,IAAK,CAAA,CAACC,SAASC,UAAYX,SAASY,IAAI,CAACF,SAASC,QAAO;IAExF,OAAO,CAACE;QACN,MAAMC,WAAWD,OAAOE,cAAc,EAAEC,YAAYF,YAAYZ;QAChE,MAAMe,SAASJ,OAAOE,cAAc,EAAEG,KAAKC,MAAMjB;QAEjDM,aAAa;YACXM;YACAI,MAAMD;YACNG,QAAQP,OAAOO,MAAM;YACrBC,SAASR,OAAOQ,OAAO;YACvBC,YAAYT,OAAOU,SAAS;QAC9B;QAEAd,QACE;YACEe,OAAOlB,MAAMkB,KAAK;YAClBC,gBAAgBnB,MAAMmB,cAAc;YACpCC,eAAepB,MAAMoB,aAAa;YAClCC,sBAAsBrB,MAAMqB,oBAAoB;YAChDC,aAAatB,MAAMsB,WAAW;YAC9BC,eAAevB,MAAMuB,aAAa;YAClCC,oBAAoBxB,MAAMwB,kBAAkB;YAC5CC,cAAclB,OAAOE,cAAc,EAAEiB,WAAWb;YAChDL;YACAG;YACAG,QAAQP,OAAOO,MAAM;YACrBC,SAASR,OAAOQ,OAAO;YACvBE,WAAWV,OAAOU,SAAS;YAC3B,GAAIV,OAAOoB,cAAc,KAAKC,YAAY,CAAC,IAAI;gBAACD,gBAAgBpB,OAAOoB,cAAc;YAAA,CAAC;YACtFE,iBAAiBC,kCAAkCvB,OAAOwB,SAAS;QACrE,GACA;IAEJ;AACF;AAEA,OAAO,SAASD,kCAAkCE,KAAc;IAC9D,OAAO;QACLC,MAAMC,SAASF,SAASG,OAAOF,IAAI,CAACD,OAAOI,IAAI,KAAK,EAAE;QACtDC,qBAAqBC,eAAeN;IACtC;AACF;AAEA,SAASM,eAAeN,KAAc;IACpC,IAAI;QACF,OAAOO,OAAOC,UAAU,CAACC,KAAKC,SAAS,CAACV,UAAU,IAAI;IACxD,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,SAASE,SAASF,KAAc;IAC9B,OAAO,OAAOA,UAAU,YAAYA,UAAU,QAAQ,CAACW,MAAMC,OAAO,CAACZ;AACvE"}
@@ -1,7 +1,15 @@
1
+ import type { LeasedJobContext } from '@shipfox/api-auth-context';
2
+ import { reportError } from '@shipfox/node-error-monitoring';
3
+ import { logger } from '@shipfox/node-opentelemetry';
1
4
  import type { IntegrationProviderRegistry } from '#core/providers/registry.js';
2
5
  import type { IntegrationToolDispatcher } from './mcp-server.js';
3
6
  export interface CreateIntegrationToolDispatcherParams {
4
7
  registry: IntegrationProviderRegistry;
8
+ lease?: LeasedJobContext | undefined;
5
9
  }
6
- export declare function createIntegrationToolDispatcher(params: CreateIntegrationToolDispatcherParams): IntegrationToolDispatcher;
10
+ export interface IntegrationToolDispatcherDependencies {
11
+ logger?: typeof logger;
12
+ reportError?: typeof reportError;
13
+ }
14
+ export declare function createIntegrationToolDispatcher(params: CreateIntegrationToolDispatcherParams, dependencies?: IntegrationToolDispatcherDependencies): IntegrationToolDispatcher;
7
15
  //# sourceMappingURL=dispatch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/agent-tools-gateway/dispatch.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,6BAA6B,CAAC;AAC7E,OAAO,KAAK,EAAC,yBAAyB,EAA+B,MAAM,iBAAiB,CAAC;AAE7F,MAAM,WAAW,qCAAqC;IACpD,QAAQ,EAAE,2BAA2B,CAAC;CACvC;AAKD,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,qCAAqC,GAC5C,yBAAyB,CAE3B"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/agent-tools-gateway/dispatch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAC,MAAM,EAAC,MAAM,6BAA6B,CAAC;AAQnD,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,6BAA6B,CAAC;AAG7E,OAAO,KAAK,EAAC,yBAAyB,EAA+B,MAAM,iBAAiB,CAAC;AAE7F,MAAM,WAAW,qCAAqC;IACpD,QAAQ,EAAE,2BAA2B,CAAC;IACtC,KAAK,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,qCAAqC;IACpD,MAAM,CAAC,EAAE,OAAO,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,WAAW,CAAC;CAClC;AAMD,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,qCAAqC,EAC7C,YAAY,GAAE,qCAA0C,GACvD,yBAAyB,CAS3B"}
@@ -1,12 +1,17 @@
1
1
  import { reportError } from '@shipfox/node-error-monitoring';
2
2
  import { logger } from '@shipfox/node-opentelemetry';
3
3
  import { IntegrationProviderError } from '#core/errors.js';
4
- const timeoutErrorPattern = /timed?\s*out|timeout/i;
4
+ import { NO_METHOD_LABEL } from './audit.js';
5
+ const timeoutErrorNamePattern = /timed?\s*out|timeout/i;
6
+ const mcpRequestTimeoutMessagePattern = /^MCP error -32001:\s*Request timed out\b/i;
5
7
  const credentialErrorNamePattern = /Token|Credential|Secret|AccessToken/;
6
- export function createIntegrationToolDispatcher(params) {
8
+ export function createIntegrationToolDispatcher(params, dependencies = {}) {
7
9
  return (input)=>dispatchIntegrationToolCall({
8
10
  ...input,
9
- registry: params.registry
11
+ registry: params.registry,
12
+ lease: params.lease,
13
+ logger: dependencies.logger ?? logger,
14
+ reportError: dependencies.reportError ?? reportError
10
15
  });
11
16
  }
12
17
  async function dispatchIntegrationToolCall(input) {
@@ -26,20 +31,41 @@ async function dispatchIntegrationToolCall(input) {
26
31
  });
27
32
  } catch (error) {
28
33
  const result = errorResult(error);
29
- if (result.code === 'provider-unavailable') {
30
- logger().error({
34
+ if (result.code === 'provider-unavailable' || result.code === 'unknown') {
35
+ input.logger().error({
36
+ ...toolCallLogContext(input),
31
37
  err: error,
32
- provider: input.authorizedTool.integration.provider
33
- }, 'Integration agent tool provider was unavailable');
34
- reportError(error, {
38
+ errorCode: result.code,
39
+ ...result.status === undefined ? {} : {
40
+ providerStatus: result.status
41
+ }
42
+ }, result.code === 'provider-unavailable' ? 'Integration agent tool provider was unavailable' : 'Integration agent tool call failed');
43
+ input.reportError(error, {
35
44
  boundary: 'integration.agent-tool'
36
45
  });
37
46
  }
38
47
  return toolError(result);
39
48
  } finally{
40
- await closeSession(session);
49
+ await closeSession(session, input.logger, input.reportError);
41
50
  }
42
51
  }
52
+ function toolCallLogContext(input) {
53
+ return {
54
+ ...input.lease === undefined ? {} : {
55
+ jobId: input.lease.jobId,
56
+ jobExecutionId: input.lease.jobExecutionId,
57
+ workflowRunId: input.lease.workflowRunId,
58
+ workflowRunAttemptId: input.lease.workflowRunAttemptId,
59
+ workspaceId: input.lease.workspaceId,
60
+ currentStepId: input.lease.currentStepId,
61
+ currentStepAttempt: input.lease.currentStepAttempt
62
+ },
63
+ connectionId: input.authorizedTool.connection.id,
64
+ provider: input.authorizedTool.integration.provider,
65
+ toolId: input.authorizedTool.tool.id,
66
+ method: input.method ?? NO_METHOD_LABEL
67
+ };
68
+ }
43
69
  function agentToolCatalogEntry(input) {
44
70
  const { tool, description, inputSchema, outputSchema } = input.authorizedTool;
45
71
  return {
@@ -63,15 +89,15 @@ function agentToolCatalogEntry(input) {
63
89
  }
64
90
  };
65
91
  }
66
- async function closeSession(session) {
92
+ async function closeSession(session, loggerFactory, reportErrorFn) {
67
93
  try {
68
94
  await session?.close?.();
69
95
  } catch (error) {
70
96
  // Cleanup must not mask the tool result returned to the runner.
71
- logger().error({
97
+ loggerFactory().error({
72
98
  err: error
73
99
  }, 'Failed to close integration agent tool session');
74
- reportError(error, {
100
+ reportErrorFn(error, {
75
101
  boundary: 'integration.agent-tool',
76
102
  operation: 'close-session'
77
103
  });
@@ -81,7 +107,13 @@ function errorResult(error) {
81
107
  if (error instanceof IntegrationProviderError) {
82
108
  return {
83
109
  code: error.reason,
84
- message: `Integration provider error: ${error.reason}`
110
+ message: error.message,
111
+ ...error.retryAfterSeconds === undefined ? {} : {
112
+ retryAfterSeconds: error.retryAfterSeconds
113
+ },
114
+ ...error.status === undefined ? {} : {
115
+ status: error.status
116
+ }
85
117
  };
86
118
  }
87
119
  if (isTimeoutError(error)) {
@@ -97,13 +129,13 @@ function errorResult(error) {
97
129
  };
98
130
  }
99
131
  return {
100
- code: 'provider-unavailable',
101
- message: 'Integration provider call failed'
132
+ code: 'unknown',
133
+ message: 'Integration tool call failed'
102
134
  };
103
135
  }
104
136
  function isTimeoutError(error) {
105
137
  if (!(error instanceof Error)) return false;
106
- return error.name === 'AbortError' || timeoutErrorPattern.test(error.name) || timeoutErrorPattern.test(error.message);
138
+ return error.name === 'AbortError' || timeoutErrorNamePattern.test(error.name) || error.name === 'McpError' && mcpRequestTimeoutMessagePattern.test(error.message);
107
139
  }
108
140
  function isCredentialError(error) {
109
141
  if (!(error instanceof Error)) return false;
@@ -119,7 +151,13 @@ function toolError(params) {
119
151
  }
120
152
  ],
121
153
  structuredContent: {
122
- code: params.code
154
+ code: params.code,
155
+ ...params.retryAfterSeconds === undefined ? {} : {
156
+ retryAfterSeconds: params.retryAfterSeconds
157
+ },
158
+ ...params.status === undefined ? {} : {
159
+ status: params.status
160
+ }
123
161
  }
124
162
  };
125
163
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/presentation/routes/agent-tools-gateway/dispatch.ts"],"sourcesContent":["import type {CallToolResult} from '@modelcontextprotocol/sdk/types.js';\nimport {reportError} from '@shipfox/node-error-monitoring';\nimport {logger} from '@shipfox/node-opentelemetry';\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 const result = errorResult(error);\n if (result.code === 'provider-unavailable') {\n logger().error(\n {err: error, provider: input.authorizedTool.integration.provider},\n 'Integration agent tool provider was unavailable',\n );\n reportError(error, {boundary: 'integration.agent-tool'});\n }\n return toolError(result);\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 (error) {\n // Cleanup must not mask the tool result returned to the runner.\n logger().error({err: error}, 'Failed to close integration agent tool session');\n reportError(error, {boundary: 'integration.agent-tool', operation: 'close-session'});\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":["reportError","logger","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","result","errorResult","code","err","boundary","toolError","closeSession","description","inputSchema","outputSchema","sensitivity","sensitive","requiredScope","undefined","methods","map","method","token","close","operation","reason","message","isTimeoutError","isCredentialError","Error","name","test","isError","content","type","text","structuredContent"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/presentation/routes/agent-tools-gateway/dispatch.ts"],"sourcesContent":["import type {CallToolResult} from '@modelcontextprotocol/sdk/types.js';\nimport type {LeasedJobContext} from '@shipfox/api-auth-context';\nimport {reportError} from '@shipfox/node-error-monitoring';\nimport {logger} from '@shipfox/node-opentelemetry';\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 {IntegrationAgentToolCallErrorCode} from '#metrics/index.js';\nimport {NO_METHOD_LABEL} from './audit.js';\nimport type {IntegrationToolDispatcher, IntegrationToolDispatchInput} from './mcp-server.js';\n\nexport interface CreateIntegrationToolDispatcherParams {\n registry: IntegrationProviderRegistry;\n lease?: LeasedJobContext | undefined;\n}\n\nexport interface IntegrationToolDispatcherDependencies {\n logger?: typeof logger;\n reportError?: typeof reportError;\n}\n\nconst timeoutErrorNamePattern = /timed?\\s*out|timeout/i;\nconst mcpRequestTimeoutMessagePattern = /^MCP error -32001:\\s*Request timed out\\b/i;\nconst credentialErrorNamePattern = /Token|Credential|Secret|AccessToken/;\n\nexport function createIntegrationToolDispatcher(\n params: CreateIntegrationToolDispatcherParams,\n dependencies: IntegrationToolDispatcherDependencies = {},\n): IntegrationToolDispatcher {\n return (input) =>\n dispatchIntegrationToolCall({\n ...input,\n registry: params.registry,\n lease: params.lease,\n logger: dependencies.logger ?? logger,\n reportError: dependencies.reportError ?? reportError,\n });\n}\n\nasync function dispatchIntegrationToolCall(\n input: IntegrationToolDispatchInput & {\n registry: IntegrationProviderRegistry;\n lease?: LeasedJobContext | undefined;\n logger: typeof logger;\n reportError: typeof reportError;\n },\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 const result = errorResult(error);\n if (result.code === 'provider-unavailable' || result.code === 'unknown') {\n input.logger().error(\n {\n ...toolCallLogContext(input),\n err: error,\n errorCode: result.code,\n ...(result.status === undefined ? {} : {providerStatus: result.status}),\n },\n result.code === 'provider-unavailable'\n ? 'Integration agent tool provider was unavailable'\n : 'Integration agent tool call failed',\n );\n input.reportError(error, {boundary: 'integration.agent-tool'});\n }\n return toolError(result);\n } finally {\n await closeSession(session, input.logger, input.reportError);\n }\n}\n\nfunction toolCallLogContext(\n input: IntegrationToolDispatchInput & {lease?: LeasedJobContext | undefined},\n): Record<string, unknown> {\n return {\n ...(input.lease === undefined\n ? {}\n : {\n jobId: input.lease.jobId,\n jobExecutionId: input.lease.jobExecutionId,\n workflowRunId: input.lease.workflowRunId,\n workflowRunAttemptId: input.lease.workflowRunAttemptId,\n workspaceId: input.lease.workspaceId,\n currentStepId: input.lease.currentStepId,\n currentStepAttempt: input.lease.currentStepAttempt,\n }),\n connectionId: input.authorizedTool.connection.id,\n provider: input.authorizedTool.integration.provider,\n toolId: input.authorizedTool.tool.id,\n method: input.method ?? NO_METHOD_LABEL,\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(\n session: {close?(): Promise<void>} | undefined,\n loggerFactory: typeof logger,\n reportErrorFn: typeof reportError,\n): Promise<void> {\n try {\n await session?.close?.();\n } catch (error) {\n // Cleanup must not mask the tool result returned to the runner.\n loggerFactory().error({err: error}, 'Failed to close integration agent tool session');\n reportErrorFn(error, {boundary: 'integration.agent-tool', operation: 'close-session'});\n }\n}\n\ninterface IntegrationToolError {\n code: IntegrationAgentToolCallErrorCode;\n message: string;\n retryAfterSeconds?: number | undefined;\n status?: number | undefined;\n}\n\nfunction errorResult(error: unknown): IntegrationToolError {\n if (error instanceof IntegrationProviderError) {\n return {\n code: error.reason,\n message: error.message,\n ...(error.retryAfterSeconds === undefined\n ? {}\n : {retryAfterSeconds: error.retryAfterSeconds}),\n ...(error.status === undefined ? {} : {status: error.status}),\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: 'unknown',\n message: 'Integration tool call failed',\n };\n}\n\nfunction isTimeoutError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n return (\n error.name === 'AbortError' ||\n timeoutErrorNamePattern.test(error.name) ||\n (error.name === 'McpError' && mcpRequestTimeoutMessagePattern.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: IntegrationToolError): CallToolResult {\n return {\n isError: true,\n content: [{type: 'text', text: params.message}],\n structuredContent: {\n code: params.code,\n ...(params.retryAfterSeconds === undefined\n ? {}\n : {retryAfterSeconds: params.retryAfterSeconds}),\n ...(params.status === undefined ? {} : {status: params.status}),\n },\n };\n}\n"],"names":["reportError","logger","IntegrationProviderError","NO_METHOD_LABEL","timeoutErrorNamePattern","mcpRequestTimeoutMessagePattern","credentialErrorNamePattern","createIntegrationToolDispatcher","params","dependencies","input","dispatchIntegrationToolCall","registry","lease","session","adapter","getAdapter","authorizedTool","integration","provider","openSession","connection","tools","agentToolCatalogEntry","scope","call","toolId","tool","id","arguments","error","result","errorResult","code","toolCallLogContext","err","errorCode","status","undefined","providerStatus","boundary","toolError","closeSession","jobId","jobExecutionId","workflowRunId","workflowRunAttemptId","workspaceId","currentStepId","currentStepAttempt","connectionId","method","description","inputSchema","outputSchema","sensitivity","sensitive","requiredScope","methods","map","token","loggerFactory","reportErrorFn","close","operation","reason","message","retryAfterSeconds","isTimeoutError","isCredentialError","Error","name","test","isError","content","type","text","structuredContent"],"mappings":"AAEA,SAAQA,WAAW,QAAO,iCAAiC;AAC3D,SAAQC,MAAM,QAAO,8BAA8B;AACnD,SAAQC,wBAAwB,QAAO,kBAAkB;AASzD,SAAQC,eAAe,QAAO,aAAa;AAa3C,MAAMC,0BAA0B;AAChC,MAAMC,kCAAkC;AACxC,MAAMC,6BAA6B;AAEnC,OAAO,SAASC,gCACdC,MAA6C,EAC7CC,eAAsD,CAAC,CAAC;IAExD,OAAO,CAACC,QACNC,4BAA4B;YAC1B,GAAGD,KAAK;YACRE,UAAUJ,OAAOI,QAAQ;YACzBC,OAAOL,OAAOK,KAAK;YACnBZ,QAAQQ,aAAaR,MAAM,IAAIA;YAC/BD,aAAaS,aAAaT,WAAW,IAAIA;QAC3C;AACJ;AAEA,eAAeW,4BACbD,KAKC;IAED,IAAII;IAEJ,IAAI;QACF,MAAMC,UAAUL,MAAME,QAAQ,CAACI,UAAU,CACvCN,MAAMO,cAAc,CAACC,WAAW,CAACC,QAAQ,EACzC;QAOFL,UAAU,MAAMC,QAAQK,WAAW,CAAC;YAClCC,YAAYX,MAAMO,cAAc,CAACI,UAAU;YAC3CC,OAAO;gBAACC,sBAAsBb;aAAO;YACrCc,OAAOd,MAAMO,cAAc,CAACC,WAAW;QACzC;QAEA,OAAO,MAAMJ,QAAQW,IAAI,CAAC;YACxBC,QAAQhB,MAAMO,cAAc,CAACU,IAAI,CAACC,EAAE;YACpCC,WAAWnB,MAAMmB,SAAS;QAC5B;IACF,EAAE,OAAOC,OAAO;QACd,MAAMC,SAASC,YAAYF;QAC3B,IAAIC,OAAOE,IAAI,KAAK,0BAA0BF,OAAOE,IAAI,KAAK,WAAW;YACvEvB,MAAMT,MAAM,GAAG6B,KAAK,CAClB;gBACE,GAAGI,mBAAmBxB,MAAM;gBAC5ByB,KAAKL;gBACLM,WAAWL,OAAOE,IAAI;gBACtB,GAAIF,OAAOM,MAAM,KAAKC,YAAY,CAAC,IAAI;oBAACC,gBAAgBR,OAAOM,MAAM;gBAAA,CAAC;YACxE,GACAN,OAAOE,IAAI,KAAK,yBACZ,oDACA;YAENvB,MAAMV,WAAW,CAAC8B,OAAO;gBAACU,UAAU;YAAwB;QAC9D;QACA,OAAOC,UAAUV;IACnB,SAAU;QACR,MAAMW,aAAa5B,SAASJ,MAAMT,MAAM,EAAES,MAAMV,WAAW;IAC7D;AACF;AAEA,SAASkC,mBACPxB,KAA4E;IAE5E,OAAO;QACL,GAAIA,MAAMG,KAAK,KAAKyB,YAChB,CAAC,IACD;YACEK,OAAOjC,MAAMG,KAAK,CAAC8B,KAAK;YACxBC,gBAAgBlC,MAAMG,KAAK,CAAC+B,cAAc;YAC1CC,eAAenC,MAAMG,KAAK,CAACgC,aAAa;YACxCC,sBAAsBpC,MAAMG,KAAK,CAACiC,oBAAoB;YACtDC,aAAarC,MAAMG,KAAK,CAACkC,WAAW;YACpCC,eAAetC,MAAMG,KAAK,CAACmC,aAAa;YACxCC,oBAAoBvC,MAAMG,KAAK,CAACoC,kBAAkB;QACpD,CAAC;QACLC,cAAcxC,MAAMO,cAAc,CAACI,UAAU,CAACO,EAAE;QAChDT,UAAUT,MAAMO,cAAc,CAACC,WAAW,CAACC,QAAQ;QACnDO,QAAQhB,MAAMO,cAAc,CAACU,IAAI,CAACC,EAAE;QACpCuB,QAAQzC,MAAMyC,MAAM,IAAIhD;IAC1B;AACF;AAEA,SAASoB,sBAAsBb,KAAmC;IAChE,MAAM,EAACiB,IAAI,EAAEyB,WAAW,EAAEC,WAAW,EAAEC,YAAY,EAAC,GAAG5C,MAAMO,cAAc;IAC3E,OAAO;QACLW,IAAID,KAAKC,EAAE;QACXwB;QACAG,aAAa5B,KAAK4B,WAAW;QAC7BC,WAAW7B,KAAK6B,SAAS;QACzBC,eAAe9B,KAAK8B,aAAa;QACjCJ;QACA,GAAIC,iBAAiBhB,YAAY,CAAC,IAAI;YAACgB;QAAY,CAAC;QACpD,GAAI3B,KAAK+B,OAAO,KAAKpB,YACjB,CAAC,IACD;YACEoB,SAAS/B,KAAK+B,OAAO,CAACC,GAAG,CACvB,CAACR,SAAoC,CAAA;oBACnCvB,IAAIuB,OAAOvB,EAAE;oBACbwB,aAAaD,OAAOC,WAAW,IAAID,OAAOS,KAAK;oBAC/CL,aAAaJ,OAAOI,WAAW;oBAC/BC,WAAWL,OAAOK,SAAS;oBAC3BC,eAAeN,OAAOM,aAAa;gBACrC,CAAA;QAEJ,CAAC;IACP;AACF;AAEA,eAAef,aACb5B,OAA8C,EAC9C+C,aAA4B,EAC5BC,aAAiC;IAEjC,IAAI;QACF,MAAMhD,SAASiD;IACjB,EAAE,OAAOjC,OAAO;QACd,gEAAgE;QAChE+B,gBAAgB/B,KAAK,CAAC;YAACK,KAAKL;QAAK,GAAG;QACpCgC,cAAchC,OAAO;YAACU,UAAU;YAA0BwB,WAAW;QAAe;IACtF;AACF;AASA,SAAShC,YAAYF,KAAc;IACjC,IAAIA,iBAAiB5B,0BAA0B;QAC7C,OAAO;YACL+B,MAAMH,MAAMmC,MAAM;YAClBC,SAASpC,MAAMoC,OAAO;YACtB,GAAIpC,MAAMqC,iBAAiB,KAAK7B,YAC5B,CAAC,IACD;gBAAC6B,mBAAmBrC,MAAMqC,iBAAiB;YAAA,CAAC;YAChD,GAAIrC,MAAMO,MAAM,KAAKC,YAAY,CAAC,IAAI;gBAACD,QAAQP,MAAMO,MAAM;YAAA,CAAC;QAC9D;IACF;IAEA,IAAI+B,eAAetC,QAAQ;QACzB,OAAO;YACLG,MAAM;YACNiC,SAAS;QACX;IACF;IAEA,IAAIG,kBAAkBvC,QAAQ;QAC5B,OAAO;YACLG,MAAM;YACNiC,SAAS;QACX;IACF;IAEA,OAAO;QACLjC,MAAM;QACNiC,SAAS;IACX;AACF;AAEA,SAASE,eAAetC,KAAc;IACpC,IAAI,CAAEA,CAAAA,iBAAiBwC,KAAI,GAAI,OAAO;IACtC,OACExC,MAAMyC,IAAI,KAAK,gBACfnE,wBAAwBoE,IAAI,CAAC1C,MAAMyC,IAAI,KACtCzC,MAAMyC,IAAI,KAAK,cAAclE,gCAAgCmE,IAAI,CAAC1C,MAAMoC,OAAO;AAEpF;AAEA,SAASG,kBAAkBvC,KAAc;IACvC,IAAI,CAAEA,CAAAA,iBAAiBwC,KAAI,GAAI,OAAO;IACtC,OAAOhE,2BAA2BkE,IAAI,CAAC1C,MAAMyC,IAAI;AACnD;AAEA,SAAS9B,UAAUjC,MAA4B;IAC7C,OAAO;QACLiE,SAAS;QACTC,SAAS;YAAC;gBAACC,MAAM;gBAAQC,MAAMpE,OAAO0D,OAAO;YAAA;SAAE;QAC/CW,mBAAmB;YACjB5C,MAAMzB,OAAOyB,IAAI;YACjB,GAAIzB,OAAO2D,iBAAiB,KAAK7B,YAC7B,CAAC,IACD;gBAAC6B,mBAAmB3D,OAAO2D,iBAAiB;YAAA,CAAC;YACjD,GAAI3D,OAAO6B,MAAM,KAAKC,YAAY,CAAC,IAAI;gBAACD,QAAQ7B,OAAO6B,MAAM;YAAA,CAAC;QAChE;IACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/agent-tools-gateway/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAEnE,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;AACzE,OAAO,EAAC,oCAAoC,EAAC,MAAM,+BAA+B,CAAC;AAEnF,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,CAiDZ"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/agent-tools-gateway/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAEnE,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;AACzE,OAAO,EAAC,oCAAoC,EAAC,MAAM,+BAA+B,CAAC;AAEnF,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,CAgDZ"}
@@ -9,9 +9,6 @@ import { buildAgentToolsMcpServer } from './mcp-server.js';
9
9
  import { resolveAuthorizedIntegrationTools } from './resolve-authorized-tools.js';
10
10
  export { createWorkflowsLeasedAgentStepLoader } from './resolve-authorized-tools.js';
11
11
  export function createAgentToolsGatewayRoutes(params) {
12
- const dispatchIntegrationToolCall = createIntegrationToolDispatcher({
13
- registry: params.registry
14
- });
15
12
  return {
16
13
  prefix: '/runs/jobs/current/integration-tools',
17
14
  auth: AUTH_LEASED_JOB,
@@ -21,6 +18,7 @@ export function createAgentToolsGatewayRoutes(params) {
21
18
  path: '/mcp',
22
19
  description: 'Gateway MCP endpoint for integration-backed agent tools',
23
20
  handler: async (request, reply)=>{
21
+ const lease = requireLeasedJobContext(request);
24
22
  const authorizedTools = await resolveAuthorizedIntegrationTools({
25
23
  request,
26
24
  loadLeasedAgentStep: params.loadLeasedAgentStep,
@@ -29,8 +27,11 @@ export function createAgentToolsGatewayRoutes(params) {
29
27
  });
30
28
  const server = buildAgentToolsMcpServer({
31
29
  authorizedTools,
32
- dispatch: dispatchIntegrationToolCall,
33
- recordCall: createIntegrationToolCallRecorder(requireLeasedJobContext(request))
30
+ dispatch: createIntegrationToolDispatcher({
31
+ registry: params.registry,
32
+ lease
33
+ }),
34
+ recordCall: createIntegrationToolCallRecorder(lease)
34
35
  });
35
36
  const transport = new StreamableHTTPServerTransport();
36
37
  await server.connect(transport);
@@ -1 +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 {reportError} from '@shipfox/node-error-monitoring';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\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';\nexport {createWorkflowsLeasedAgentStepLoader} 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().catch((error) => {\n logger().error({err: error}, 'Failed to close integration agent tool transport');\n reportError(error, {\n boundary: 'integration.agent-tool',\n operation: 'close-transport',\n });\n });\n void server.close().catch((error) => {\n logger().error({err: error}, 'Failed to close integration agent tool server');\n reportError(error, {\n boundary: 'integration.agent-tool',\n operation: 'close-server',\n });\n });\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","reportError","defineRoute","logger","createIntegrationToolCallRecorder","createIntegrationToolDispatcher","buildAgentToolsMcpServer","resolveAuthorizedIntegrationTools","createWorkflowsLeasedAgentStepLoader","createAgentToolsGatewayRoutes","params","dispatchIntegrationToolCall","registry","prefix","auth","routes","method","path","description","handler","request","reply","authorizedTools","loadLeasedAgentStep","getIntegrationConnectionById","server","dispatch","recordCall","transport","connect","raw","on","close","catch","error","err","boundary","operation","hijack","handleRequest","body"],"mappings":"AAAA,SAAQA,6BAA6B,QAAO,qDAAqD;AAEjG,SAAQC,eAAe,EAAEC,uBAAuB,QAAO,4BAA4B;AACnF,SAAQC,WAAW,QAAO,iCAAiC;AAC3D,SAAQC,WAAW,QAAwB,wBAAwB;AACnE,SAAQC,MAAM,QAAO,8BAA8B;AAGnD,SAAQC,iCAAiC,QAAO,aAAa;AAC7D,SAAQC,+BAA+B,QAAO,gBAAgB;AAC9D,SAAQC,wBAAwB,QAAO,kBAAkB;AACzD,SAEEC,iCAAiC,QAC5B,gCAAgC;AAGvC,SAAQC,oCAAoC,QAAO,gCAAgC;AAQnF,OAAO,SAASC,8BACdC,MAA2C;IAE3C,MAAMC,8BAA8BN,gCAAgC;QAACO,UAAUF,OAAOE,QAAQ;IAAA;IAE9F,OAAO;QACLC,QAAQ;QACRC,MAAMf;QACNgB,QAAQ;YACNb,YAAY;gBACVc,QAAQ;gBACRC,MAAM;gBACNC,aAAa;gBACbC,SAAS,OAAOC,SAASC;oBACvB,MAAMC,kBAAkB,MAAMf,kCAAkC;wBAC9Da;wBACAG,qBAAqBb,OAAOa,mBAAmB;wBAC/CX,UAAUF,OAAOE,QAAQ;wBACzBY,8BAA8Bd,OAAOc,4BAA4B;oBACnE;oBACA,MAAMC,SAASnB,yBAAyB;wBACtCgB;wBACAI,UAAUf;wBACVgB,YAAYvB,kCAAkCJ,wBAAwBoB;oBACxE;oBACA,MAAMQ,YAAY,IAAI9B;oBAEtB,MAAM2B,OAAOI,OAAO,CAACD;oBACrBP,MAAMS,GAAG,CAACC,EAAE,CAAC,SAAS;wBACpB,KAAKH,UAAUI,KAAK,GAAGC,KAAK,CAAC,CAACC;4BAC5B/B,SAAS+B,KAAK,CAAC;gCAACC,KAAKD;4BAAK,GAAG;4BAC7BjC,YAAYiC,OAAO;gCACjBE,UAAU;gCACVC,WAAW;4BACb;wBACF;wBACA,KAAKZ,OAAOO,KAAK,GAAGC,KAAK,CAAC,CAACC;4BACzB/B,SAAS+B,KAAK,CAAC;gCAACC,KAAKD;4BAAK,GAAG;4BAC7BjC,YAAYiC,OAAO;gCACjBE,UAAU;gCACVC,WAAW;4BACb;wBACF;oBACF;oBAEAhB,MAAMiB,MAAM;oBACZ,MAAMV,UAAUW,aAAa,CAACnB,QAAQU,GAAG,EAAET,MAAMS,GAAG,EAAEV,QAAQoB,IAAI;gBACpE;YACF;SACD;IACH;AACF"}
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 {reportError} from '@shipfox/node-error-monitoring';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport {logger} from '@shipfox/node-opentelemetry';\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';\nexport {createWorkflowsLeasedAgentStepLoader} 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 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 lease = requireLeasedJobContext(request);\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: createIntegrationToolDispatcher({registry: params.registry, lease}),\n recordCall: createIntegrationToolCallRecorder(lease),\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().catch((error) => {\n logger().error({err: error}, 'Failed to close integration agent tool transport');\n reportError(error, {\n boundary: 'integration.agent-tool',\n operation: 'close-transport',\n });\n });\n void server.close().catch((error) => {\n logger().error({err: error}, 'Failed to close integration agent tool server');\n reportError(error, {\n boundary: 'integration.agent-tool',\n operation: 'close-server',\n });\n });\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","reportError","defineRoute","logger","createIntegrationToolCallRecorder","createIntegrationToolDispatcher","buildAgentToolsMcpServer","resolveAuthorizedIntegrationTools","createWorkflowsLeasedAgentStepLoader","createAgentToolsGatewayRoutes","params","prefix","auth","routes","method","path","description","handler","request","reply","lease","authorizedTools","loadLeasedAgentStep","registry","getIntegrationConnectionById","server","dispatch","recordCall","transport","connect","raw","on","close","catch","error","err","boundary","operation","hijack","handleRequest","body"],"mappings":"AAAA,SAAQA,6BAA6B,QAAO,qDAAqD;AAEjG,SAAQC,eAAe,EAAEC,uBAAuB,QAAO,4BAA4B;AACnF,SAAQC,WAAW,QAAO,iCAAiC;AAC3D,SAAQC,WAAW,QAAwB,wBAAwB;AACnE,SAAQC,MAAM,QAAO,8BAA8B;AAGnD,SAAQC,iCAAiC,QAAO,aAAa;AAC7D,SAAQC,+BAA+B,QAAO,gBAAgB;AAC9D,SAAQC,wBAAwB,QAAO,kBAAkB;AACzD,SAEEC,iCAAiC,QAC5B,gCAAgC;AAGvC,SAAQC,oCAAoC,QAAO,gCAAgC;AAQnF,OAAO,SAASC,8BACdC,MAA2C;IAE3C,OAAO;QACLC,QAAQ;QACRC,MAAMb;QACNc,QAAQ;YACNX,YAAY;gBACVY,QAAQ;gBACRC,MAAM;gBACNC,aAAa;gBACbC,SAAS,OAAOC,SAASC;oBACvB,MAAMC,QAAQpB,wBAAwBkB;oBACtC,MAAMG,kBAAkB,MAAMd,kCAAkC;wBAC9DW;wBACAI,qBAAqBZ,OAAOY,mBAAmB;wBAC/CC,UAAUb,OAAOa,QAAQ;wBACzBC,8BAA8Bd,OAAOc,4BAA4B;oBACnE;oBACA,MAAMC,SAASnB,yBAAyB;wBACtCe;wBACAK,UAAUrB,gCAAgC;4BAACkB,UAAUb,OAAOa,QAAQ;4BAAEH;wBAAK;wBAC3EO,YAAYvB,kCAAkCgB;oBAChD;oBACA,MAAMQ,YAAY,IAAI9B;oBAEtB,MAAM2B,OAAOI,OAAO,CAACD;oBACrBT,MAAMW,GAAG,CAACC,EAAE,CAAC,SAAS;wBACpB,KAAKH,UAAUI,KAAK,GAAGC,KAAK,CAAC,CAACC;4BAC5B/B,SAAS+B,KAAK,CAAC;gCAACC,KAAKD;4BAAK,GAAG;4BAC7BjC,YAAYiC,OAAO;gCACjBE,UAAU;gCACVC,WAAW;4BACb;wBACF;wBACA,KAAKZ,OAAOO,KAAK,GAAGC,KAAK,CAAC,CAACC;4BACzB/B,SAAS+B,KAAK,CAAC;gCAACC,KAAKD;4BAAK,GAAG;4BAC7BjC,YAAYiC,OAAO;gCACjBE,UAAU;gCACVC,WAAW;4BACb;wBACF;oBACF;oBAEAlB,MAAMmB,MAAM;oBACZ,MAAMV,UAAUW,aAAa,CAACrB,QAAQY,GAAG,EAAEX,MAAMW,GAAG,EAAEZ,QAAQsB,IAAI;gBACpE;YACF;SACD;IACH;AACF"}
@@ -1 +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;AAG5C,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"}
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;AAI5C,OAAO,EAGL,KAAK,2BAA2B,EAEjC,MAAM,YAAY,CAAC;AACpB,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,CA0FvF"}
@@ -2,6 +2,7 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
3
3
  import { reportError } from '@shipfox/node-error-monitoring';
4
4
  import { logger } from '@shipfox/node-opentelemetry';
5
+ import { normalizeIntegrationAgentToolCallErrorCode } from '#metrics/index.js';
5
6
  import { INVALID_METHOD_LABEL, NO_METHOD_LABEL } from './audit.js';
6
7
  export function buildAgentToolsMcpServer(params) {
7
8
  const server = new Server({
@@ -30,7 +31,8 @@ export function buildAgentToolsMcpServer(params) {
30
31
  recordToolCall(params.recordCall, {
31
32
  arguments: request.params.arguments ?? {},
32
33
  method: NO_METHOD_LABEL,
33
- outcome: 'invalid-request'
34
+ outcome: 'invalid-request',
35
+ errorCode: 'invalid-request'
34
36
  });
35
37
  return toolError(`Unknown integration tool: ${request.params.name}`);
36
38
  }
@@ -40,7 +42,8 @@ export function buildAgentToolsMcpServer(params) {
40
42
  authorizedTool,
41
43
  arguments: args,
42
44
  method: NO_METHOD_LABEL,
43
- outcome: 'invalid-request'
45
+ outcome: 'invalid-request',
46
+ errorCode: 'invalid-request'
44
47
  });
45
48
  return toolError('Tool arguments must be an object');
46
49
  }
@@ -50,7 +53,8 @@ export function buildAgentToolsMcpServer(params) {
50
53
  authorizedTool,
51
54
  arguments: args,
52
55
  method: INVALID_METHOD_LABEL,
53
- outcome: 'invalid-request'
56
+ outcome: 'invalid-request',
57
+ errorCode: 'invalid-request'
54
58
  });
55
59
  return toolError(methodValidation.message);
56
60
  }
@@ -65,7 +69,8 @@ export function buildAgentToolsMcpServer(params) {
65
69
  authorizedTool,
66
70
  arguments: args,
67
71
  method,
68
- outcome: result.isError === true ? 'tool-error' : 'success'
72
+ outcome: result.isError === true ? 'tool-error' : 'success',
73
+ ...toolCallErrorDetails(result)
69
74
  });
70
75
  return result;
71
76
  } catch (error) {
@@ -73,13 +78,27 @@ export function buildAgentToolsMcpServer(params) {
73
78
  authorizedTool,
74
79
  arguments: args,
75
80
  method,
76
- outcome: 'exception'
81
+ outcome: 'exception',
82
+ errorCode: 'unknown'
77
83
  });
78
84
  throw error;
79
85
  }
80
86
  });
81
87
  return server;
82
88
  }
89
+ function toolCallErrorDetails(result) {
90
+ if (result.isError !== true) return {
91
+ errorCode: 'none'
92
+ };
93
+ const structuredContent = isRecord(result.structuredContent) ? result.structuredContent : undefined;
94
+ const providerStatus = statusCode(structuredContent?.status);
95
+ return {
96
+ errorCode: normalizeIntegrationAgentToolCallErrorCode(structuredContent?.code),
97
+ ...providerStatus === undefined ? {} : {
98
+ providerStatus
99
+ }
100
+ };
101
+ }
83
102
  function recordToolCall(recordCall, record) {
84
103
  try {
85
104
  recordCall?.(record);
@@ -131,5 +150,8 @@ function toolError(message) {
131
150
  function isRecord(value) {
132
151
  return typeof value === 'object' && value !== null && !Array.isArray(value);
133
152
  }
153
+ function statusCode(value) {
154
+ return typeof value === 'number' && Number.isInteger(value) && value >= 100 && value <= 599 ? value : undefined;
155
+ }
134
156
 
135
157
  //# sourceMappingURL=mcp-server.js.map