@qase/mcp-server 1.0.0 → 1.1.1

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 (83) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +151 -2
  3. package/build/client/index.d.ts +50 -6
  4. package/build/client/index.d.ts.map +1 -1
  5. package/build/client/index.js +114 -18
  6. package/build/client/index.js.map +1 -1
  7. package/build/index.d.ts +1 -1
  8. package/build/index.d.ts.map +1 -1
  9. package/build/index.js +170 -62
  10. package/build/index.js.map +1 -1
  11. package/build/operations/attachments.js +35 -16
  12. package/build/operations/attachments.js.map +1 -1
  13. package/build/operations/authors.js +4 -4
  14. package/build/operations/authors.js.map +1 -1
  15. package/build/operations/cases.js +75 -34
  16. package/build/operations/cases.js.map +1 -1
  17. package/build/operations/configurations.d.ts +2 -0
  18. package/build/operations/configurations.d.ts.map +1 -1
  19. package/build/operations/configurations.js +28 -8
  20. package/build/operations/configurations.js.map +1 -1
  21. package/build/operations/custom-fields.js +7 -7
  22. package/build/operations/custom-fields.js.map +1 -1
  23. package/build/operations/defects.js +59 -29
  24. package/build/operations/defects.js.map +1 -1
  25. package/build/operations/environments.js +7 -7
  26. package/build/operations/environments.js.map +1 -1
  27. package/build/operations/milestones.js +6 -6
  28. package/build/operations/milestones.js.map +1 -1
  29. package/build/operations/plans.js +6 -6
  30. package/build/operations/plans.js.map +1 -1
  31. package/build/operations/projects.js +8 -8
  32. package/build/operations/projects.js.map +1 -1
  33. package/build/operations/results.js +7 -7
  34. package/build/operations/results.js.map +1 -1
  35. package/build/operations/runs.js +16 -10
  36. package/build/operations/runs.js.map +1 -1
  37. package/build/operations/search.js +10 -13
  38. package/build/operations/search.js.map +1 -1
  39. package/build/operations/shared-parameters.d.ts +2 -0
  40. package/build/operations/shared-parameters.d.ts.map +1 -1
  41. package/build/operations/shared-parameters.js +30 -78
  42. package/build/operations/shared-parameters.js.map +1 -1
  43. package/build/operations/shared-steps.js +6 -6
  44. package/build/operations/shared-steps.js.map +1 -1
  45. package/build/operations/suites.js +6 -6
  46. package/build/operations/suites.js.map +1 -1
  47. package/build/operations/system-fields.d.ts +2 -0
  48. package/build/operations/system-fields.d.ts.map +1 -1
  49. package/build/operations/system-fields.js +8 -2
  50. package/build/operations/system-fields.js.map +1 -1
  51. package/build/operations/users.d.ts +2 -0
  52. package/build/operations/users.d.ts.map +1 -1
  53. package/build/operations/users.js +19 -11
  54. package/build/operations/users.js.map +1 -1
  55. package/build/transports/sse.d.ts +10 -0
  56. package/build/transports/sse.d.ts.map +1 -0
  57. package/build/transports/sse.js +38 -0
  58. package/build/transports/sse.js.map +1 -0
  59. package/build/transports/streamableHttp.d.ts +9 -0
  60. package/build/transports/streamableHttp.d.ts.map +1 -0
  61. package/build/transports/streamableHttp.js +169 -0
  62. package/build/transports/streamableHttp.js.map +1 -0
  63. package/build/utils/auth-context.d.ts +8 -0
  64. package/build/utils/auth-context.d.ts.map +1 -0
  65. package/build/utils/auth-context.js +8 -0
  66. package/build/utils/auth-context.js.map +1 -0
  67. package/build/utils/case-enums.d.ts +6 -0
  68. package/build/utils/case-enums.d.ts.map +1 -0
  69. package/build/utils/case-enums.js +91 -0
  70. package/build/utils/case-enums.js.map +1 -0
  71. package/build/utils/errors.d.ts +25 -0
  72. package/build/utils/errors.d.ts.map +1 -1
  73. package/build/utils/errors.js +78 -0
  74. package/build/utils/errors.js.map +1 -1
  75. package/build/utils/registry.d.ts.map +1 -1
  76. package/build/utils/registry.js +4 -0
  77. package/build/utils/registry.js.map +1 -1
  78. package/build/utils/test-helpers.d.ts +2 -2
  79. package/build/utils/test-helpers.d.ts.map +1 -1
  80. package/build/utils/test-helpers.js +16 -0
  81. package/build/utils/test-helpers.js.map +1 -1
  82. package/package.json +17 -5
  83. package/server.json +36 -0
@@ -1 +1 @@
1
- {"version":3,"file":"system-fields.js","sourceRoot":"","sources":["../../src/operations/system-fields.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;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,MAAM,oBAAoB,CAAC;AAEnD,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;GAEG;AACH,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAE5C,+EAA+E;AAC/E,WAAW;AACX,+EAA+E;AAE/E;;GAEG;AACH,KAAK,UAAU,gBAAgB,CAAC,KAA6C;IAC3E,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAE9B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAE,MAAc,CAAC,YAAY,CAAC,eAAe,EAAE,CAAC,CAAC;IAEnF,OAAO,MAAM,CAAC,KAAK,CACjB,CAAC,QAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EACvC,CAAC,KAAK,EAAE,EAAE;QACR,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IACzB,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
+ {"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"}
@@ -3,6 +3,8 @@
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
  export {};
8
10
  //# sourceMappingURL=users.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/operations/users.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
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(args) {
41
+ async function listUsers(_args) {
35
42
  const client = getApiClient();
36
- const { limit, offset } = args;
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 new Error(error);
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 new Error(error);
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 with optional pagination',
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;;;;;GAKG;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,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;GAEG;AACH,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;IAC1F,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;CACtF,CAAC,CAAC;AAEH;;GAEG;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;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,IAAqC;IAC5D,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAE/B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAE,MAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IAElF,OAAO,MAAM,CAAC,KAAK,CACjB,CAAC,QAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EACvC,CAAC,KAAK,EAAE,EAAE;QACR,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;GAEG;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,CAAE,MAAc,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAEtE,OAAO,MAAM,CAAC,KAAK,CACjB,CAAC,QAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EACvC,CAAC,KAAK,EAAE,EAAE;QACR,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IACzB,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,yDAAyD;IACtE,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"}
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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"}
@@ -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"}
@@ -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