@qase/mcp-server 1.0.0-beta.1 → 1.1.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/CHANGELOG.md +34 -0
- package/README.md +152 -3
- package/build/client/index.d.ts +50 -6
- package/build/client/index.d.ts.map +1 -1
- package/build/client/index.js +114 -18
- package/build/client/index.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +170 -62
- package/build/index.js.map +1 -1
- package/build/operations/attachments.js +35 -16
- package/build/operations/attachments.js.map +1 -1
- package/build/operations/authors.js +4 -4
- package/build/operations/authors.js.map +1 -1
- package/build/operations/cases.js +75 -34
- package/build/operations/cases.js.map +1 -1
- package/build/operations/configurations.d.ts +2 -0
- package/build/operations/configurations.d.ts.map +1 -1
- package/build/operations/configurations.js +28 -8
- package/build/operations/configurations.js.map +1 -1
- package/build/operations/custom-fields.js +7 -7
- package/build/operations/custom-fields.js.map +1 -1
- package/build/operations/defects.js +59 -29
- package/build/operations/defects.js.map +1 -1
- package/build/operations/environments.js +7 -7
- package/build/operations/environments.js.map +1 -1
- package/build/operations/milestones.js +6 -6
- package/build/operations/milestones.js.map +1 -1
- package/build/operations/plans.js +6 -6
- package/build/operations/plans.js.map +1 -1
- package/build/operations/projects.js +8 -8
- package/build/operations/projects.js.map +1 -1
- package/build/operations/results.js +7 -7
- package/build/operations/results.js.map +1 -1
- package/build/operations/runs.js +16 -10
- package/build/operations/runs.js.map +1 -1
- package/build/operations/search.js +10 -13
- package/build/operations/search.js.map +1 -1
- package/build/operations/shared-parameters.d.ts +2 -0
- package/build/operations/shared-parameters.d.ts.map +1 -1
- package/build/operations/shared-parameters.js +30 -78
- package/build/operations/shared-parameters.js.map +1 -1
- package/build/operations/shared-steps.js +6 -6
- package/build/operations/shared-steps.js.map +1 -1
- package/build/operations/suites.js +6 -6
- package/build/operations/suites.js.map +1 -1
- package/build/operations/system-fields.d.ts +2 -0
- package/build/operations/system-fields.d.ts.map +1 -1
- package/build/operations/system-fields.js +8 -2
- package/build/operations/system-fields.js.map +1 -1
- package/build/operations/users.d.ts +2 -0
- package/build/operations/users.d.ts.map +1 -1
- package/build/operations/users.js +19 -11
- package/build/operations/users.js.map +1 -1
- package/build/transports/sse.d.ts +10 -0
- package/build/transports/sse.d.ts.map +1 -0
- package/build/transports/sse.js +38 -0
- package/build/transports/sse.js.map +1 -0
- package/build/transports/streamableHttp.d.ts +9 -0
- package/build/transports/streamableHttp.d.ts.map +1 -0
- package/build/transports/streamableHttp.js +169 -0
- package/build/transports/streamableHttp.js.map +1 -0
- package/build/utils/auth-context.d.ts +8 -0
- package/build/utils/auth-context.d.ts.map +1 -0
- package/build/utils/auth-context.js +8 -0
- package/build/utils/auth-context.js.map +1 -0
- package/build/utils/case-enums.d.ts +6 -0
- package/build/utils/case-enums.d.ts.map +1 -0
- package/build/utils/case-enums.js +91 -0
- package/build/utils/case-enums.js.map +1 -0
- package/build/utils/errors.d.ts +25 -0
- package/build/utils/errors.d.ts.map +1 -1
- package/build/utils/errors.js +78 -0
- package/build/utils/errors.js.map +1 -1
- package/build/utils/registry.d.ts.map +1 -1
- package/build/utils/registry.js +4 -0
- package/build/utils/registry.js.map +1 -1
- package/build/utils/test-helpers.d.ts +2 -2
- package/build/utils/test-helpers.d.ts.map +1 -1
- package/build/utils/test-helpers.js +16 -0
- package/build/utils/test-helpers.js.map +1 -1
- package/package.json +17 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-fields.js","sourceRoot":"","sources":["../../src/operations/system-fields.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"system-fields.js","sourceRoot":"","sources":["../../src/operations/system-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEpE,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAE5C,+EAA+E;AAC/E,WAAW;AACX,+EAA+E;AAE/E;;;;GAIG;AACH,KAAK,UAAU,gBAAgB,CAAC,KAA6C;IAC3E,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAE9B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,eAAe,EAAE,CAAC,CAAC;IAE1E,OAAO,MAAM,CAAC,KAAK,CACjB,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAClC,CAAC,KAAK,EAAE,EAAE;QACR,MAAM,eAAe,CAAC,KAAK,EAAE,uBAAuB,CAAC,CAAC;IACxD,CAAC,CACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E,YAAY,CAAC,QAAQ,CAAC;IACpB,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EACT,qFAAqF;IACvF,MAAM,EAAE,sBAAsB;IAC9B,OAAO,EAAE,gBAAgB;CAC1B,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/operations/users.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/operations/users.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -3,24 +3,29 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Implements all MCP tools for managing users in Qase.
|
|
5
5
|
* Users are team members with access to the Qase workspace.
|
|
6
|
+
*
|
|
7
|
+
* https://developers.qase.io/reference/get-users
|
|
6
8
|
*/
|
|
7
9
|
import { z } from 'zod';
|
|
8
10
|
import { getApiClient } from '../client/index.js';
|
|
9
11
|
import { toolRegistry } from '../utils/registry.js';
|
|
10
|
-
import { toResultAsync } from '../utils/errors.js';
|
|
12
|
+
import { toResultAsync, createToolError } from '../utils/errors.js';
|
|
11
13
|
import { IdSchema } from '../utils/validation.js';
|
|
12
14
|
// ============================================================================
|
|
13
15
|
// SCHEMAS
|
|
14
16
|
// ============================================================================
|
|
15
17
|
/**
|
|
16
18
|
* Schema for listing users
|
|
19
|
+
* API: GET /v1/user
|
|
20
|
+
* https://developers.qase.io/reference/get-users
|
|
21
|
+
*
|
|
22
|
+
* The Qase API does not document pagination parameters for this endpoint.
|
|
17
23
|
*/
|
|
18
|
-
const ListUsersSchema = z.object({
|
|
19
|
-
limit: z.number().int().positive().max(100).optional().describe('Maximum number of items'),
|
|
20
|
-
offset: z.number().int().nonnegative().optional().describe('Number of items to skip'),
|
|
21
|
-
});
|
|
24
|
+
const ListUsersSchema = z.object({});
|
|
22
25
|
/**
|
|
23
26
|
* Schema for getting a specific user
|
|
27
|
+
* API: GET /v1/user/{id}
|
|
28
|
+
* https://developers.qase.io/reference/get-user
|
|
24
29
|
*/
|
|
25
30
|
const GetUserSchema = z.object({
|
|
26
31
|
id: IdSchema,
|
|
@@ -30,24 +35,27 @@ const GetUserSchema = z.object({
|
|
|
30
35
|
// ============================================================================
|
|
31
36
|
/**
|
|
32
37
|
* List all users
|
|
38
|
+
* API: GET /v1/user
|
|
39
|
+
* https://developers.qase.io/reference/get-users
|
|
33
40
|
*/
|
|
34
|
-
async function listUsers(
|
|
41
|
+
async function listUsers(_args) {
|
|
35
42
|
const client = getApiClient();
|
|
36
|
-
const
|
|
37
|
-
const result = await toResultAsync(client.users.getUsers(limit, offset));
|
|
43
|
+
const result = await toResultAsync(client.users.getUsers());
|
|
38
44
|
return result.match((response) => response.data.result, (error) => {
|
|
39
|
-
throw
|
|
45
|
+
throw createToolError(error, 'listing users');
|
|
40
46
|
});
|
|
41
47
|
}
|
|
42
48
|
/**
|
|
43
49
|
* Get a specific user
|
|
50
|
+
* API: GET /v1/user/{id}
|
|
51
|
+
* https://developers.qase.io/reference/get-user
|
|
44
52
|
*/
|
|
45
53
|
async function getUser(args) {
|
|
46
54
|
const client = getApiClient();
|
|
47
55
|
const { id } = args;
|
|
48
56
|
const result = await toResultAsync(client.users.getUser(id));
|
|
49
57
|
return result.match((response) => response.data.result, (error) => {
|
|
50
|
-
throw
|
|
58
|
+
throw createToolError(error, 'getting user');
|
|
51
59
|
});
|
|
52
60
|
}
|
|
53
61
|
// ============================================================================
|
|
@@ -55,7 +63,7 @@ async function getUser(args) {
|
|
|
55
63
|
// ============================================================================
|
|
56
64
|
toolRegistry.register({
|
|
57
65
|
name: 'list_users',
|
|
58
|
-
description: 'Get all users in the workspace
|
|
66
|
+
description: 'Get all users in the workspace',
|
|
59
67
|
schema: ListUsersSchema,
|
|
60
68
|
handler: listUsers,
|
|
61
69
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/operations/users.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/operations/users.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;;;;;GAMG;AACH,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAErC;;;;GAIG;AACH,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,EAAE,EAAE,QAAQ;CACb,CAAC,CAAC;AAEH,+EAA+E;AAC/E,WAAW;AACX,+EAA+E;AAE/E;;;;GAIG;AACH,KAAK,UAAU,SAAS,CAAC,KAAsC;IAC7D,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAE9B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE5D,OAAO,MAAM,CAAC,KAAK,CACjB,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAClC,CAAC,KAAK,EAAE,EAAE;QACR,MAAM,eAAe,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;IAChD,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,OAAO,CAAC,IAAmC;IACxD,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;IAEpB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAE7D,OAAO,MAAM,CAAC,KAAK,CACjB,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAClC,CAAC,KAAK,EAAE,EAAE;QACR,MAAM,eAAe,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IAC/C,CAAC,CACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E,YAAY,CAAC,QAAQ,CAAC;IACpB,IAAI,EAAE,YAAY;IAClB,WAAW,EAAE,gCAAgC;IAC7C,MAAM,EAAE,eAAe;IACvB,OAAO,EAAE,SAAS;CACnB,CAAC,CAAC;AAEH,YAAY,CAAC,QAAQ,CAAC;IACpB,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,2BAA2B;IACxC,MAAM,EAAE,aAAa;IACrB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Express } from 'express';
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
export interface SSETransportConfig {
|
|
4
|
+
port: number;
|
|
5
|
+
host?: string;
|
|
6
|
+
sseEndpoint?: string;
|
|
7
|
+
messagesEndpoint?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function setupSSETransport(server: Server, config: SSETransportConfig): Express;
|
|
10
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/transports/sse.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAwCrF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
3
|
+
import { requestTokenStorage } from '../utils/auth-context.js';
|
|
4
|
+
export function setupSSETransport(server, config) {
|
|
5
|
+
const app = express();
|
|
6
|
+
app.use(express.json());
|
|
7
|
+
const sseEndpoint = config.sseEndpoint || '/sse';
|
|
8
|
+
const messagesEndpoint = config.messagesEndpoint || '/messages';
|
|
9
|
+
const host = config.host || '0.0.0.0';
|
|
10
|
+
let transport = null;
|
|
11
|
+
// Health check endpoint
|
|
12
|
+
app.get('/health', (_req, res) => {
|
|
13
|
+
res.json({ status: 'ok', transport: 'sse' });
|
|
14
|
+
});
|
|
15
|
+
// SSE endpoint for establishing connection
|
|
16
|
+
app.get(sseEndpoint, (_req, res) => {
|
|
17
|
+
console.error('[SSE] Client connected');
|
|
18
|
+
transport = new SSEServerTransport(messagesEndpoint, res);
|
|
19
|
+
server.connect(transport);
|
|
20
|
+
});
|
|
21
|
+
// Messages endpoint for receiving client messages
|
|
22
|
+
app.post(messagesEndpoint, (req, res) => {
|
|
23
|
+
if (!transport) {
|
|
24
|
+
res.status(503).json({ error: 'No SSE connection established' });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const authHeader = req.headers['authorization'] || '';
|
|
28
|
+
const requestToken = authHeader.startsWith('Bearer ') ? authHeader.slice(7).trim() : '';
|
|
29
|
+
requestTokenStorage.run(requestToken, () => transport.handlePostMessage(req, res));
|
|
30
|
+
});
|
|
31
|
+
// Start server
|
|
32
|
+
app.listen(config.port, host, () => {
|
|
33
|
+
console.error(`[SSE] Server listening on http://${host}:${config.port}${sseEndpoint}`);
|
|
34
|
+
console.error(`[SSE] Health check: http://${host}:${config.port}/health`);
|
|
35
|
+
});
|
|
36
|
+
return app;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=sse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/transports/sse.ts"],"names":[],"mappings":"AAAA,OAAO,OAAoB,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAS/D,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,MAA0B;IAC1E,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAExB,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC;IACjD,MAAM,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,WAAW,CAAC;IAChE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,SAAS,CAAC;IAEtC,IAAI,SAAS,GAA8B,IAAI,CAAC;IAEhD,wBAAwB;IACxB,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,2CAA2C;IAC3C,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACjC,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACxC,SAAS,GAAG,IAAI,kBAAkB,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QAC1D,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,kDAAkD;IAClD,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACtC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QACD,MAAM,UAAU,GAAI,GAAG,CAAC,OAAO,CAAC,eAAe,CAAY,IAAI,EAAE,CAAC;QAClE,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxF,mBAAmB,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,SAAU,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,eAAe;IACf,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;QACjC,OAAO,CAAC,KAAK,CAAC,oCAAoC,IAAI,IAAI,MAAM,CAAC,IAAI,GAAG,WAAW,EAAE,CAAC,CAAC;QACvF,OAAO,CAAC,KAAK,CAAC,8BAA8B,IAAI,IAAI,MAAM,CAAC,IAAI,SAAS,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Express } from 'express';
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
export interface StreamableHttpConfig {
|
|
4
|
+
port: number;
|
|
5
|
+
host?: string;
|
|
6
|
+
endpoint?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function setupStreamableHttpTransport(createServer: () => Server, config: StreamableHttpConfig): Express;
|
|
9
|
+
//# sourceMappingURL=streamableHttp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamableHttp.d.ts","sourceRoot":"","sources":["../../src/transports/streamableHttp.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AASD,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,MAAM,MAAM,EAC1B,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CA+LT"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
3
|
+
import { randomUUID } from 'crypto';
|
|
4
|
+
import { requestTokenStorage } from '../utils/auth-context.js';
|
|
5
|
+
// Helper to check if a request is an initialize request
|
|
6
|
+
function isInitializeRequest(body) {
|
|
7
|
+
return (typeof body === 'object' && body !== null && 'method' in body && body.method === 'initialize');
|
|
8
|
+
}
|
|
9
|
+
export function setupStreamableHttpTransport(createServer, config) {
|
|
10
|
+
const app = express();
|
|
11
|
+
// CORS middleware for inspector
|
|
12
|
+
app.use((req, res, next) => {
|
|
13
|
+
res.header('Access-Control-Allow-Origin', process.env.CORS_ORIGIN || '*');
|
|
14
|
+
res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
|
15
|
+
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, mcp-session-id');
|
|
16
|
+
if (req.method === 'OPTIONS') {
|
|
17
|
+
res.sendStatus(200);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
// Log request (omit headers to avoid exposing Authorization token in logs)
|
|
21
|
+
console.error(`[StreamableHTTP] ${req.method} ${req.path}`, { query: req.query });
|
|
22
|
+
next();
|
|
23
|
+
});
|
|
24
|
+
app.use(express.json());
|
|
25
|
+
const endpoint = config.endpoint || '/mcp';
|
|
26
|
+
const host = config.host || '0.0.0.0';
|
|
27
|
+
// Session management - store transport and last-seen timestamp per session
|
|
28
|
+
const SESSION_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
29
|
+
const sessions = new Map();
|
|
30
|
+
const sessionLastSeen = new Map();
|
|
31
|
+
// Periodically evict stale sessions that never sent a DELETE
|
|
32
|
+
const cleanupInterval = setInterval(() => {
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
for (const [id, ts] of sessionLastSeen) {
|
|
35
|
+
if (now - ts > SESSION_TTL_MS) {
|
|
36
|
+
const staleTransport = sessions.get(id);
|
|
37
|
+
if (staleTransport) {
|
|
38
|
+
staleTransport.close().catch(() => { });
|
|
39
|
+
sessions.delete(id);
|
|
40
|
+
}
|
|
41
|
+
sessionLastSeen.delete(id);
|
|
42
|
+
console.error(`[StreamableHTTP] Evicted stale session: ${id}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, 5 * 60 * 1000); // check every 5 minutes
|
|
46
|
+
cleanupInterval.unref(); // don't keep the process alive just for cleanup
|
|
47
|
+
// Health check endpoint
|
|
48
|
+
app.get('/health', (_req, res) => {
|
|
49
|
+
res.json({ status: 'ok', transport: 'streamable-http' });
|
|
50
|
+
});
|
|
51
|
+
// MCP endpoint - DELETE for session cleanup
|
|
52
|
+
app.delete(endpoint, async (req, res) => {
|
|
53
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
54
|
+
if (!sessionId || !sessions.has(sessionId)) {
|
|
55
|
+
res.status(400).json({
|
|
56
|
+
error: 'Invalid or missing session ID',
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const transport = sessions.get(sessionId);
|
|
61
|
+
try {
|
|
62
|
+
await transport.close();
|
|
63
|
+
sessions.delete(sessionId);
|
|
64
|
+
sessionLastSeen.delete(sessionId);
|
|
65
|
+
console.error(`[StreamableHTTP] Session closed: ${sessionId}`);
|
|
66
|
+
res.status(200).json({ status: 'session closed' });
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error('[StreamableHTTP] Error closing session:', error);
|
|
70
|
+
if (!res.headersSent) {
|
|
71
|
+
res.status(500).json({
|
|
72
|
+
error: 'Internal server error',
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// MCP endpoint - GET for SSE streams (if needed)
|
|
78
|
+
app.get(endpoint, async (req, res) => {
|
|
79
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
80
|
+
if (!sessionId || !sessions.has(sessionId)) {
|
|
81
|
+
res.status(400).json({
|
|
82
|
+
error: 'Invalid or missing session ID',
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const transport = sessions.get(sessionId);
|
|
87
|
+
try {
|
|
88
|
+
await transport.handleRequest(req, res);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
console.error('[StreamableHTTP] Error handling GET request:', error);
|
|
92
|
+
if (!res.headersSent) {
|
|
93
|
+
res.status(500).json({
|
|
94
|
+
error: 'Internal server error',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
// MCP endpoint - POST for messages
|
|
100
|
+
app.post(endpoint, async (req, res) => {
|
|
101
|
+
console.error(`[StreamableHTTP] POST ${endpoint} received`, {
|
|
102
|
+
sessionId: req.headers['mcp-session-id'],
|
|
103
|
+
body: req.body,
|
|
104
|
+
});
|
|
105
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
106
|
+
let transport;
|
|
107
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
108
|
+
// Reuse existing transport for this session — bump last-seen
|
|
109
|
+
transport = sessions.get(sessionId);
|
|
110
|
+
sessionLastSeen.set(sessionId, Date.now());
|
|
111
|
+
}
|
|
112
|
+
else if (isInitializeRequest(req.body)) {
|
|
113
|
+
// This is an initialize request - create new session
|
|
114
|
+
const newSessionId = randomUUID();
|
|
115
|
+
transport = new StreamableHTTPServerTransport({
|
|
116
|
+
sessionIdGenerator: () => newSessionId,
|
|
117
|
+
enableJsonResponse: true,
|
|
118
|
+
});
|
|
119
|
+
// Store session and record creation time
|
|
120
|
+
sessions.set(newSessionId, transport);
|
|
121
|
+
sessionLastSeen.set(newSessionId, Date.now());
|
|
122
|
+
// Create a fresh Server instance per session — the SDK requires one Server per transport
|
|
123
|
+
await createServer().connect(transport);
|
|
124
|
+
console.error(`[StreamableHTTP] New session created: ${newSessionId}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Invalid request - has session ID but session not found
|
|
128
|
+
res.status(400).json({
|
|
129
|
+
error: 'Invalid request: session not found or not an initialize request',
|
|
130
|
+
});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Extract per-request token from Authorization header (Bearer <token>)
|
|
134
|
+
const authHeader = req.headers['authorization'] || '';
|
|
135
|
+
const requestToken = authHeader.startsWith('Bearer ') ? authHeader.slice(7).trim() : '';
|
|
136
|
+
if (requestToken) {
|
|
137
|
+
console.error('[StreamableHTTP] Using per-request Bearer token');
|
|
138
|
+
}
|
|
139
|
+
// Run the handler inside AsyncLocalStorage context so getApiClient() can read the token
|
|
140
|
+
try {
|
|
141
|
+
await requestTokenStorage.run(requestToken, () => transport.handleRequest(req, res, req.body));
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.error('[StreamableHTTP] Error handling request:', error);
|
|
145
|
+
if (!res.headersSent) {
|
|
146
|
+
res.status(500).json({
|
|
147
|
+
error: 'Internal server error',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
// Start server and store reference to keep it alive
|
|
153
|
+
const httpServer = app.listen(config.port, host, () => {
|
|
154
|
+
console.error(`[StreamableHTTP] Server listening on http://${host}:${config.port}${endpoint}`);
|
|
155
|
+
console.error(`[StreamableHTTP] Health check: http://${host}:${config.port}/health`);
|
|
156
|
+
});
|
|
157
|
+
// Handle server errors
|
|
158
|
+
httpServer.on('error', (error) => {
|
|
159
|
+
console.error('[StreamableHTTP] Server error:', error);
|
|
160
|
+
});
|
|
161
|
+
// Handle client connections
|
|
162
|
+
httpServer.on('connection', (socket) => {
|
|
163
|
+
console.error(`[StreamableHTTP] New client connection from ${socket.remoteAddress}:${socket.remotePort}`);
|
|
164
|
+
});
|
|
165
|
+
// Keep server reference alive (prevent garbage collection)
|
|
166
|
+
app._httpServer = httpServer;
|
|
167
|
+
return app;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=streamableHttp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamableHttp.js","sourceRoot":"","sources":["../../src/transports/streamableHttp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
|
+
/**
|
|
3
|
+
* Per-request token storage.
|
|
4
|
+
* Holds the Bearer token extracted from the Authorization header for the current request.
|
|
5
|
+
* Empty string means no user token — fall back to shared QASE_API_TOKEN.
|
|
6
|
+
*/
|
|
7
|
+
export declare const requestTokenStorage: AsyncLocalStorage<string>;
|
|
8
|
+
//# sourceMappingURL=auth-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-context.d.ts","sourceRoot":"","sources":["../../src/utils/auth-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,2BAAkC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
|
+
/**
|
|
3
|
+
* Per-request token storage.
|
|
4
|
+
* Holds the Bearer token extracted from the Authorization header for the current request.
|
|
5
|
+
* Empty string means no user token — fall back to shared QASE_API_TOKEN.
|
|
6
|
+
*/
|
|
7
|
+
export const requestTokenStorage = new AsyncLocalStorage();
|
|
8
|
+
//# sourceMappingURL=auth-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-context.js","sourceRoot":"","sources":["../../src/utils/auth-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,iBAAiB,EAAU,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type SystemFieldMap = Record<string, Map<string, number>>;
|
|
2
|
+
export declare function normalizeCaseEnums<T extends Record<string, unknown>>(caseData: T): Promise<T>;
|
|
3
|
+
export declare function resetCaseEnumCacheForTest(): void;
|
|
4
|
+
export declare function __setCaseEnumCacheForTest(snapshot: SystemFieldMap): void;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=case-enums.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case-enums.d.ts","sourceRoot":"","sources":["../../src/utils/case-enums.ts"],"names":[],"mappings":"AAwBA,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAsF1D,wBAAsB,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxE,QAAQ,EAAE,CAAC,GACV,OAAO,CAAC,CAAC,CAAC,CAeZ;AAED,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAExE"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { getApiClient } from '../client/index.js';
|
|
2
|
+
const caseEnumFields = [
|
|
3
|
+
'priority',
|
|
4
|
+
'type',
|
|
5
|
+
'behavior',
|
|
6
|
+
'severity',
|
|
7
|
+
'status',
|
|
8
|
+
'layer',
|
|
9
|
+
];
|
|
10
|
+
let systemFieldCache = null;
|
|
11
|
+
function normalizeEnumValue(raw, lookup) {
|
|
12
|
+
if (raw === undefined || raw === null) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
if (typeof raw === 'number') {
|
|
16
|
+
return raw;
|
|
17
|
+
}
|
|
18
|
+
if (typeof raw === 'string') {
|
|
19
|
+
const trimmed = raw.trim();
|
|
20
|
+
if (trimmed === '') {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const parsed = Number(trimmed);
|
|
24
|
+
if (Number.isInteger(parsed)) {
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
const lower = trimmed.toLowerCase();
|
|
28
|
+
if (lookup?.has(lower)) {
|
|
29
|
+
return lookup.get(lower);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
function createFieldMap(entry) {
|
|
35
|
+
if (!entry.options || entry.options.length === 0) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const map = new Map();
|
|
39
|
+
for (const option of entry.options) {
|
|
40
|
+
map.set(option.slug.toLowerCase(), option.id);
|
|
41
|
+
if (option.title) {
|
|
42
|
+
map.set(option.title.toLowerCase(), option.id);
|
|
43
|
+
}
|
|
44
|
+
map.set(option.id.toString(), option.id);
|
|
45
|
+
}
|
|
46
|
+
return map;
|
|
47
|
+
}
|
|
48
|
+
async function getSystemFieldMaps() {
|
|
49
|
+
if (systemFieldCache) {
|
|
50
|
+
return systemFieldCache;
|
|
51
|
+
}
|
|
52
|
+
const client = getApiClient();
|
|
53
|
+
systemFieldCache = client.systemFields
|
|
54
|
+
.getSystemFields()
|
|
55
|
+
.then((response) => {
|
|
56
|
+
const map = {};
|
|
57
|
+
for (const entry of response.data.result) {
|
|
58
|
+
const normalizedSlug = entry.slug.toLowerCase();
|
|
59
|
+
const fieldMap = createFieldMap(entry);
|
|
60
|
+
if (fieldMap) {
|
|
61
|
+
map[normalizedSlug] = fieldMap;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return map;
|
|
65
|
+
})
|
|
66
|
+
.catch((error) => {
|
|
67
|
+
systemFieldCache = null;
|
|
68
|
+
throw error;
|
|
69
|
+
});
|
|
70
|
+
return systemFieldCache;
|
|
71
|
+
}
|
|
72
|
+
export async function normalizeCaseEnums(caseData) {
|
|
73
|
+
const normalized = { ...caseData };
|
|
74
|
+
const systemFields = await getSystemFieldMaps();
|
|
75
|
+
for (const field of caseEnumFields) {
|
|
76
|
+
const rawValue = normalized[field];
|
|
77
|
+
const lookup = systemFields[field];
|
|
78
|
+
const mappedValue = normalizeEnumValue(rawValue, lookup);
|
|
79
|
+
if (mappedValue !== undefined) {
|
|
80
|
+
normalized[field] = mappedValue;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return normalized;
|
|
84
|
+
}
|
|
85
|
+
export function resetCaseEnumCacheForTest() {
|
|
86
|
+
systemFieldCache = null;
|
|
87
|
+
}
|
|
88
|
+
export function __setCaseEnumCacheForTest(snapshot) {
|
|
89
|
+
systemFieldCache = Promise.resolve(snapshot);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=case-enums.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case-enums.js","sourceRoot":"","sources":["../../src/utils/case-enums.ts"],"names":[],"mappings":"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"}
|
package/build/utils/errors.d.ts
CHANGED
|
@@ -17,6 +17,23 @@ export declare class QaseApiError extends Error {
|
|
|
17
17
|
details?: unknown | undefined;
|
|
18
18
|
constructor(message: string, statusCode?: number | undefined, errorCode?: string | undefined, details?: unknown | undefined);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Error class for tool execution errors
|
|
22
|
+
*
|
|
23
|
+
* Use this for expected failures during tool execution (API errors, validation errors,
|
|
24
|
+
* business logic errors). These errors are returned to the LLM with isError: true,
|
|
25
|
+
* allowing the LLM to understand the failure and take corrective action.
|
|
26
|
+
*
|
|
27
|
+
* Protocol errors (unknown tool, invalid schema) should use regular Error instead.
|
|
28
|
+
*/
|
|
29
|
+
export declare class ToolExecutionError extends Error {
|
|
30
|
+
suggestion?: string | undefined;
|
|
31
|
+
constructor(message: string, suggestion?: string | undefined);
|
|
32
|
+
/**
|
|
33
|
+
* Format the error message for LLM consumption
|
|
34
|
+
*/
|
|
35
|
+
toUserMessage(): string;
|
|
36
|
+
}
|
|
20
37
|
/**
|
|
21
38
|
* Format API error into a user-friendly message
|
|
22
39
|
*
|
|
@@ -70,4 +87,12 @@ export declare function isRateLimitError(error: unknown): boolean;
|
|
|
70
87
|
* ```
|
|
71
88
|
*/
|
|
72
89
|
export declare function toResultAsync<T>(promise: Promise<T>): ResultAsync<T, string>;
|
|
90
|
+
/**
|
|
91
|
+
* Create a ToolExecutionError from an API error with helpful suggestions
|
|
92
|
+
*
|
|
93
|
+
* @param error - The formatted error message
|
|
94
|
+
* @param context - Optional context about the operation (e.g., "creating project")
|
|
95
|
+
* @returns ToolExecutionError with suggestion for recovery
|
|
96
|
+
*/
|
|
97
|
+
export declare function createToolError(error: string, context?: string): ToolExecutionError;
|
|
73
98
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;IAG5B,UAAU,CAAC,EAAE,MAAM;IACnB,SAAS,CAAC,EAAE,MAAM;IAClB,OAAO,CAAC,EAAE,OAAO;gBAHxB,OAAO,EAAE,MAAM,EACR,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM,YAAA,EAClB,OAAO,CAAC,EAAE,OAAO,YAAA;CAM3B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA4CrD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAK7D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKvD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAMzD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKxD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,CAE5E"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;IAG5B,UAAU,CAAC,EAAE,MAAM;IACnB,SAAS,CAAC,EAAE,MAAM;IAClB,OAAO,CAAC,EAAE,OAAO;gBAHxB,OAAO,EAAE,MAAM,EACR,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM,YAAA,EAClB,OAAO,CAAC,EAAE,OAAO,YAAA;CAM3B;AAED;;;;;;;;GAQG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAGlC,UAAU,CAAC,EAAE,MAAM;gBAD1B,OAAO,EAAE,MAAM,EACR,UAAU,CAAC,EAAE,MAAM,YAAA;IAO5B;;OAEG;IACH,aAAa,IAAI,MAAM;CAMxB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA4CrD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAK7D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKvD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAMzD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKxD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,CAE5E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAGnF"}
|
package/build/utils/errors.js
CHANGED
|
@@ -25,6 +25,33 @@ export class QaseApiError extends Error {
|
|
|
25
25
|
Object.setPrototypeOf(this, QaseApiError.prototype);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Error class for tool execution errors
|
|
30
|
+
*
|
|
31
|
+
* Use this for expected failures during tool execution (API errors, validation errors,
|
|
32
|
+
* business logic errors). These errors are returned to the LLM with isError: true,
|
|
33
|
+
* allowing the LLM to understand the failure and take corrective action.
|
|
34
|
+
*
|
|
35
|
+
* Protocol errors (unknown tool, invalid schema) should use regular Error instead.
|
|
36
|
+
*/
|
|
37
|
+
export class ToolExecutionError extends Error {
|
|
38
|
+
suggestion;
|
|
39
|
+
constructor(message, suggestion) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.suggestion = suggestion;
|
|
42
|
+
this.name = 'ToolExecutionError';
|
|
43
|
+
Object.setPrototypeOf(this, ToolExecutionError.prototype);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Format the error message for LLM consumption
|
|
47
|
+
*/
|
|
48
|
+
toUserMessage() {
|
|
49
|
+
if (this.suggestion) {
|
|
50
|
+
return `${this.message}\n\nSuggestion: ${this.suggestion}`;
|
|
51
|
+
}
|
|
52
|
+
return this.message;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
28
55
|
/**
|
|
29
56
|
* Format API error into a user-friendly message
|
|
30
57
|
*
|
|
@@ -133,4 +160,55 @@ export function isRateLimitError(error) {
|
|
|
133
160
|
export function toResultAsync(promise) {
|
|
134
161
|
return ResultAsync.fromPromise(promise, formatApiError);
|
|
135
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Create a ToolExecutionError from an API error with helpful suggestions
|
|
165
|
+
*
|
|
166
|
+
* @param error - The formatted error message
|
|
167
|
+
* @param context - Optional context about the operation (e.g., "creating project")
|
|
168
|
+
* @returns ToolExecutionError with suggestion for recovery
|
|
169
|
+
*/
|
|
170
|
+
export function createToolError(error, context) {
|
|
171
|
+
const suggestion = getSuggestionForError(error, context);
|
|
172
|
+
return new ToolExecutionError(error, suggestion);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get a helpful suggestion based on the error message
|
|
176
|
+
*/
|
|
177
|
+
function getSuggestionForError(error, context) {
|
|
178
|
+
const lowerError = error.toLowerCase();
|
|
179
|
+
// Authentication errors
|
|
180
|
+
if (lowerError.includes('authentication failed') || lowerError.includes('401')) {
|
|
181
|
+
return 'Check that QASE_API_TOKEN environment variable is set correctly.';
|
|
182
|
+
}
|
|
183
|
+
// Permission errors
|
|
184
|
+
if (lowerError.includes('forbidden') || lowerError.includes('403')) {
|
|
185
|
+
return 'Verify you have the required permissions for this operation.';
|
|
186
|
+
}
|
|
187
|
+
// Not found errors
|
|
188
|
+
if (lowerError.includes('not found') || lowerError.includes('404')) {
|
|
189
|
+
return 'Verify the resource exists. Use the appropriate list or get tool to check.';
|
|
190
|
+
}
|
|
191
|
+
// Validation errors - try to provide context-specific suggestions
|
|
192
|
+
if (lowerError.includes('invalid') || lowerError.includes('validation')) {
|
|
193
|
+
if (context?.includes('project')) {
|
|
194
|
+
return 'The project code may already exist, or the input data is invalid. Use list_projects or get_project to check existing projects.';
|
|
195
|
+
}
|
|
196
|
+
if (context?.includes('case')) {
|
|
197
|
+
return 'Verify the project code exists and the case data is valid. Use get_project to check the project.';
|
|
198
|
+
}
|
|
199
|
+
if (context?.includes('run')) {
|
|
200
|
+
return 'Verify the project code exists and the run configuration is valid.';
|
|
201
|
+
}
|
|
202
|
+
return 'Check that all required fields are provided and values are in the correct format.';
|
|
203
|
+
}
|
|
204
|
+
// Rate limiting
|
|
205
|
+
if (lowerError.includes('rate limit') || lowerError.includes('429')) {
|
|
206
|
+
return 'Wait a moment and try again, or reduce the frequency of requests.';
|
|
207
|
+
}
|
|
208
|
+
// Server errors
|
|
209
|
+
if (lowerError.includes('server error') || lowerError.includes('5')) {
|
|
210
|
+
return 'This is a temporary server issue. Please try again later.';
|
|
211
|
+
}
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
136
214
|
//# sourceMappingURL=errors.js.map
|