@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.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +32 -0
- package/dist/metrics/instance.d.ts +5 -0
- package/dist/metrics/instance.d.ts.map +1 -1
- package/dist/metrics/instance.js +21 -1
- package/dist/metrics/instance.js.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/audit.d.ts +4 -1
- package/dist/presentation/routes/agent-tools-gateway/audit.d.ts.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/audit.js +6 -1
- package/dist/presentation/routes/agent-tools-gateway/audit.js.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts +9 -1
- package/dist/presentation/routes/agent-tools-gateway/dispatch.d.ts.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/dispatch.js +55 -17
- package/dist/presentation/routes/agent-tools-gateway/dispatch.js.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/index.d.ts.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/index.js +6 -5
- package/dist/presentation/routes/agent-tools-gateway/index.js.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/mcp-server.d.ts.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/mcp-server.js +27 -5
- package/dist/presentation/routes/agent-tools-gateway/mcp-server.js.map +1 -1
- package/dist/presentation/routes/errors.d.ts.map +1 -1
- package/dist/presentation/routes/errors.js +1 -1
- package/dist/presentation/routes/errors.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/metrics/instance.ts +40 -1
- package/src/presentation/routes/agent-tools-gateway/agent-tools-gateway.route.test.ts +11 -4
- package/src/presentation/routes/agent-tools-gateway/audit.test.ts +51 -0
- package/src/presentation/routes/agent-tools-gateway/audit.ts +8 -0
- package/src/presentation/routes/agent-tools-gateway/dispatch.test.ts +167 -0
- package/src/presentation/routes/agent-tools-gateway/dispatch.ts +93 -20
- package/src/presentation/routes/agent-tools-gateway/index.ts +3 -4
- package/src/presentation/routes/agent-tools-gateway/mcp-server.test.ts +41 -4
- package/src/presentation/routes/agent-tools-gateway/mcp-server.ts +35 -1
- package/src/presentation/routes/errors.ts +1 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -54,6 +54,7 @@ describe('buildAgentToolsMcpServer', () => {
|
|
|
54
54
|
}),
|
|
55
55
|
method: 'get',
|
|
56
56
|
outcome: 'success',
|
|
57
|
+
errorCode: 'none',
|
|
57
58
|
},
|
|
58
59
|
]);
|
|
59
60
|
});
|
|
@@ -93,6 +94,7 @@ describe('buildAgentToolsMcpServer', () => {
|
|
|
93
94
|
{
|
|
94
95
|
method: expectedMethod,
|
|
95
96
|
outcome: 'invalid-request',
|
|
97
|
+
errorCode: 'invalid-request',
|
|
96
98
|
},
|
|
97
99
|
]);
|
|
98
100
|
if (expectedToolId) {
|
|
@@ -128,7 +130,9 @@ describe('buildAgentToolsMcpServer', () => {
|
|
|
128
130
|
arguments: {method: 'ignored'},
|
|
129
131
|
}),
|
|
130
132
|
);
|
|
131
|
-
expect(records).toMatchObject([
|
|
133
|
+
expect(records).toMatchObject([
|
|
134
|
+
{method: NO_METHOD_LABEL, outcome: 'success', errorCode: 'none'},
|
|
135
|
+
]);
|
|
132
136
|
});
|
|
133
137
|
|
|
134
138
|
it('defaults omitted arguments to an empty object for optional-argument tools', async () => {
|
|
@@ -179,7 +183,7 @@ describe('buildAgentToolsMcpServer', () => {
|
|
|
179
183
|
arguments: expect.objectContaining({issue_number: 'not-an-integer'}),
|
|
180
184
|
}),
|
|
181
185
|
);
|
|
182
|
-
expect(records).toMatchObject([{method: 'get', outcome: 'success'}]);
|
|
186
|
+
expect(records).toMatchObject([{method: 'get', outcome: 'success', errorCode: 'none'}]);
|
|
183
187
|
});
|
|
184
188
|
|
|
185
189
|
it('records tool-error when dispatch returns an error result', async () => {
|
|
@@ -204,7 +208,7 @@ describe('buildAgentToolsMcpServer', () => {
|
|
|
204
208
|
await close();
|
|
205
209
|
|
|
206
210
|
expect(result.isError).toBe(true);
|
|
207
|
-
expect(records).toMatchObject([{method: 'get', outcome: 'tool-error'}]);
|
|
211
|
+
expect(records).toMatchObject([{method: 'get', outcome: 'tool-error', errorCode: 'unknown'}]);
|
|
208
212
|
});
|
|
209
213
|
|
|
210
214
|
it('records exception before rethrowing dispatcher failures', async () => {
|
|
@@ -227,7 +231,40 @@ describe('buildAgentToolsMcpServer', () => {
|
|
|
227
231
|
).rejects.toThrow('MCP error -32603');
|
|
228
232
|
await close();
|
|
229
233
|
|
|
230
|
-
expect(records).toMatchObject([{method: 'get', outcome: 'exception'}]);
|
|
234
|
+
expect(records).toMatchObject([{method: 'get', outcome: 'exception', errorCode: 'unknown'}]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('records bounded provider error details returned by the dispatcher', async () => {
|
|
238
|
+
const dispatch = vi.fn(async () => ({
|
|
239
|
+
isError: true,
|
|
240
|
+
content: [{type: 'text' as const, text: 'provider rejected call'}],
|
|
241
|
+
structuredContent: {code: 'provider-rejected', status: 422},
|
|
242
|
+
}));
|
|
243
|
+
const records: Parameters<
|
|
244
|
+
NonNullable<Parameters<typeof buildAgentToolsMcpServer>[0]['recordCall']>
|
|
245
|
+
>[0][] = [];
|
|
246
|
+
const {client, close} = await connectClient(dispatch, defaultAuthorizedTools(), (record) =>
|
|
247
|
+
records.push(record),
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
const result = await client.callTool(
|
|
251
|
+
{
|
|
252
|
+
name: 'github_main__issue_read',
|
|
253
|
+
arguments: {method: 'get', owner: 'shipfox', repo: 'platform', issue_number: 1},
|
|
254
|
+
},
|
|
255
|
+
CallToolResultSchema,
|
|
256
|
+
);
|
|
257
|
+
await close();
|
|
258
|
+
|
|
259
|
+
expect(result.isError).toBe(true);
|
|
260
|
+
expect(records).toMatchObject([
|
|
261
|
+
{
|
|
262
|
+
method: 'get',
|
|
263
|
+
outcome: 'tool-error',
|
|
264
|
+
errorCode: 'provider-rejected',
|
|
265
|
+
providerStatus: 422,
|
|
266
|
+
},
|
|
267
|
+
]);
|
|
231
268
|
});
|
|
232
269
|
});
|
|
233
270
|
|
|
@@ -6,7 +6,13 @@ import {
|
|
|
6
6
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
7
7
|
import {reportError} from '@shipfox/node-error-monitoring';
|
|
8
8
|
import {logger} from '@shipfox/node-opentelemetry';
|
|
9
|
-
import {
|
|
9
|
+
import {normalizeIntegrationAgentToolCallErrorCode} from '#metrics/index.js';
|
|
10
|
+
import {
|
|
11
|
+
INVALID_METHOD_LABEL,
|
|
12
|
+
type IntegrationAgentToolCallErrorCode,
|
|
13
|
+
type IntegrationToolCallRecorder,
|
|
14
|
+
NO_METHOD_LABEL,
|
|
15
|
+
} from './audit.js';
|
|
10
16
|
import type {
|
|
11
17
|
AuthorizedIntegrationTool,
|
|
12
18
|
AuthorizedIntegrationToolMap,
|
|
@@ -61,6 +67,7 @@ export function buildAgentToolsMcpServer(params: BuildAgentToolsMcpServerParams)
|
|
|
61
67
|
arguments: request.params.arguments ?? {},
|
|
62
68
|
method: NO_METHOD_LABEL,
|
|
63
69
|
outcome: 'invalid-request',
|
|
70
|
+
errorCode: 'invalid-request',
|
|
64
71
|
});
|
|
65
72
|
return toolError(`Unknown integration tool: ${request.params.name}`);
|
|
66
73
|
}
|
|
@@ -72,6 +79,7 @@ export function buildAgentToolsMcpServer(params: BuildAgentToolsMcpServerParams)
|
|
|
72
79
|
arguments: args,
|
|
73
80
|
method: NO_METHOD_LABEL,
|
|
74
81
|
outcome: 'invalid-request',
|
|
82
|
+
errorCode: 'invalid-request',
|
|
75
83
|
});
|
|
76
84
|
return toolError('Tool arguments must be an object');
|
|
77
85
|
}
|
|
@@ -83,6 +91,7 @@ export function buildAgentToolsMcpServer(params: BuildAgentToolsMcpServerParams)
|
|
|
83
91
|
arguments: args,
|
|
84
92
|
method: INVALID_METHOD_LABEL,
|
|
85
93
|
outcome: 'invalid-request',
|
|
94
|
+
errorCode: 'invalid-request',
|
|
86
95
|
});
|
|
87
96
|
return toolError(methodValidation.message);
|
|
88
97
|
}
|
|
@@ -99,6 +108,7 @@ export function buildAgentToolsMcpServer(params: BuildAgentToolsMcpServerParams)
|
|
|
99
108
|
arguments: args,
|
|
100
109
|
method,
|
|
101
110
|
outcome: result.isError === true ? 'tool-error' : 'success',
|
|
111
|
+
...toolCallErrorDetails(result),
|
|
102
112
|
});
|
|
103
113
|
return result;
|
|
104
114
|
} catch (error) {
|
|
@@ -107,6 +117,7 @@ export function buildAgentToolsMcpServer(params: BuildAgentToolsMcpServerParams)
|
|
|
107
117
|
arguments: args,
|
|
108
118
|
method,
|
|
109
119
|
outcome: 'exception',
|
|
120
|
+
errorCode: 'unknown',
|
|
110
121
|
});
|
|
111
122
|
throw error;
|
|
112
123
|
}
|
|
@@ -115,6 +126,23 @@ export function buildAgentToolsMcpServer(params: BuildAgentToolsMcpServerParams)
|
|
|
115
126
|
return server;
|
|
116
127
|
}
|
|
117
128
|
|
|
129
|
+
function toolCallErrorDetails(result: CallToolResult): {
|
|
130
|
+
errorCode: IntegrationAgentToolCallErrorCode | 'none';
|
|
131
|
+
providerStatus?: number | undefined;
|
|
132
|
+
} {
|
|
133
|
+
if (result.isError !== true) return {errorCode: 'none'};
|
|
134
|
+
|
|
135
|
+
const structuredContent = isRecord(result.structuredContent)
|
|
136
|
+
? result.structuredContent
|
|
137
|
+
: undefined;
|
|
138
|
+
const providerStatus = statusCode(structuredContent?.status);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
errorCode: normalizeIntegrationAgentToolCallErrorCode(structuredContent?.code),
|
|
142
|
+
...(providerStatus === undefined ? {} : {providerStatus}),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
118
146
|
function recordToolCall(
|
|
119
147
|
recordCall: IntegrationToolCallRecorder | undefined,
|
|
120
148
|
record: Parameters<IntegrationToolCallRecorder>[0],
|
|
@@ -157,3 +185,9 @@ function toolError(message: string): CallToolResult {
|
|
|
157
185
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
158
186
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
159
187
|
}
|
|
188
|
+
|
|
189
|
+
function statusCode(value: unknown): number | undefined {
|
|
190
|
+
return typeof value === 'number' && Number.isInteger(value) && value >= 100 && value <= 599
|
|
191
|
+
? value
|
|
192
|
+
: undefined;
|
|
193
|
+
}
|
|
@@ -28,6 +28,7 @@ function isProviderError(error: unknown): error is IntegrationProviderError {
|
|
|
28
28
|
error.reason === 'rate-limited' ||
|
|
29
29
|
error.reason === 'timeout' ||
|
|
30
30
|
error.reason === 'provider-unavailable' ||
|
|
31
|
+
error.reason === 'provider-rejected' ||
|
|
31
32
|
error.reason === 'malformed-provider-response' ||
|
|
32
33
|
error.reason === 'content-too-large' ||
|
|
33
34
|
error.reason === 'too-many-files'))
|