zephyr-scale-mcp-server 0.4.1 → 0.4.2

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.
@@ -34,7 +34,7 @@ export class ZephyrToolHandlers {
34
34
  return this.createTestCaseDC(args);
35
35
  }
36
36
  async createTestCaseCloud(args) {
37
- const { project_key, name, test_script, folder, priority, precondition, objective, estimated_time, labels, custom_fields, } = args;
37
+ const { project_key, name, test_script, folder, priority, precondition, objective, estimated_time, labels, custom_fields, issue_links, } = args;
38
38
  const payload = { projectKey: project_key, name };
39
39
  // Cloud v2 uses statusName/priorityName (strings), folderId (integer)
40
40
  payload.statusName = 'Draft';
@@ -66,10 +66,27 @@ export class ZephyrToolHandlers {
66
66
  if (test_script) {
67
67
  await this.upsertTestScriptCloud(testKey, test_script);
68
68
  }
69
+ // Step 3: link Jira issues via POST /testcases/{key}/links/issues
70
+ // IssueLinkInput requires a numeric issueId — resolve each key via Jira REST API
71
+ const linkWarnings = [];
72
+ if (issue_links && issue_links.length > 0) {
73
+ for (const issueKey of issue_links) {
74
+ try {
75
+ const issueId = await this.resolveJiraIssueId(issueKey);
76
+ await this.axiosInstance.post(`${this.jiraConfig.apiEndpoints.testcase}/${testKey}/links/issues`, { issueId });
77
+ }
78
+ catch (e) {
79
+ linkWarnings.push(`${issueKey}: ${this.formatError(e)}`);
80
+ }
81
+ }
82
+ }
83
+ const warningText = linkWarnings.length > 0
84
+ ? `\n⚠️ Some issue links failed:\n${linkWarnings.map(w => ` - ${w}`).join('\n')}`
85
+ : '';
69
86
  return {
70
87
  content: [{
71
88
  type: 'text',
72
- text: `✅ Test case created successfully: ${testKey}\n${JSON.stringify({ key: testKey, type: test_script?.type || 'none' }, null, 2)}`,
89
+ text: `✅ Test case created successfully: ${testKey}\n${JSON.stringify({ key: testKey, type: test_script?.type || 'none', linkedIssues: (issue_links ?? []).length - linkWarnings.length }, null, 2)}${warningText}`,
73
90
  }],
74
91
  };
75
92
  }
@@ -751,6 +768,18 @@ export class ZephyrToolHandlers {
751
768
  throw new McpError(ErrorCode.InternalError, `Failed to add test cases: ${this.formatError(error)}`);
752
769
  }
753
770
  }
