@qase/mcp-server 1.0.0 → 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.
Files changed (82) 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 +16 -5
package/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.0]
9
+
10
+ ### Added
11
+
12
+ - Per-request authentication: clients can pass `Authorization: Bearer <token>` to use their own Qase API token instead of the shared `QASE_API_TOKEN` environment variable (supported on both Streamable HTTP and SSE transports)
13
+ - `create_case`, `update_case`: Added `steps_type` field (`classic` / `gherkin`) ([#17](https://github.com/qase-tms/qase-mcp-server/issues/17))
14
+ - 87 schema-API contract tests to prevent future type drift between Zod schemas and SDK expectations
15
+
16
+ ### Changed
17
+
18
+ - **Breaking (internal):** Replaced deprecated `qaseio` SDK with `qase-api-client` — the new auto-generated OpenAPI client for Qase API v1
19
+ - `QaseApiClient` wrapper now instantiates all 18 API classes (`ProjectsApi`, `CasesApi`, `ConfigurationsApi`, `SystemFieldsApi`, `UsersApi`, `SharedParametersApi`, etc.) instead of relying on the old `QaseApi` facade
20
+ - `list_users`, `get_user`: Now use `UsersApi` from the SDK instead of direct HTTP calls
21
+ - `list_shared_parameters`, `get_shared_parameter`: Now use `SharedParametersApi` from the SDK instead of direct HTTP calls
22
+ - `list_system_fields`: Now uses `SystemFieldsApi` from the SDK instead of direct HTTP calls
23
+ - `list_configurations`, `create_configuration_group`: Now use `ConfigurationsApi` from the SDK instead of direct HTTP calls
24
+ - `qql_search`: Updated to positional arguments `search(query, limit, offset)` per new SDK signature
25
+ - Case enum resolution (`normalizeCaseEnums`): Now fetches system fields via `SystemFieldsApi` instead of direct HTTP calls
26
+ - `create_case`, `update_case`: Enum fields (`severity`, `priority`, `type`, `layer`, `behavior`, `status`) now accept human-readable string labels instead of numeric IDs; `normalizeCaseEnums` resolves labels to IDs via system fields ([#13](https://github.com/qase-tms/qase-mcp-server/issues/13))
27
+
28
+ ### Fixed
29
+
30
+ - `list_defects`: Corrected API call signature (status as single value, not array) and removed unsupported severity filter
31
+ - `list_custom_fields`: Fixed argument order — was passing `(limit, offset)` where SDK expects `(entity, type, limit, offset)`
32
+ - `list_environments`: Fixed argument order — was passing `(code, limit, offset)` where SDK expects `(code, search, slug, limit, offset)`
33
+ - `list_authors`: Fixed argument order — was passing `(limit, offset)` where SDK expects `(search, type, limit, offset)`
34
+ - `TestCaseexternalIssuesTypeEnum` → `TestCaseExternalIssuesTypeEnum`: Fixed enum name casing for the new SDK
35
+ - `upload_attachment`: Fixed "file.forEach is not a function" crash — now correctly converts base64 string or file path into the `[{name, value}]` array format expected by the SDK's multipart upload ([#14](https://github.com/qase-tms/qase-mcp-server/issues/14))
36
+ - `create_defect`, `update_defect`: `severity` now accepts human-readable labels (`blocker`, `critical`, `major`, `normal`, `minor`, `trivial`) instead of numeric IDs; converted to numbers internally ([#18](https://github.com/qase-tms/qase-mcp-server/issues/18))
37
+ - `create_run`: Changed `start_time` / `end_time` from number to string (RFC 3339 format) matching SDK type
38
+ - `qql_search`: Removed non-existent fields from response to prevent confusion
39
+ - `list_attachments`: Added default `limit=10` to prevent slow responses on accounts with large numbers of attachments
40
+
8
41
  ## [1.0.0] - 2025-10-08
9
42
 
10
43
  ### Added
@@ -162,4 +195,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
162
195
  - Results are identified by hash, not numeric ID
163
196
  - Custom fields accessed via bracket notation in QQL: `cf["Field Name"]`
164
197
 
198
+ [1.1.0]: https://github.com/qase-tms/qase-mcp-server/compare/v1.0.0...v1.1.0
165
199
  [1.0.0]: https://github.com/qase-tms/qase-mcp-server/releases/tag/v1.0.0
package/README.md CHANGED
@@ -149,9 +149,47 @@ Or, if installed from source:
149
149
 
150
150
  ### Claude Code
151
151
 
152
- In Claude Code, the server can be configured similarly to Claude Desktop.
152
+ You can add the Qase MCP server to Claude Code using the CLI command:
153
153
 
154
- Add to your MCP configuration:
154
+ ```bash
155
+ claude mcp add qase -- npx -y @qase/mcp-server
156
+ ```
157
+
158
+ Set the required environment variable:
159
+
160
+ ```bash
161
+ export QASE_API_TOKEN=your_api_token_here
162
+ ```
163
+
164
+ Alternatively, add a `.mcp.json` file to your project root for automatic project-scoped configuration:
165
+
166
+ ```json
167
+ {
168
+ "mcpServers": {
169
+ "qase": {
170
+ "command": "npx",
171
+ "args": ["-y", "@qase/mcp-server"],
172
+ "env": {
173
+ "QASE_API_TOKEN": "your_api_token_here"
174
+ }
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ You can also use the `--scope` flag to choose where the configuration is stored:
181
+
182
+ ```bash
183
+ # Project-scoped (saved in .mcp.json)
184
+ claude mcp add --scope project qase -- npx -y @qase/mcp-server
185
+
186
+ # User-scoped (available in all projects)
187
+ claude mcp add --scope user qase -- npx -y @qase/mcp-server
188
+ ```
189
+
190
+ ### OpenAI Codex CLI
191
+
192
+ Add a `.codex/config.json` file to your project root:
155
193
 
156
194
  ```json
157
195
  {
@@ -167,6 +205,32 @@ Add to your MCP configuration:
167
205
  }
168
206
  ```
169
207
 
208
+ Set the required environment variable before running Codex:
209
+
210
+ ```bash
211
+ export QASE_API_TOKEN=your_api_token_here
212
+ ```
213
+
214
+ ### OpenCode
215
+
216
+ Add an `opencode.json` file to your project root (or `~/.config/opencode/opencode.json` for global configuration):
217
+
218
+ ```json
219
+ {
220
+ "mcp": {
221
+ "qase": {
222
+ "type": "local",
223
+ "command": ["npx", "-y", "@qase/mcp-server"],
224
+ "environment": {
225
+ "QASE_API_TOKEN": "your_api_token_here"
226
+ }
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ > **Note:** OpenCode uses a different format from other MCP clients — the command and args are combined into a single `command` array, env vars go under `environment`, and servers are nested under `mcp` (not `mcpServers`).
233
+
170
234
  ## Usage Examples
171
235
 
172
236
  ### Basic Operations
@@ -242,6 +306,13 @@ Create a test run in project DEMO called "Sprint 24 Regression" including all te
242
306
  - `attach_external_issue` - Link external issue (Jira, etc.)
243
307
  - `detach_external_issue` - Unlink external issue
244
308
 
309
+ ### Case enum values
310
+
311
+ `create_case`, `bulk_create_cases`, and `update_case` automatically normalize friendly labels to whatever numeric IDs your workspace currently configures for the built-in system fields (`priority`, `severity`, `type`, `behavior`, `status`, `layer`). The server fetches `/v1/system_field` and caches the available options so you can keep using the titles or slugs you see in Qase while still satisfying the API’s numeric requirements.
312
+
313
+ The normalization does not touch `automation`/`is_flaky`, so pass those values as the API expects (strings or booleans).
314
+ The server keeps any existing label in place when it does not match a known enum so invalid values still bubble up as errors from Qase.
315
+
245
316
  ### Test Runs (7 tools)
246
317
  - `list_runs` - List test runs
247
318
  - `get_run` - Get run details
@@ -365,6 +436,52 @@ npm run inspector
365
436
 
366
437
  Set your API token when prompted, then interact with tools in the inspector UI.
367
438
 
439
+ ### Transport Options
440
+
441
+ The server supports multiple transport types for different use cases:
442
+
443
+ #### Stdio Transport (Default)
444
+
445
+ Used by MCP clients like Claude Desktop and Cursor:
446
+
447
+ ```bash
448
+ npm start
449
+ # or
450
+ npm run start:stdio
451
+ ```
452
+
453
+ #### SSE Transport
454
+
455
+ Server-Sent Events for web-based clients:
456
+
457
+ ```bash
458
+ npm run start:sse
459
+ # Server runs on http://localhost:3000/sse
460
+ # Health check: http://localhost:3000/health
461
+ ```
462
+
463
+ #### Streamable HTTP Transport
464
+
465
+ Full HTTP-based transport with session management:
466
+
467
+ ```bash
468
+ npm run start:http
469
+ # Server runs on http://localhost:3000/mcp
470
+ # Health check: http://localhost:3000/health
471
+ ```
472
+
473
+ #### Custom Configuration
474
+
475
+ ```bash
476
+ # Custom port and host
477
+ node build/index.js --transport streamable-http --port 8080 --host 0.0.0.0
478
+
479
+ # Available options:
480
+ # --transport: stdio | sse | streamable-http (default: stdio)
481
+ # --port: Port number (default: 3000)
482
+ # --host: Host address (default: 0.0.0.0)
483
+ ```
484
+
368
485
  ## Troubleshooting
369
486
 
370
487
  ### Authentication Errors
@@ -385,6 +502,38 @@ Set your API token when prompted, then interact with tools in the inspector UI.
385
502
  2. Verify the API domain is correct (especially for enterprise customers)
386
503
  3. Check if Qase is accessible: https://api.qase.io/v1/
387
504
 
505
+ ### SSL Certificate Errors
506
+
507
+ **Error**: `unable to get local issuer certificate`
508
+
509
+ This error typically occurs in corporate environments with:
510
+ - SSL-intercepting proxy servers
511
+ - Self-signed certificates
512
+ - Internal Certificate Authorities (CA)
513
+
514
+ **Solution**: Add the `NODE_EXTRA_CA_CERTS` environment variable pointing to your CA certificate file:
515
+
516
+ ```json
517
+ {
518
+ "mcpServers": {
519
+ "qase": {
520
+ "command": "npx",
521
+ "args": ["-y", "@qase/mcp-server"],
522
+ "env": {
523
+ "QASE_API_TOKEN": "your_api_token_here",
524
+ "NODE_EXTRA_CA_CERTS": "/path/to/your/certificate.pem"
525
+ }
526
+ }
527
+ }
528
+ }
529
+ ```
530
+
531
+ To find your certificate:
532
+ - **Corporate environments**: Contact your IT department for the CA certificate
533
+ - **macOS**: Export from Keychain Access (System Roots → your CA → Export as .pem)
534
+ - **Windows**: Export from Certificate Manager (certmgr.msc)
535
+ - **Linux**: Usually in `/etc/ssl/certs/` or `/etc/pki/tls/certs/`
536
+
388
537
  ### Custom Domain Issues
389
538
 
390
539
  **Error**: `Invalid domain` or connection errors with custom domain
@@ -3,21 +3,65 @@
3
3
  *
4
4
  * Provides a singleton instance of the Qase API client with support for:
5
5
  * - Token-based authentication
6
+ * - Per-request Bearer token authentication
6
7
  * - Custom enterprise domains
7
8
  * - Environment-based configuration
8
9
  */
9
- import { QaseApi } from 'qaseio';
10
+ import { ProjectsApi, CasesApi, SuitesApi, RunsApi, ResultsApi, PlansApi, MilestonesApi, DefectsApi, EnvironmentsApi, AttachmentsApi, SharedStepsApi, AuthorsApi, CustomFieldsApi, SearchApi, ConfigurationsApi, SystemFieldsApi, UsersApi, SharedParametersApi } from 'qase-api-client';
11
+ import { AxiosRequestConfig } from 'axios';
10
12
  /**
11
- * Get or create the Qase API client instance
13
+ * Configuration for the Qase API client
14
+ */
15
+ interface ApiClientConfig {
16
+ token: string;
17
+ host: string;
18
+ }
19
+ /**
20
+ * Wrapper around qase-api-client that preserves the `client.resource.method()` interface.
21
+ */
22
+ declare class QaseApiClient {
23
+ readonly projects: ProjectsApi;
24
+ readonly cases: CasesApi;
25
+ readonly suites: SuitesApi;
26
+ readonly runs: RunsApi;
27
+ readonly results: ResultsApi;
28
+ readonly plans: PlansApi;
29
+ readonly milestones: MilestonesApi;
30
+ readonly defects: DefectsApi;
31
+ readonly environment: EnvironmentsApi;
32
+ readonly attachments: AttachmentsApi;
33
+ readonly sharedSteps: SharedStepsApi;
34
+ readonly authors: AuthorsApi;
35
+ readonly customFields: CustomFieldsApi;
36
+ readonly search: SearchApi;
37
+ readonly configurations: ConfigurationsApi;
38
+ readonly systemFields: SystemFieldsApi;
39
+ readonly users: UsersApi;
40
+ readonly sharedParameters: SharedParametersApi;
41
+ private readonly token;
42
+ private readonly host;
43
+ constructor(config: ApiClientConfig);
44
+ /**
45
+ * Make a direct API call for endpoints not fully covered by the SDK.
46
+ */
47
+ request<T = any>(path: string, options?: AxiosRequestConfig): Promise<T>;
48
+ }
49
+ /**
50
+ * Get or create the Qase API client instance.
51
+ *
52
+ * Auth priority:
53
+ * 1. Per-request Bearer token from Authorization header (set via AsyncLocalStorage)
54
+ * → creates a fresh QaseApiClient instance with the user's own token
55
+ * 2. Shared QASE_API_TOKEN env var (singleton, read-only fallback)
12
56
  *
13
- * @returns Configured QaseApi instance
14
- * @throws Error if QASE_API_TOKEN is not set
57
+ * @returns Configured QaseApiClient instance
58
+ * @throws Error if neither a request token nor QASE_API_TOKEN is available
15
59
  */
16
- export declare function getApiClient(): QaseApi;
60
+ export declare function getApiClient(): QaseApiClient;
17
61
  /**
18
62
  * Reset the client instance (useful for testing)
19
63
  * @internal
20
64
  */
21
65
  export declare function resetClientInstance(): void;
22
- export type { QaseApi } from 'qaseio';
66
+ export type { QaseApiClient };
23
67
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AA4CjC;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAWtC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAGD,YAAY,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,WAAW,EACX,QAAQ,EACR,SAAS,EACT,OAAO,EACP,UAAU,EACV,QAAQ,EACR,aAAa,EACb,UAAU,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,UAAU,EACV,eAAe,EACf,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,QAAQ,EACR,mBAAmB,EACpB,MAAM,iBAAiB,CAAC;AACzB,OAAc,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAIlD;;GAEG;AACH,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,cAAM,aAAa;IACjB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,iBAAiB,CAAC;IAC3C,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,CAAC;IAE/C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,MAAM,EAAE,eAAe;IA8BnC;;OAEG;IACG,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,CAAC,CAAC;CAcnF;AAqDD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,aAAa,CAY5C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,YAAY,EAAE,aAAa,EAAE,CAAC"}
@@ -3,45 +3,141 @@
3
3
  *
4
4
  * Provides a singleton instance of the Qase API client with support for:
5
5
  * - Token-based authentication
6
+ * - Per-request Bearer token authentication
6
7
  * - Custom enterprise domains
7
8
  * - Environment-based configuration
8
9
  */
9
- import { QaseApi } from 'qaseio';
10
+ import { Configuration, ProjectsApi, CasesApi, SuitesApi, RunsApi, ResultsApi, PlansApi, MilestonesApi, DefectsApi, EnvironmentsApi, AttachmentsApi, SharedStepsApi, AuthorsApi, CustomFieldsApi, SearchApi, ConfigurationsApi, SystemFieldsApi, UsersApi, SharedParametersApi, } from 'qase-api-client';
11
+ import axios from 'axios';
12
+ import FormData from 'form-data';
13
+ import { requestTokenStorage } from '../utils/auth-context.js';
10
14
  /**
11
- * Get API client configuration from environment variables
15
+ * Wrapper around qase-api-client that preserves the `client.resource.method()` interface.
12
16
  */
13
- function getConfig() {
14
- const token = process.env.QASE_API_TOKEN;
15
- if (!token) {
16
- throw new Error('QASE_API_TOKEN environment variable is required. ' +
17
- 'Get your token from: https://app.qase.io/user/api/token');
17
+ class QaseApiClient {
18
+ projects;
19
+ cases;
20
+ suites;
21
+ runs;
22
+ results;
23
+ plans;
24
+ milestones;
25
+ defects;
26
+ environment;
27
+ attachments;
28
+ sharedSteps;
29
+ authors;
30
+ customFields;
31
+ search;
32
+ configurations;
33
+ systemFields;
34
+ users;
35
+ sharedParameters;
36
+ token;
37
+ host;
38
+ constructor(config) {
39
+ this.token = config.token;
40
+ this.host = config.host;
41
+ const cfg = new Configuration({
42
+ apiKey: config.token,
43
+ basePath: `${config.host}/v1`,
44
+ formDataCtor: FormData,
45
+ });
46
+ this.projects = new ProjectsApi(cfg);
47
+ this.cases = new CasesApi(cfg);
48
+ this.suites = new SuitesApi(cfg);
49
+ this.runs = new RunsApi(cfg);
50
+ this.results = new ResultsApi(cfg);
51
+ this.plans = new PlansApi(cfg);
52
+ this.milestones = new MilestonesApi(cfg);
53
+ this.defects = new DefectsApi(cfg);
54
+ this.environment = new EnvironmentsApi(cfg);
55
+ this.attachments = new AttachmentsApi(cfg);
56
+ this.sharedSteps = new SharedStepsApi(cfg);
57
+ this.authors = new AuthorsApi(cfg);
58
+ this.customFields = new CustomFieldsApi(cfg);
59
+ this.search = new SearchApi(cfg);
60
+ this.configurations = new ConfigurationsApi(cfg);
61
+ this.systemFields = new SystemFieldsApi(cfg);
62
+ this.users = new UsersApi(cfg);
63
+ this.sharedParameters = new SharedParametersApi(cfg);
64
+ }
65
+ /**
66
+ * Make a direct API call for endpoints not fully covered by the SDK.
67
+ */
68
+ async request(path, options = {}) {
69
+ const response = await axios({
70
+ method: options.method || 'GET',
71
+ url: `${this.host}${path}`,
72
+ headers: {
73
+ Token: this.token,
74
+ 'Content-Type': 'application/json',
75
+ ...options.headers,
76
+ },
77
+ ...options,
78
+ });
79
+ return response.data;
18
80
  }
81
+ }
82
+ /**
83
+ * Get validated API host from QASE_API_DOMAIN env var.
84
+ */
85
+ function getHost() {
19
86
  const domain = process.env.QASE_API_DOMAIN || 'api.qase.io';
20
- // Validate domain format (should not include protocol or path)
21
87
  if (domain.includes('://') || domain.includes('/')) {
22
88
  throw new Error('QASE_API_DOMAIN should only contain the domain name (e.g., api.qase.io), ' +
23
89
  'not the full URL with protocol or path');
24
90
  }
25
- const host = `https://${domain}`;
26
- return { token, domain, host };
91
+ return `https://${domain}`;
92
+ }
93
+ /**
94
+ * Get the effective API token for the current request.
95
+ *
96
+ * Priority:
97
+ * 1. Per-request Bearer token from Authorization header (AsyncLocalStorage)
98
+ * 2. Shared QASE_API_TOKEN environment variable
99
+ */
100
+ function getEffectiveToken() {
101
+ const requestToken = requestTokenStorage.getStore();
102
+ if (requestToken) {
103
+ return requestToken;
104
+ }
105
+ const envToken = process.env.QASE_API_TOKEN;
106
+ if (!envToken) {
107
+ throw new Error('QASE_API_TOKEN environment variable is required. ' +
108
+ 'Get your token from: https://app.qase.io/user/api/token');
109
+ }
110
+ return envToken;
111
+ }
112
+ /**
113
+ * Get full API client config (token + host).
114
+ */
115
+ function getConfig() {
116
+ return { token: getEffectiveToken(), host: getHost() };
27
117
  }
28
118
  /**
29
- * Singleton API client instance
119
+ * Singleton API client instance (used for shared QASE_API_TOKEN fallback).
30
120
  */
31
121
  let clientInstance = null;
32
122
  /**
33
- * Get or create the Qase API client instance
123
+ * Get or create the Qase API client instance.
34
124
  *
35
- * @returns Configured QaseApi instance
36
- * @throws Error if QASE_API_TOKEN is not set
125
+ * Auth priority:
126
+ * 1. Per-request Bearer token from Authorization header (set via AsyncLocalStorage)
127
+ * → creates a fresh QaseApiClient instance with the user's own token
128
+ * 2. Shared QASE_API_TOKEN env var (singleton, read-only fallback)
129
+ *
130
+ * @returns Configured QaseApiClient instance
131
+ * @throws Error if neither a request token nor QASE_API_TOKEN is available
37
132
  */
38
133
  export function getApiClient() {
134
+ const requestToken = requestTokenStorage.getStore();
135
+ if (requestToken) {
136
+ return new QaseApiClient({ token: requestToken, host: getHost() });
137
+ }
39
138
  if (!clientInstance) {
40
139
  const config = getConfig();
41
- clientInstance = new QaseApi({
42
- token: config.token,
43
- host: config.host,
44
- });
140
+ clientInstance = new QaseApiClient({ token: config.token, host: config.host });
45
141
  }
46
142
  return clientInstance;
47
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAWjC;;GAEG;AACH,SAAS,SAAS;IAChB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAEzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,mDAAmD;YACjD,yDAAyD,CAC5D,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,aAAa,CAAC;IAE5D,+DAA+D;IAC/D,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,2EAA2E;YACzE,wCAAwC,CAC3C,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,MAAM,EAAE,CAAC;IAEjC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,IAAI,cAAc,GAAmB,IAAI,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAE3B,cAAc,GAAG,IAAI,OAAO,CAAC;YAC3B,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,cAAc,GAAG,IAAI,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
package/build/index.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * It exposes 90+ tools for managing projects, test cases, runs, results, defects, and more.
7
7
  *
8
8
  * Architecture:
9
- * - Uses stdio transport for communication with MCP clients
9
+ * - Supports multiple transports: stdio, sse, streamable-http
10
10
  * - Tools are registered via the global tool registry
11
11
  * - Operation modules self-register their tools on import
12
12
  * - All API errors are handled gracefully with user-friendly messages
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;GAWG;AASH,OAAO,0BAA0B,CAAC;AAClC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,yBAAyB,CAAC;AACjC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,8BAA8B,CAAC;AACtC,OAAO,mCAAmC,CAAC;AAC3C,OAAO,4BAA4B,CAAC;AACpC,OAAO,yBAAyB,CAAC;AACjC,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,yBAAyB,CAAC;AACjC,OAAO,+BAA+B,CAAC;AACvC,OAAO,+BAA+B,CAAC;AACvC,OAAO,gCAAgC,CAAC;AACxC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;GAWG;AAWH,OAAO,0BAA0B,CAAC;AAClC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC;AAChC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,yBAAyB,CAAC;AACjC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,8BAA8B,CAAC;AACtC,OAAO,mCAAmC,CAAC;AAC3C,OAAO,4BAA4B,CAAC;AACpC,OAAO,yBAAyB,CAAC;AACjC,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,yBAAyB,CAAC;AACjC,OAAO,+BAA+B,CAAC;AACvC,OAAO,+BAA+B,CAAC;AACvC,OAAO,gCAAgC,CAAC;AACxC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,wBAAwB,CAAC"}