@qase/mcp-server 2.0.0 → 2.0.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.
package/CHANGELOG.md CHANGED
@@ -5,11 +5,24 @@ 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
+ ## [2.0.1]
9
+
10
+ ### Added
11
+
12
+ - **`qase_external_issue_link`** — Link or unlink test cases and test runs to issues in an external tracker (Jira Cloud / Jira Server), wrapping `POST /v1/case/{code}/external-issue/attach`, `.../detach`, and `POST /v1/run/{code}/external-issue`. A case can hold several links; a run holds one, and detaching clears it. ([#64](https://github.com/qase-tms/qase-mcp-server/issues/64))
13
+ - **`qase_get`** now requests external issue links for cases and runs by default (`include=external_issues` / `include=external_issue`), so a link created via `qase_external_issue_link` can be verified without leaving the MCP client. The new `include` parameter overrides the default; if a deployment rejects the value, the request is retried without it. ([#65](https://github.com/qase-tms/qase-mcp-server/issues/65))
14
+
15
+ - **`qase_case_bulk_create`** — Create up to 100 test cases in a single request, restoring the bulk creation that v1 offered as `bulk_create_cases`. Unlike the v1 tool it normalises enum labels per case, so `priority: "high"` works the same as in `qase_case_upsert`.
16
+
17
+ ### Fixed
18
+
19
+ - `qase_discover_tools` no longer skips activation when the `activate` argument is omitted. Tool handlers receive raw MCP arguments, so the schema default never applied and matched tools stayed hidden from the client's tool list.
20
+
8
21
  ## [2.0.0]
9
22
 
10
23
  ### Breaking Changes
11
24
 
12
- - **All 83 v1 tool names have been removed.** The tool set has been consolidated from 83 tools to 29. See [MIGRATION.md](./MIGRATION.md) for a complete v1→v2 tool mapping table.
25
+ - **All 83 v1 tool names have been removed.** The tool set has been consolidated from 83 tools to 29. See [docs/migration.md](docs/migration.md) for a complete v1→v2 tool mapping table.
13
26
  - Response format is now compact JSON with null values stripped (no indentation). This reduces token usage but changes the raw string format of responses.
14
27
 
15
28
  ### Added
package/README.md CHANGED
@@ -27,7 +27,7 @@ The Qase MCP Server lets AI assistants (Claude, Cursor, Codex, and any other MCP
27
27
 
28
28
  **Features:**
29
29
 
30
- - **29 task-oriented tools** (30 total, including `qase_discover_tools`) — consolidated from 83 v1 tools for lower token usage and better LLM accuracy
30
+ - **31 task-oriented tools** (32 total, including `qase_discover_tools`) — consolidated from 83 v1 tools for lower token usage and better LLM accuracy
31
31
  - **Composite tools** — multi-step workflows in a single call: CI reporting, defect triage, regression run setup
32
32
  - **QQL support** — Qase Query Language for advanced searches across cases, runs, results, defects, and plans
33
33
  - **Project context bootstrap** — one call returns full project structure (suites, milestones, environments, users, custom fields)
@@ -76,17 +76,17 @@ Then point your MCP client's stdio config at the `@qase/mcp-server` binary. Full
76
76
 
77
77
  ## Upgrading from v1
78
78
 
79
- v2 consolidates 83 v1 tools into **29 task-oriented tools** (30 total, including a discovery tool). Tool names and response shapes have changed. See **[MIGRATION.md](MIGRATION.md)** for the complete tool mapping table, response format changes, and before/after examples.
79
+ v2 consolidates 83 v1 tools into **29 task-oriented tools** (30 total, including a discovery tool). Tool names and response shapes have changed. See **[docs/migration.md](docs/migration.md)** for the complete tool mapping table, response format changes, and before/after examples.
80
80
 
81
81
  ## Tools
82
82
 
83
- 30 tools across 6 groups (29 task-oriented tools plus `qase_discover_tools` for on-demand activation of secondary tools):
83
+ 32 tools across 6 groups (31 task-oriented tools plus `qase_discover_tools` for on-demand activation of secondary tools):
84
84
 
85
85
  | Group | Count | Description |
86
86
  | --- | --- | --- |
87
87
  | Read | 2 | Fetch any entity by type/ID, or bootstrap full project context in one call |
88
88
  | QQL | 2 | Search across cases, runs, results, defects, and plans with Qase Query Language |
89
- | Write | 21 | Create, update, and delete cases, runs, results, defects, suites, milestones, plans, shared steps, environments, and attachments |
89
+ | Write | 23 | Create, update, and delete cases (single or up to 100 at once), runs, results, defects, suites, milestones, plans, shared steps, environments, and attachments; link cases and runs to Jira issues |
90
90
  | Composite | 3 | Multi-step workflows in one call: CI reporting, defect triage, regression run setup |
91
91
  | Escape hatch | 1 | Direct REST API access for any endpoint not covered by the tools above |
92
92
  | Meta | 1 | `qase_discover_tools` — find and activate secondary tools on demand |
@@ -99,7 +99,7 @@ Full tool-by-tool reference with parameters and the discovery model: **[docs/too
99
99
  - **[docs/self-run.md](docs/self-run.md)** — install and run the server yourself (config, clients, transports)
100
100
  - **[docs/tools.md](docs/tools.md)** — full tool reference, groups, and the discovery model
101
101
  - **[docs/troubleshooting.md](docs/troubleshooting.md)** — auth, OAuth/connector, and SSL issues
102
- - **[MIGRATION.md](MIGRATION.md)** — v1 → v2 tool mapping and migration guide
102
+ - **[docs/migration.md](docs/migration.md)** — v1 → v2 tool mapping and migration guide
103
103
 
104
104
  ## Contributing
105
105
 
@@ -2,6 +2,7 @@ import './read/project-context.js';
2
2
  import './read/get.js';
3
3
  import './qql/index.js';
4
4
  import './write/cases.js';
5
+ import './write/cases-bulk.js';
5
6
  import './write/runs.js';
6
7
  import './write/results.js';
7
8
  import './write/defects.js';
@@ -11,6 +12,7 @@ import './write/plans.js';
11
12
  import './write/shared-steps.js';
12
13
  import './write/environments.js';
13
14
  import './write/attachments.js';
15
+ import './write/external-issues.js';
14
16
  import './composites/ci-report.js';
15
17
  import './composites/triage-defect.js';
16
18
  import './composites/regression-run.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/operations-v2/index.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC;AACnC,OAAO,eAAe,CAAC;AAGvB,OAAO,gBAAgB,CAAC;AAGxB,OAAO,kBAAkB,CAAC;AAC1B,OAAO,iBAAiB,CAAC;AACzB,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,mBAAmB,CAAC;AAC3B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,yBAAyB,CAAC;AACjC,OAAO,yBAAyB,CAAC;AACjC,OAAO,wBAAwB,CAAC;AAGhC,OAAO,2BAA2B,CAAC;AACnC,OAAO,+BAA+B,CAAC;AACvC,OAAO,gCAAgC,CAAC;AAGxC,OAAO,oBAAoB,CAAC;AAG5B,OAAO,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/operations-v2/index.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC;AACnC,OAAO,eAAe,CAAC;AAGvB,OAAO,gBAAgB,CAAC;AAGxB,OAAO,kBAAkB,CAAC;AAC1B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,iBAAiB,CAAC;AACzB,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,mBAAmB,CAAC;AAC3B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,yBAAyB,CAAC;AACjC,OAAO,yBAAyB,CAAC;AACjC,OAAO,wBAAwB,CAAC;AAChC,OAAO,4BAA4B,CAAC;AAGpC,OAAO,2BAA2B,CAAC;AACnC,OAAO,+BAA+B,CAAC;AACvC,OAAO,gCAAgC,CAAC;AAGxC,OAAO,oBAAoB,CAAC;AAG5B,OAAO,iBAAiB,CAAC"}
@@ -5,6 +5,7 @@ import './read/get.js';
5
5
  import './qql/index.js';
6
6
  // Write tools
7
7
  import './write/cases.js';
8
+ import './write/cases-bulk.js';
8
9
  import './write/runs.js';
9
10
  import './write/results.js';
10
11
  import './write/defects.js';
@@ -14,6 +15,7 @@ import './write/plans.js';
14
15
  import './write/shared-steps.js';
15
16
  import './write/environments.js';
16
17
  import './write/attachments.js';
18
+ import './write/external-issues.js';
17
19
  // Composites
18
20
  import './composites/ci-report.js';
19
21
  import './composites/triage-defect.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/operations-v2/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,2BAA2B,CAAC;AACnC,OAAO,eAAe,CAAC;AAEvB,YAAY;AACZ,OAAO,gBAAgB,CAAC;AAExB,cAAc;AACd,OAAO,kBAAkB,CAAC;AAC1B,OAAO,iBAAiB,CAAC;AACzB,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,mBAAmB,CAAC;AAC3B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,yBAAyB,CAAC;AACjC,OAAO,yBAAyB,CAAC;AACjC,OAAO,wBAAwB,CAAC;AAEhC,aAAa;AACb,OAAO,2BAA2B,CAAC;AACnC,OAAO,+BAA+B,CAAC;AACvC,OAAO,gCAAgC,CAAC;AAExC,aAAa;AACb,OAAO,oBAAoB,CAAC;AAE5B,eAAe;AACf,OAAO,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/operations-v2/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,2BAA2B,CAAC;AACnC,OAAO,eAAe,CAAC;AAEvB,YAAY;AACZ,OAAO,gBAAgB,CAAC;AAExB,cAAc;AACd,OAAO,kBAAkB,CAAC;AAC1B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,iBAAiB,CAAC;AACzB,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,mBAAmB,CAAC;AAC3B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,yBAAyB,CAAC;AACjC,OAAO,yBAAyB,CAAC;AACjC,OAAO,wBAAwB,CAAC;AAChC,OAAO,4BAA4B,CAAC;AAEpC,aAAa;AACb,OAAO,2BAA2B,CAAC;AACnC,OAAO,+BAA+B,CAAC;AACvC,OAAO,gCAAgC,CAAC;AAExC,aAAa;AACb,OAAO,oBAAoB,CAAC;AAE5B,eAAe;AACf,OAAO,iBAAiB,CAAC"}
@@ -25,7 +25,10 @@ const Schema = z.object({
25
25
  .describe('If true (default), found tools are activated and become available for use'),
26
26
  });
27
27
  async function handler(args) {
28
- const { query, category, activate } = args;
28
+ const { query, category } = args;
29
+ // Handlers receive raw MCP arguments, so the schema default never runs —
30
+ // apply it here or discovery silently stops activating anything.
31
+ const activate = args.activate ?? true;
29
32
  let matches = query ? toolRegistry.searchTools(query) : toolRegistry.getAllTools();
30
33
  // Filter by category based on tool annotations
31
34
  if (category && category !== 'all') {
@@ -1 +1 @@
1
- {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../../src/operations-v2/meta/discover.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../../src/operations-v2/meta/discover.ts"],"names":[],"mappings":"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"}
@@ -43,11 +43,26 @@ const Schema = z.object({
43
43
  .array(z.string())
44
44
  .optional()
45
45
  .describe('Optional field projection — only return these top-level fields. Pass ["*"] for all fields.'),
46
+ include: z
47
+ .string()
48
+ .optional()
49
+ .describe('Comma-separated list of related entities to include in the response. ' +
50
+ 'Cases and runs already request their external issue links by default ' +
51
+ '("external_issues" / "external_issue"); pass this only to override that.'),
46
52
  });
53
+ /**
54
+ * Related entities requested automatically so linked issues are visible without
55
+ * the caller knowing about the `include` query parameter. The Qase API omits
56
+ * these fields entirely unless they are asked for.
57
+ */
58
+ const DEFAULT_INCLUDE = {
59
+ case: 'external_issues',
60
+ run: 'external_issue',
61
+ };
47
62
  const FETCHERS = {
48
- case: (c, code, id) => c.cases.getCase(code, id),
63
+ case: (c, code, id, include) => c.cases.getCase(code, id, include),
49
64
  suite: (c, code, id) => c.suites.getSuite(code, id),
50
- run: (c, code, id) => c.runs.getRun(code, id),
65
+ run: (c, code, id, include) => c.runs.getRun(code, id, include),
51
66
  result: (c, code, id) => c.results.getResult(code, id),
52
67
  plan: (c, code, id) => c.plans.getPlan(code, id),
53
68
  defect: (c, code, id) => c.defects.getDefect(code, id),
@@ -62,7 +77,7 @@ const FETCHERS = {
62
77
  custom_field: (c, _code, id) => c.customFields.getCustomField(id),
63
78
  };
64
79
  async function handler(args) {
65
- const { entity, code, id, fields: fieldList } = args;
80
+ const { entity, code, id, fields: fieldList, include } = args;
66
81
  if (ENTITIES_REQUIRING_CODE.has(entity) && !code) {
67
82
  throw createToolError(`Project code is required for entity type "${entity}"`, 'get operation');
68
83
  }
@@ -70,7 +85,16 @@ async function handler(args) {
70
85
  const fetcher = FETCHERS[entity];
71
86
  if (!fetcher)
72
87
  throw createToolError(`Unknown entity type: ${entity}`, 'get operation');
73
- const result = await toResultAsync(fetcher(client, code || '', id));
88
+ const effectiveInclude = include ?? DEFAULT_INCLUDE[entity];
89
+ const isDefaultInclude = !include && effectiveInclude !== undefined;
90
+ let result = await toResultAsync(fetcher(client, code || '', id, effectiveInclude));
91
+ // Deployments that don't know the `include` value (older self-hosted
92
+ // instances) reject the request outright — retry without it rather than
93
+ // failing a plain get. An explicit `include` from the caller is never
94
+ // silently dropped.
95
+ if (result.isErr() && isDefaultInclude) {
96
+ result = await toResultAsync(fetcher(client, code || '', id, undefined));
97
+ }
74
98
  return result.match((response) => {
75
99
  const data = response.data.result;
76
100
  return fieldList ? projectFields(data, fieldList) : data;
@@ -1 +1 @@
1
- {"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/operations-v2/read/get.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/operations-v2/read/get.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Shared test case field definitions.
3
+ *
4
+ * `qase_case_upsert` and `qase_case_bulk_create` accept the same case body, so
5
+ * the schema and the automation mapping live here instead of being duplicated
6
+ * — a new case field only has to be added once.
7
+ */
8
+ import { z } from 'zod';
9
+ export declare const TestStepSchema: z.ZodObject<{
10
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
11
+ action: z.ZodOptional<z.ZodString>;
12
+ expected_result: z.ZodOptional<z.ZodString>;
13
+ data: z.ZodOptional<z.ZodString>;
14
+ value: z.ZodOptional<z.ZodString>;
15
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
16
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
17
+ action: z.ZodOptional<z.ZodString>;
18
+ expected_result: z.ZodOptional<z.ZodString>;
19
+ data: z.ZodOptional<z.ZodString>;
20
+ value: z.ZodOptional<z.ZodString>;
21
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
22
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
23
+ action: z.ZodOptional<z.ZodString>;
24
+ expected_result: z.ZodOptional<z.ZodString>;
25
+ data: z.ZodOptional<z.ZodString>;
26
+ value: z.ZodOptional<z.ZodString>;
27
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
28
+ }, z.ZodTypeAny, "passthrough">>, "many">>;
29
+ action: z.ZodOptional<z.ZodString>;
30
+ expected_result: z.ZodOptional<z.ZodString>;
31
+ data: z.ZodOptional<z.ZodString>;
32
+ value: z.ZodOptional<z.ZodString>;
33
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
34
+ }, "strip", z.ZodTypeAny, {
35
+ value?: string | undefined;
36
+ data?: string | undefined;
37
+ action?: string | undefined;
38
+ expected_result?: string | undefined;
39
+ attachments?: string[] | undefined;
40
+ steps?: z.objectOutputType<{
41
+ action: z.ZodOptional<z.ZodString>;
42
+ expected_result: z.ZodOptional<z.ZodString>;
43
+ data: z.ZodOptional<z.ZodString>;
44
+ value: z.ZodOptional<z.ZodString>;
45
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
46
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
47
+ }, {
48
+ value?: string | undefined;
49
+ data?: string | undefined;
50
+ action?: string | undefined;
51
+ expected_result?: string | undefined;
52
+ attachments?: string[] | undefined;
53
+ steps?: z.objectInputType<{
54
+ action: z.ZodOptional<z.ZodString>;
55
+ expected_result: z.ZodOptional<z.ZodString>;
56
+ data: z.ZodOptional<z.ZodString>;
57
+ value: z.ZodOptional<z.ZodString>;
58
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
59
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
60
+ }>;
61
+ export declare const CaseFieldsSchema: z.ZodObject<{
62
+ title: z.ZodString;
63
+ description: z.ZodOptional<z.ZodString>;
64
+ preconditions: z.ZodOptional<z.ZodString>;
65
+ postconditions: z.ZodOptional<z.ZodString>;
66
+ severity: z.ZodOptional<z.ZodString>;
67
+ priority: z.ZodOptional<z.ZodString>;
68
+ type: z.ZodOptional<z.ZodString>;
69
+ layer: z.ZodOptional<z.ZodString>;
70
+ behavior: z.ZodOptional<z.ZodString>;
71
+ automation: z.ZodOptional<z.ZodString>;
72
+ status: z.ZodOptional<z.ZodString>;
73
+ is_flaky: z.ZodOptional<z.ZodBoolean>;
74
+ suite_id: z.ZodOptional<z.ZodNumber>;
75
+ milestone_id: z.ZodOptional<z.ZodNumber>;
76
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
77
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
78
+ action: z.ZodOptional<z.ZodString>;
79
+ expected_result: z.ZodOptional<z.ZodString>;
80
+ data: z.ZodOptional<z.ZodString>;
81
+ value: z.ZodOptional<z.ZodString>;
82
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
83
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
84
+ action: z.ZodOptional<z.ZodString>;
85
+ expected_result: z.ZodOptional<z.ZodString>;
86
+ data: z.ZodOptional<z.ZodString>;
87
+ value: z.ZodOptional<z.ZodString>;
88
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
89
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
90
+ action: z.ZodOptional<z.ZodString>;
91
+ expected_result: z.ZodOptional<z.ZodString>;
92
+ data: z.ZodOptional<z.ZodString>;
93
+ value: z.ZodOptional<z.ZodString>;
94
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
95
+ }, z.ZodTypeAny, "passthrough">>, "many">>;
96
+ action: z.ZodOptional<z.ZodString>;
97
+ expected_result: z.ZodOptional<z.ZodString>;
98
+ data: z.ZodOptional<z.ZodString>;
99
+ value: z.ZodOptional<z.ZodString>;
100
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
101
+ }, "strip", z.ZodTypeAny, {
102
+ value?: string | undefined;
103
+ data?: string | undefined;
104
+ action?: string | undefined;
105
+ expected_result?: string | undefined;
106
+ attachments?: string[] | undefined;
107
+ steps?: z.objectOutputType<{
108
+ action: z.ZodOptional<z.ZodString>;
109
+ expected_result: z.ZodOptional<z.ZodString>;
110
+ data: z.ZodOptional<z.ZodString>;
111
+ value: z.ZodOptional<z.ZodString>;
112
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
113
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
114
+ }, {
115
+ value?: string | undefined;
116
+ data?: string | undefined;
117
+ action?: string | undefined;
118
+ expected_result?: string | undefined;
119
+ attachments?: string[] | undefined;
120
+ steps?: z.objectInputType<{
121
+ action: z.ZodOptional<z.ZodString>;
122
+ expected_result: z.ZodOptional<z.ZodString>;
123
+ data: z.ZodOptional<z.ZodString>;
124
+ value: z.ZodOptional<z.ZodString>;
125
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
126
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
127
+ }>, "many">>;
128
+ steps_type: z.ZodOptional<z.ZodEnum<["classic", "gherkin"]>>;
129
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
130
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
131
+ custom_field: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
132
+ }, "strip", z.ZodTypeAny, {
133
+ title: string;
134
+ type?: string | undefined;
135
+ status?: string | undefined;
136
+ description?: string | undefined;
137
+ priority?: string | undefined;
138
+ custom_field?: Record<string, any> | undefined;
139
+ behavior?: string | undefined;
140
+ severity?: string | undefined;
141
+ layer?: string | undefined;
142
+ automation?: string | undefined;
143
+ attachments?: string[] | undefined;
144
+ steps?: {
145
+ value?: string | undefined;
146
+ data?: string | undefined;
147
+ action?: string | undefined;
148
+ expected_result?: string | undefined;
149
+ attachments?: string[] | undefined;
150
+ steps?: z.objectOutputType<{
151
+ action: z.ZodOptional<z.ZodString>;
152
+ expected_result: z.ZodOptional<z.ZodString>;
153
+ data: z.ZodOptional<z.ZodString>;
154
+ value: z.ZodOptional<z.ZodString>;
155
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
156
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
157
+ }[] | undefined;
158
+ preconditions?: string | undefined;
159
+ postconditions?: string | undefined;
160
+ is_flaky?: boolean | undefined;
161
+ suite_id?: number | undefined;
162
+ milestone_id?: number | undefined;
163
+ steps_type?: "classic" | "gherkin" | undefined;
164
+ tags?: string[] | undefined;
165
+ }, {
166
+ title: string;
167
+ type?: string | undefined;
168
+ status?: string | undefined;
169
+ description?: string | undefined;
170
+ priority?: string | undefined;
171
+ custom_field?: Record<string, any> | undefined;
172
+ behavior?: string | undefined;
173
+ severity?: string | undefined;
174
+ layer?: string | undefined;
175
+ automation?: string | undefined;
176
+ attachments?: string[] | undefined;
177
+ steps?: {
178
+ value?: string | undefined;
179
+ data?: string | undefined;
180
+ action?: string | undefined;
181
+ expected_result?: string | undefined;
182
+ attachments?: string[] | undefined;
183
+ steps?: z.objectInputType<{
184
+ action: z.ZodOptional<z.ZodString>;
185
+ expected_result: z.ZodOptional<z.ZodString>;
186
+ data: z.ZodOptional<z.ZodString>;
187
+ value: z.ZodOptional<z.ZodString>;
188
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
189
+ }, z.ZodTypeAny, "passthrough">[] | undefined;
190
+ }[] | undefined;
191
+ preconditions?: string | undefined;
192
+ postconditions?: string | undefined;
193
+ is_flaky?: boolean | undefined;
194
+ suite_id?: number | undefined;
195
+ milestone_id?: number | undefined;
196
+ steps_type?: "classic" | "gherkin" | undefined;
197
+ tags?: string[] | undefined;
198
+ }>;
199
+ /**
200
+ * Map the user-facing `automation` enum (0=Manual, 1=To be automated,
201
+ * 2=Automated) to the current API contract (`isManual` + `isToBeAutomated`).
202
+ * The legacy `automation` field is deprecated in qase-api-client and dropped
203
+ * from the outbound payload.
204
+ */
205
+ export declare function applyAutomationMapping(caseData: Record<string, unknown>): Record<string, unknown>;
206
+ //# sourceMappingURL=case-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"case-fields.d.ts","sourceRoot":"","sources":["../../../src/operations-v2/write/case-fields.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMzB,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4B3B,CAAC;AAEH;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA0BjG"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shared test case field definitions.
3
+ *
4
+ * `qase_case_upsert` and `qase_case_bulk_create` accept the same case body, so
5
+ * the schema and the automation mapping live here instead of being duplicated
6
+ * — a new case field only has to be added once.
7
+ */
8
+ import { z } from 'zod';
9
+ const stepFields = {
10
+ action: z.string().optional().describe('Step action (classic steps)'),
11
+ expected_result: z.string().optional().describe('Expected result'),
12
+ data: z.string().optional().describe('Test data'),
13
+ value: z.string().optional().describe('Gherkin scenario text (when steps_type is "gherkin")'),
14
+ attachments: z.array(z.string()).optional().describe('Attachment hashes'),
15
+ };
16
+ export const TestStepSchema = z.object({
17
+ ...stepFields,
18
+ steps: z
19
+ .array(z.object(stepFields).passthrough())
20
+ .optional()
21
+ .describe('Nested substeps. Same structure as parent steps, supports further nesting.'),
22
+ });
23
+ export const CaseFieldsSchema = z.object({
24
+ title: z.string().min(1).max(255).describe('Test case title'),
25
+ description: z.string().optional(),
26
+ preconditions: z.string().optional(),
27
+ postconditions: z.string().optional(),
28
+ severity: z.string().optional().describe('Severity label or numeric ID'),
29
+ priority: z
30
+ .string()
31
+ .optional()
32
+ .describe('Priority label or numeric ID (0=not set, 1=high, 2=medium, 3=low)'),
33
+ type: z.string().optional().describe('Type label or numeric ID'),
34
+ layer: z.string().optional().describe('Layer label or numeric ID'),
35
+ behavior: z.string().optional().describe('Behavior label or numeric ID'),
36
+ automation: z
37
+ .string()
38
+ .optional()
39
+ .describe('Automation status (label, slug, or numeric ID: 0=Manual / is-not-automated, 1=To be automated, 2=Automated)'),
40
+ status: z.string().optional().describe('Status label or numeric ID'),
41
+ is_flaky: z.boolean().optional(),
42
+ suite_id: z.number().int().positive().optional(),
43
+ milestone_id: z.number().int().positive().optional(),
44
+ steps: z.array(TestStepSchema).optional(),
45
+ steps_type: z.enum(['classic', 'gherkin']).optional(),
46
+ tags: z.array(z.string()).optional(),
47
+ attachments: z.array(z.string()).optional(),
48
+ custom_field: z.record(z.any()).optional(),
49
+ });
50
+ /**
51
+ * Map the user-facing `automation` enum (0=Manual, 1=To be automated,
52
+ * 2=Automated) to the current API contract (`isManual` + `isToBeAutomated`).
53
+ * The legacy `automation` field is deprecated in qase-api-client and dropped
54
+ * from the outbound payload.
55
+ */
56
+ export function applyAutomationMapping(caseData) {
57
+ const automationId = caseData.automation;
58
+ if (typeof automationId !== 'number') {
59
+ return caseData;
60
+ }
61
+ const { automation: _drop, ...rest } = caseData;
62
+ const mapped = rest;
63
+ switch (automationId) {
64
+ case 2: // Automated
65
+ mapped.isManual = 0;
66
+ break;
67
+ case 1: // To be automated
68
+ mapped.isManual = 1;
69
+ mapped.isToBeAutomated = 1;
70
+ break;
71
+ case 0: // Manual
72
+ default:
73
+ mapped.isManual = 1;
74
+ mapped.isToBeAutomated = 0;
75
+ break;
76
+ }
77
+ return mapped;
78
+ }
79
+ //# sourceMappingURL=case-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"case-fields.js","sourceRoot":"","sources":["../../../src/operations-v2/write/case-fields.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cases-bulk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cases-bulk.d.ts","sourceRoot":"","sources":["../../../src/operations-v2/write/cases-bulk.ts"],"names":[],"mappings":""}
@@ -0,0 +1,54 @@
1
+ import { z } from 'zod';
2
+ import { getApiClient } from '../../client/index.js';
3
+ import { toolRegistry, CreateAnnotation } from '../../utils/registry.js';
4
+ import { toResultAsync, createToolError } from '../../utils/errors.js';
5
+ import { ProjectCodeSchema } from '../../utils/validation.js';
6
+ import { normalizeCaseEnums } from '../../utils/case-enums.js';
7
+ import { CaseFieldsSchema, applyAutomationMapping } from './case-fields.js';
8
+ const CONTEXT = 'bulk case creation';
9
+ /** Qase creates the whole batch in one transaction; 100 keeps the request sane. */
10
+ const MAX_CASES = 100;
11
+ const Schema = z.object({
12
+ code: ProjectCodeSchema,
13
+ cases: z
14
+ .array(CaseFieldsSchema)
15
+ .min(1)
16
+ .max(MAX_CASES, `A single call creates at most ${MAX_CASES} test cases — split larger batches.`)
17
+ .describe(`Test cases to create, 1 to ${MAX_CASES} per call`),
18
+ });
19
+ async function handler(rawArgs) {
20
+ // Tool handlers get raw MCP arguments — validate here so a malformed call
21
+ // returns a readable error instead of a TypeError from deeper in the code.
22
+ const parsed = Schema.safeParse(rawArgs);
23
+ if (!parsed.success) {
24
+ const details = parsed.error.issues
25
+ .map((issue) => `${issue.path.join('.') || 'arguments'}: ${issue.message}`)
26
+ .join('; ');
27
+ throw createToolError(`Invalid arguments — ${details}`, CONTEXT);
28
+ }
29
+ const { code, cases } = parsed.data;
30
+ // Same enum handling as qase_case_upsert, so "high" and "blocker" work here
31
+ // too. System fields are cached, so this does not fan out into N API calls.
32
+ const normalized = await Promise.all(cases.map(async (testCase) => applyAutomationMapping(await normalizeCaseEnums(testCase))));
33
+ const client = getApiClient();
34
+ const result = await toResultAsync(client.cases.bulk(code, { cases: normalized }));
35
+ return result.match((r) => {
36
+ const ids = r.data.result?.ids ?? [];
37
+ return { created: ids.length, ids };
38
+ }, (e) => {
39
+ throw createToolError(e, CONTEXT);
40
+ });
41
+ }
42
+ toolRegistry.register({
43
+ name: 'qase_case_bulk_create',
44
+ description: `Create up to ${MAX_CASES} test cases in a single request. Use this instead of calling ` +
45
+ '`qase_case_upsert` repeatedly when importing or generating several cases at once. ' +
46
+ 'Enum fields (priority, severity, type, etc.) accept both labels ("high", "blocker") and ' +
47
+ 'numeric IDs. Creates only — use `qase_case_upsert` with an `id` to update an existing case. ' +
48
+ 'Returns the IDs of the created cases in the order they were submitted.',
49
+ schema: Schema,
50
+ handler,
51
+ annotations: CreateAnnotation,
52
+ visibility: 'discoverable',
53
+ });
54
+ //# sourceMappingURL=cases-bulk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cases-bulk.js","sourceRoot":"","sources":["../../../src/operations-v2/write/cases-bulk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE5E,MAAM,OAAO,GAAG,oBAAoB,CAAC;AAErC,mFAAmF;AACnF,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,CAAC;SACL,KAAK,CAAC,gBAAgB,CAAC;SACvB,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,SAAS,EAAE,iCAAiC,SAAS,qCAAqC,CAAC;SAC/F,QAAQ,CAAC,8BAA8B,SAAS,WAAW,CAAC;CAChE,CAAC,CAAC;AAEH,KAAK,UAAU,OAAO,CAAC,OAAgB;IACrC,0EAA0E;IAC1E,2EAA2E;IAC3E,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAChC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,WAAW,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;aAC1E,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,eAAe,CAAC,uBAAuB,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IAEpC,4EAA4E;IAC5E,4EAA4E;IAC5E,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,sBAAsB,CAAC,MAAM,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAC1F,CAAC;IAEF,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,UAAU,EAAS,CAAC,CAAC,CAAC;IAE1F,OAAO,MAAM,CAAC,KAAK,CACjB,CAAC,CAAC,EAAE,EAAE;QACJ,MAAM,GAAG,GAAa,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC;QAC/C,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;IACtC,CAAC,EACD,CAAC,CAAC,EAAE,EAAE;QACJ,MAAM,eAAe,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACpC,CAAC,CACF,CAAC;AACJ,CAAC;AAED,YAAY,CAAC,QAAQ,CAAC;IACpB,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACT,gBAAgB,SAAS,+DAA+D;QACxF,oFAAoF;QACpF,0FAA0F;QAC1F,8FAA8F;QAC9F,wEAAwE;IAC1E,MAAM,EAAE,MAAM;IACd,OAAO;IACP,WAAW,EAAE,gBAAgB;IAC7B,UAAU,EAAE,cAAc;CAC3B,CAAC,CAAC"}
@@ -4,47 +4,7 @@ import { toolRegistry, CreateAnnotation, DeleteAnnotation } from '../../utils/re
4
4
  import { toResultAsync, createToolError } from '../../utils/errors.js';
5
5
  import { ProjectCodeSchema, IdSchema } from '../../utils/validation.js';
6
6
  import { normalizeCaseEnums } from '../../utils/case-enums.js';
7
- const stepFields = {
8
- action: z.string().optional().describe('Step action (classic steps)'),
9
- expected_result: z.string().optional().describe('Expected result'),
10
- data: z.string().optional().describe('Test data'),
11
- value: z.string().optional().describe('Gherkin scenario text (when steps_type is "gherkin")'),
12
- attachments: z.array(z.string()).optional().describe('Attachment hashes'),
13
- };
14
- const TestStepSchema = z.object({
15
- ...stepFields,
16
- steps: z
17
- .array(z.object(stepFields).passthrough())
18
- .optional()
19
- .describe('Nested substeps. Same structure as parent steps, supports further nesting.'),
20
- });
21
- const CaseFieldsSchema = z.object({
22
- title: z.string().min(1).max(255).describe('Test case title'),
23
- description: z.string().optional(),
24
- preconditions: z.string().optional(),
25
- postconditions: z.string().optional(),
26
- severity: z.string().optional().describe('Severity label or numeric ID'),
27
- priority: z
28
- .string()
29
- .optional()
30
- .describe('Priority label or numeric ID (0=not set, 1=high, 2=medium, 3=low)'),
31
- type: z.string().optional().describe('Type label or numeric ID'),
32
- layer: z.string().optional().describe('Layer label or numeric ID'),
33
- behavior: z.string().optional().describe('Behavior label or numeric ID'),
34
- automation: z
35
- .string()
36
- .optional()
37
- .describe('Automation status (label, slug, or numeric ID: 0=Manual / is-not-automated, 1=To be automated, 2=Automated)'),
38
- status: z.string().optional().describe('Status label or numeric ID'),
39
- is_flaky: z.boolean().optional(),
40
- suite_id: z.number().int().positive().optional(),
41
- milestone_id: z.number().int().positive().optional(),
42
- steps: z.array(TestStepSchema).optional(),
43
- steps_type: z.enum(['classic', 'gherkin']).optional(),
44
- tags: z.array(z.string()).optional(),
45
- attachments: z.array(z.string()).optional(),
46
- custom_field: z.record(z.any()).optional(),
47
- });
7
+ import { CaseFieldsSchema, applyAutomationMapping } from './case-fields.js';
48
8
  const UpsertSchema = z.object({
49
9
  code: ProjectCodeSchema,
50
10
  id: IdSchema.optional().describe('Case ID — if provided, updates the case; if omitted, creates a new one'),
@@ -54,35 +14,6 @@ const DeleteSchema = z.object({
54
14
  code: ProjectCodeSchema,
55
15
  id: IdSchema,
56
16
  });
57
- /**
58
- * Map the user-facing `automation` enum (0=Manual, 1=To be automated,
59
- * 2=Automated) to the current API contract (`isManual` + `isToBeAutomated`).
60
- * The legacy `automation` field is deprecated in qase-api-client and dropped
61
- * from the outbound payload.
62
- */
63
- function applyAutomationMapping(caseData) {
64
- const automationId = caseData.automation;
65
- if (typeof automationId !== 'number') {
66
- return caseData;
67
- }
68
- const { automation: _drop, ...rest } = caseData;
69
- const mapped = rest;
70
- switch (automationId) {
71
- case 2: // Automated
72
- mapped.isManual = 0;
73
- break;
74
- case 1: // To be automated
75
- mapped.isManual = 1;
76
- mapped.isToBeAutomated = 1;
77
- break;
78
- case 0: // Manual
79
- default:
80
- mapped.isManual = 1;
81
- mapped.isToBeAutomated = 0;
82
- break;
83
- }
84
- return mapped;
85
- }
86
17
  async function upsert(args) {
87
18
  const client = getApiClient();
88
19
  const { code, id, ...data } = args;
@@ -1 +1 @@
1
- {"version":3,"file":"cases.js","sourceRoot":"","sources":["../../../src/operations-v2/write/cases.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cases.js","sourceRoot":"","sources":["../../../src/operations-v2/write/cases.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE5E,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,IAAI,EAAE,iBAAiB;IACvB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAC9B,wEAAwE,CACzE;IACD,GAAG,gBAAgB,CAAC,KAAK;CAC1B,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,IAAI,EAAE,iBAAiB;IACvB,EAAE,EAAE,QAAQ;CACb,CAAC,CAAC;AAEH,KAAK,UAAU,MAAM,CAAC,IAAkC;IACtD,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACnC,MAAM,UAAU,GAAG,sBAAsB,CAAC,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IAE1E,MAAM,MAAM,GAAG,MAAM,aAAa,CAChC,EAAE;QACA,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,EAAE,UAAiB,CAAC;QACtD,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,UAAiB,CAAC,CACrD,CAAC;IAEF,OAAO,MAAM,CAAC,KAAK,CACjB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EACpB,CAAC,CAAC,EAAE,EAAE;QACJ,MAAM,eAAe,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAC7C,CAAC,CACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,GAAG,CAAC,IAAkC;IACnD,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAChF,OAAO,MAAM,CAAC,KAAK,CACjB,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,EACtC,CAAC,CAAC,EAAE,EAAE;QACJ,MAAM,eAAe,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAC7C,CAAC,CACF,CAAC;AACJ,CAAC;AAED,YAAY,CAAC,QAAQ,CAAC;IACpB,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,gFAAgF;QAChF,qFAAqF;QACrF,wFAAwF;IAC1F,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,MAAM;IACf,WAAW,EAAE,gBAAgB;CAC9B,CAAC,CAAC;AAEH,YAAY,CAAC,QAAQ,CAAC;IACpB,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,iDAAiD;IAC9D,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,GAAG;IACZ,WAAW,EAAE,gBAAgB;IAC7B,UAAU,EAAE,cAAc;CAC3B,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=external-issues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-issues.d.ts","sourceRoot":"","sources":["../../../src/operations-v2/write/external-issues.ts"],"names":[],"mappings":""}
@@ -0,0 +1,96 @@
1
+ import { z } from 'zod';
2
+ import { getApiClient } from '../../client/index.js';
3
+ import { toolRegistry, UpdateAnnotation } from '../../utils/registry.js';
4
+ import { toResultAsync, createToolError } from '../../utils/errors.js';
5
+ import { ProjectCodeSchema, IdSchema } from '../../utils/validation.js';
6
+ const CONTEXT = 'external issue link operation';
7
+ const LinkSchema = z.object({
8
+ id: IdSchema.describe('Test case ID (entity="case") or test run ID (entity="run")'),
9
+ issues: z
10
+ .array(z.string().min(1))
11
+ .optional()
12
+ .describe('External issue keys, e.g. ["PROJ-1234"]. Required for cases (attach and detach) and ' +
13
+ 'for attaching to a run — a run accepts exactly one issue. Omit when detaching a run.'),
14
+ });
15
+ const Schema = z.object({
16
+ code: ProjectCodeSchema,
17
+ entity: z.enum(['case', 'run']).describe('Entity to link — test case or test run'),
18
+ action: z
19
+ .enum(['attach', 'detach'])
20
+ .describe('attach creates the link, detach removes it (for runs, clears the single link)'),
21
+ type: z
22
+ .enum(['jira-cloud', 'jira-server'])
23
+ .describe('Issue tracker integration configured for the project'),
24
+ links: z.array(LinkSchema).min(1).describe('Entities to link, processed in a single API request'),
25
+ });
26
+ function requireIssues(link, entity) {
27
+ if (!link.issues?.length) {
28
+ throw createToolError(`No issues provided for ${entity} ${link.id} — "issues" must contain at least one external issue key (e.g. "PROJ-1234").`, CONTEXT);
29
+ }
30
+ return link.issues;
31
+ }
32
+ async function linkCases(args) {
33
+ const client = getApiClient();
34
+ const payload = {
35
+ type: args.type,
36
+ links: args.links.map((link) => ({
37
+ case_id: link.id,
38
+ external_issues: requireIssues(link, 'case'),
39
+ })),
40
+ };
41
+ const call = args.action === 'attach'
42
+ ? client.cases.caseAttachExternalIssue(args.code, payload)
43
+ : client.cases.caseDetachExternalIssue(args.code, payload);
44
+ return toResultAsync(call);
45
+ }
46
+ async function linkRuns(args) {
47
+ const client = getApiClient();
48
+ const payload = {
49
+ type: args.type,
50
+ links: args.links.map((link) => {
51
+ if (args.action === 'detach') {
52
+ return { run_id: link.id, external_issue: null };
53
+ }
54
+ const issues = requireIssues(link, 'run');
55
+ if (issues.length > 1) {
56
+ throw createToolError(`A test run can have only one external issue link — ${issues.length} issues provided for run ${link.id}.`, CONTEXT);
57
+ }
58
+ return { run_id: link.id, external_issue: issues[0] };
59
+ }),
60
+ };
61
+ return toResultAsync(client.runs.runUpdateExternalIssue(args.code, payload));
62
+ }
63
+ async function handler(rawArgs) {
64
+ // Tool handlers get raw MCP arguments — validate here so a malformed call
65
+ // returns a readable error instead of a TypeError from deeper in the code.
66
+ const parsed = Schema.safeParse(rawArgs);
67
+ if (!parsed.success) {
68
+ const details = parsed.error.issues
69
+ .map((issue) => `${issue.path.join('.') || 'arguments'}: ${issue.message}`)
70
+ .join('; ');
71
+ throw createToolError(`Invalid arguments — ${details}`, CONTEXT);
72
+ }
73
+ const args = parsed.data;
74
+ const result = args.entity === 'case' ? await linkCases(args) : await linkRuns(args);
75
+ return result.match(() => ({
76
+ success: true,
77
+ entity: args.entity,
78
+ action: args.action,
79
+ linked: args.links.length,
80
+ }), (e) => {
81
+ throw createToolError(e, CONTEXT);
82
+ });
83
+ }
84
+ toolRegistry.register({
85
+ name: 'qase_external_issue_link',
86
+ description: 'Link or unlink test cases and test runs to issues in an external tracker (Jira Cloud or ' +
87
+ 'Jira Server). Use `entity` to choose between cases and runs and `action` to attach or ' +
88
+ 'detach. A test case can be linked to several issues; a test run can have only one link, ' +
89
+ 'and attaching a new issue replaces the previous one. Read linked issues back with ' +
90
+ '`qase_get` (entity "case" or "run").',
91
+ schema: Schema,
92
+ handler,
93
+ annotations: UpdateAnnotation,
94
+ visibility: 'discoverable',
95
+ });
96
+ //# sourceMappingURL=external-issues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-issues.js","sourceRoot":"","sources":["../../../src/operations-v2/write/external-issues.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "2.0.0";
1
+ export declare const VERSION = "2.0.1";
2
2
  //# sourceMappingURL=version.d.ts.map
package/build/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by prebuild script — do not edit manually
2
- export const VERSION = '2.0.0';
2
+ export const VERSION = '2.0.1';
3
3
  //# sourceMappingURL=version.js.map
package/docs/migration.md CHANGED
@@ -41,7 +41,7 @@ All 83 v1 tools mapped to their v2 equivalents.
41
41
  | `create_case` | `qase_case_upsert` (omit `id`) | Pass all case fields; no `id` = create |
42
42
  | `update_case` | `qase_case_upsert` (include `id`) | Include `id` to update existing case |
43
43
  | `delete_case` | `qase_case_delete` | — |
44
- | `bulk_create_cases` | `qase_case_upsert` (call per case) | Call once per case without `id` |
44
+ | `bulk_create_cases` | `qase_case_bulk_create` | Same shape: `cases` array, up to 100 per call |
45
45
  | `attach_external_issue` | `qase_api` | Use escape hatch: `POST /case/{code}/{id}/external-issues` |
46
46
  | `detach_external_issue` | `qase_api` | Use escape hatch: `DELETE /case/{code}/{id}/external-issues` |
47
47
 
package/docs/tools.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # Tool Reference
2
2
 
3
- The Qase MCP Server exposes **30 tools** across 6 groups: Read, QQL, Write, Composite, Escape hatch, and Meta.
3
+ The Qase MCP Server exposes **32 tools** across 6 groups: Read, QQL, Write, Composite, Escape hatch, and Meta.
4
4
 
5
5
  ## Discovery model
6
6
 
7
7
  To keep context-token usage low, tools are split into two visibility tiers:
8
8
 
9
9
  - **`core`** — always listed to the MCP client, no activation needed (13 tools).
10
- - **`discoverable`** — hidden by default; the LLM finds and activates them on demand via `qase_discover_tools`, which searches tool names/descriptions and activates matches for the rest of the session (17 tools, mostly deletes and secondary write operations).
10
+ - **`discoverable`** — hidden by default; the LLM finds and activates them on demand via `qase_discover_tools`, which searches tool names/descriptions and activates matches for the rest of the session (19 tools, mostly deletes and secondary write operations).
11
11
 
12
12
  If a tool you need isn't showing up in your client's tool list, call `qase_discover_tools` with a query (e.g. `"delete"`, `"milestone"`, `"attachment"`) to activate it first.
13
13
 
@@ -17,7 +17,7 @@ Every tool's schema uses "label or numeric ID" strings for Qase's configurable e
17
17
 
18
18
  | Tool | Description | Key params | Visibility |
19
19
  | --- | --- | --- | --- |
20
- | `qase_get` | Get any Qase entity by type and ID. Supports field projection via `fields`. `code` is required for project-scoped entities (case, suite, run, result, plan, defect, milestone, environment, shared_step, shared_parameter, configuration); optional for global entities (user, author, attachment, custom_field). | `entity` (enum: case, suite, run, result, plan, defect, milestone, environment, shared_step, shared_parameter, configuration, attachment, author, user, custom_field), `code` (optional), `id` (number or hash string), `fields` (optional string array — pass `["*"]` for all) | core |
20
+ | `qase_get` | Get any Qase entity by type and ID. Supports field projection via `fields`. `code` is required for project-scoped entities (case, suite, run, result, plan, defect, milestone, environment, shared_step, shared_parameter, configuration); optional for global entities (user, author, attachment, custom_field). Cases and runs automatically request their external issue links (`external_issues` / `external_issue`); override with `include`. | `entity` (enum: case, suite, run, result, plan, defect, milestone, environment, shared_step, shared_parameter, configuration, attachment, author, user, custom_field), `code` (optional), `id` (number or hash string), `fields` (optional string array — pass `["*"]` for all), `include` (optional string) | core |
21
21
  | `qase_project_context` | Get full project context in one call: project details, suites tree, milestones, environments, custom fields, and users. Cached for 5 minutes. Recommended as the first call when starting work with a project. | `code` | core |
22
22
 
23
23
  ## QQL tools
@@ -30,11 +30,12 @@ Every tool's schema uses "label or numeric ID" strings for Qase's configurable e
30
30
  ## Write tools
31
31
 
32
32
  <details>
33
- <summary>Write tools (21)</summary>
33
+ <summary>Write tools (23)</summary>
34
34
 
35
35
  | Tool | Description | Key params | Visibility |
36
36
  | --- | --- | --- | --- |
37
37
  | `qase_case_upsert` | Create or update a test case. If `id` is provided, updates the existing case; if omitted, creates a new one. Enum fields (priority, severity, type, etc.) accept both labels ("high", "blocker") and numeric IDs — the server normalizes automatically. | `code`, `id` (optional), `title` (1-255 chars), `description`, `preconditions`, `postconditions`, `severity`, `priority`, `type`, `layer`, `behavior`, `automation`, `status` (all label-or-ID strings), `is_flaky`, `suite_id`, `milestone_id`, `steps` (array, supports nesting), `steps_type` (enum: classic, gherkin), `tags`, `attachments`, `custom_field` | core |
38
+ | `qase_case_bulk_create` | Create up to 100 test cases in a single request. Use instead of calling `qase_case_upsert` repeatedly when importing or generating several cases at once. Enum fields accept labels or numeric IDs. Creates only — use `qase_case_upsert` with an `id` to update. Returns the IDs of the created cases in submission order. | `code`, `cases` (array, 1-100 — same fields as `qase_case_upsert` without `id`) | discoverable |
38
39
  | `qase_case_delete` | Delete a test case by project code and case ID. | `code`, `id` | discoverable |
39
40
  | `qase_defect_upsert` | Create or update a defect. If `id` is provided, updates (including status changes and resolve). If omitted, creates a new defect. Set `status: "resolved"` to resolve an existing defect. | `code`, `id` (optional), `title` (1-255 chars), `actual_result`, `severity` (enum, see [below](#case-enum-values)), `status` (enum: open, in_progress, resolved, invalid), `tags`, `attachments`, `custom_field` | core |
40
41
  | `qase_defect_delete` | Delete a defect by project code and defect ID. | `code`, `id` | discoverable |
@@ -55,6 +56,7 @@ Every tool's schema uses "label or numeric ID" strings for Qase's configurable e
55
56
  | `qase_environment_delete` | Delete a test environment by project code and environment ID. | `code`, `id` | discoverable |
56
57
  | `qase_attachment_upload` | Upload a file attachment. Accepts the file as a base64 encoded string or an absolute path. Returns the attachment hash that can be referenced in test cases and results. | `code`, `file` (base64 string or absolute path), `filename` | discoverable |
57
58
  | `qase_attachment_delete` | Delete an attachment by its hash. | `hash` | discoverable |
59
+ | `qase_external_issue_link` | Link or unlink test cases and test runs to issues in an external tracker (Jira Cloud or Jira Server). A case can be linked to several issues; a run can have only one link, and attaching a new issue replaces the previous one. Detaching a run clears its link. Read linked issues back with `qase_get`. | `code`, `entity` (enum: case, run), `action` (enum: attach, detach), `type` (enum: jira-cloud, jira-server), `links` (array: `id`, `issues` — issue keys such as `PROJ-1234`) | discoverable |
58
60
 
59
61
  </details>
60
62
 
@@ -134,7 +134,7 @@ To find your certificate:
134
134
 
135
135
  **Solution**:
136
136
  1. Ensure you're using the latest version: `npm update -g @qase/mcp-server` (self-run) — the hosted connector always runs the latest version
137
- 2. If upgrading from v1, tool names have changed — see [MIGRATION.md](../MIGRATION.md)
137
+ 2. If upgrading from v1, tool names have changed — see [docs/migration.md](migration.md)
138
138
  3. Restart your MCP client after updating
139
139
 
140
140
  ## Still stuck?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qase/mcp-server",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "mcpName": "io.qase/mcp-server",
5
5
  "description": "Official MCP server for Qase Test Management Platform",
6
6
  "type": "module",
@@ -74,7 +74,7 @@
74
74
  "jose": "^6.1.3",
75
75
  "lru-cache": "^11.3.5",
76
76
  "neverthrow": "^8.2.0",
77
- "npm": "^12.0.1",
77
+ "npm": "^12.0.2",
78
78
  "qase-api-client": "^1.1.10",
79
79
  "zod": "^3.24.2",
80
80
  "zod-to-json-schema": "^3.24.3"
package/server.json CHANGED
@@ -8,12 +8,12 @@
8
8
  "url": "https://github.com/qase-tms/qase-mcp-server",
9
9
  "source": "github"
10
10
  },
11
- "version": "2.0.0",
11
+ "version": "2.0.1",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "@qase/mcp-server",
16
- "version": "2.0.0",
16
+ "version": "2.0.1",
17
17
  "transport": {
18
18
  "type": "stdio"
19
19
  },