771
+ async resolveJiraIssueId(issueKey) {
772
+ // The Zephyr API key is a Jira-issued token — it works against the Jira REST API too.
773
+ // We call GET {jiraBaseUrl}/rest/api/3/issue/{key}?fields=id to get the numeric issue ID
774
+ // required by POST /testcases/{key}/links/issues { issueId: <integer> }.
775
+ const url = `${this.jiraConfig.jiraBaseUrl}/rest/api/3/issue/${issueKey}?fields=id`;
776
+ const response = await this.axiosInstance.get(url, { baseURL: '' });
777
+ const id = parseInt(response.data.id, 10);
778
+ if (!id || isNaN(id)) {
779
+ throw new Error(`Could not resolve numeric ID for Jira issue "${issueKey}"`);
780
+ }
781
+ return id;
782
+ }
754
783
  formatError(error) {
755
784
  if (error instanceof Error && 'response' in error) {
756
785
  const axiosError = error;
@@ -110,7 +110,7 @@ export const toolSchemas = [
110
110
  },
111
111
  issue_links: {
112
112
  type: 'array',
113
- description: 'Array of issue links (optional) - will be mapped to issueLinks in API',
113
+ description: 'Array of Jira issue keys to link to this test case (e.g. ["PROJ-123", "PROJ-456"]). On Cloud, each key is resolved to a numeric Jira issue ID via the Jira REST API, then linked via POST /testcases/{key}/links/issues — failures are reported as warnings but do not fail the tool call. On Data Center, sent directly in the create payload.',
114
114
  items: { type: 'string' }
115
115
  },
116
116
  custom_fields: {
package/build/utils.js CHANGED
@@ -145,6 +145,7 @@ export function createJiraConfig() {
145
145
  return {
146
146
  type,
147
147
  baseUrl,
148
+ jiraBaseUrl,
148
149
  authHeaders,
149
150
  apiEndpoints,
150
151
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zephyr-scale-mcp-server",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Model Context Protocol (MCP) server for Zephyr Scale test case management with comprehensive STEP_BY_STEP, PLAIN_TEXT, and BDD support",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",
@@ -46,7 +46,7 @@ export class ZephyrToolHandlers {
46
46
  private async createTestCaseCloud(args: TestCaseArgs) {
47
47
  const {
48
48
  project_key, name, test_script, folder, priority, precondition,
49
- objective, estimated_time, labels, custom_fields,
49
+ objective, estimated_time, labels, custom_fields, issue_links,
50
50
  } = args;
51
51
 
52
52
  const payload: any = { projectKey: project_key, name };
@@ -79,10 +79,31 @@ export class ZephyrToolHandlers {
79
79
  await this.upsertTestScriptCloud(testKey, test_script);
80
80
  }
81
81
 
82
+ // Step 3: link Jira issues via POST /testcases/{key}/links/issues
83
+ // IssueLinkInput requires a numeric issueId — resolve each key via Jira REST API
84
+ const linkWarnings: string[] = [];
85
+ if (issue_links && issue_links.length > 0) {
86
+ for (const issueKey of issue_links) {
87
+ try {
88
+ const issueId = await this.resolveJiraIssueId(issueKey);
89
+ await this.axiosInstance.post(
90
+ `${this.jiraConfig.apiEndpoints.testcase}/${testKey}/links/issues`,
91
+ { issueId }
92
+ );
93
+ } catch (e) {
94
+ linkWarnings.push(`${issueKey}: ${this.formatError(e)}`);
95
+ }
96
+ }
97
+ }
98
+
99
+ const warningText = linkWarnings.length > 0
100
+ ? `\n⚠️ Some issue links failed:\n${linkWarnings.map(w => ` - ${w}`).join('\n')}`
101
+ : '';
102
+
82
103
  return {
83
104
  content: [{
84
105
  type: 'text',
85
- text: `✅ Test case created successfully: ${testKey}\n${JSON.stringify({ key: testKey, type: test_script?.type || 'none' }, null, 2)}`,
106
+ text: `✅ Test case created successfully: ${testKey}\n${JSON.stringify({ key: testKey, type: test_script?.type || 'none', linkedIssues: (issue_links ?? []).length - linkWarnings.length }, null, 2)}${warningText}`,
86
107
  }],
87
108
  };
88
109
  } catch (error) {
@@ -824,6 +845,19 @@ export class ZephyrToolHandlers {
824
845
  }
825
846
  }
826
847
 
848
+ private async resolveJiraIssueId(issueKey: string): Promise<number> {
849
+ // The Zephyr API key is a Jira-issued token — it works against the Jira REST API too.
850
+ // We call GET {jiraBaseUrl}/rest/api/3/issue/{key}?fields=id to get the numeric issue ID
851
+ // required by POST /testcases/{key}/links/issues { issueId: <integer> }.
852
+ const url = `${this.jiraConfig.jiraBaseUrl}/rest/api/3/issue/${issueKey}?fields=id`;
853
+ const response = await this.axiosInstance.get(url, { baseURL: '' });
854
+ const id = parseInt(response.data.id, 10);
855
+ if (!id || isNaN(id)) {
856
+ throw new Error(`Could not resolve numeric ID for Jira issue "${issueKey}"`);
857
+ }
858
+ return id;
859
+ }
860
+
827
861
  private formatError(error: unknown): string {
828
862
  if (error instanceof Error && 'response' in error) {
829
863
  const axiosError = error as any;
@@ -110,7 +110,7 @@ export const toolSchemas = [
110
110
  },
111
111
  issue_links: {
112
112
  type: 'array',
113
- description: 'Array of issue links (optional) - will be mapped to issueLinks in API',
113
+ description: 'Array of Jira issue keys to link to this test case (e.g. ["PROJ-123", "PROJ-456"]). On Cloud, each key is resolved to a numeric Jira issue ID via the Jira REST API, then linked via POST /testcases/{key}/links/issues — failures are reported as warnings but do not fail the tool call. On Data Center, sent directly in the create payload.',
114
114
  items: { type: 'string' }
115
115
  },
116
116
  custom_fields: {
package/src/types.ts CHANGED
@@ -104,6 +104,7 @@ export interface ApiEndpoints {
104
104
  export interface JiraConfig {
105
105
  type: JiraType;
106
106
  baseUrl: string;
107
+ jiraBaseUrl: string;
107
108
  authHeaders: Record<string, string>;
108
109
  apiEndpoints: ApiEndpoints;
109
110
  }
package/src/utils.ts CHANGED
@@ -170,6 +170,7 @@ export function createJiraConfig() {
170
170
  return {
171
171
  type,
172
172
  baseUrl,
173
+ jiraBaseUrl,
173
174
  authHeaders,
174
175
  apiEndpoints,
175
176
  };