zephyr-scale-mcp-server 0.2.4 → 0.2.5

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.
@@ -125,27 +125,70 @@ export class ZephyrToolHandlers {
125
125
  // First, get the existing test case data
126
126
  const getResponse = await this.axiosInstance.get(`${this.jiraConfig.apiEndpoints.testcase}/${test_case_key}`);
127
127
  const testCaseData = getResponse.data;
128
- // Prepare the payload with only the fields that need updating
129
- const payload = {
130
- testScript: testCaseData.testScript,
128
+ // Convert incoming BDD markdown (fallback to raw content if conversion returns empty)
129
+ const converted = convertToGherkin(bdd_content);
130
+ const finalText = converted && converted.trim().length > 0 ? converted : bdd_content;
131
+ // Build a payload aligned with Zephyr Scale Server's update schema.
132
+ // Only include fields that exist to avoid accidental nulling; required fields must be present.
133
+ const payload = {};
134
+ // Required base fields (schema requires these on Server/Data Center). If missing, throw.
135
+ const requiredFields = [
136
+ ['projectKey', testCaseData.projectKey],
137
+ ['name', testCaseData.name],
138
+ ['status', testCaseData.status],
139
+ ['priority', testCaseData.priority]
140
+ ];
141
+ for (const [field, value] of requiredFields) {
142
+ if (value === undefined || value === null || value === '') {
143
+ throw new McpError(ErrorCode.InternalError, `Existing test case is missing required field '${field}' needed for update.`);
144
+ }
145
+ payload[field] = value;
146
+ }
147
+ // Optional simple scalar/string fields
148
+ const optionalScalarFields = [
149
+ 'objective',
150
+ 'precondition',
151
+ 'folder',
152
+ 'component',
153
+ 'owner',
154
+ 'estimatedTime'
155
+ ];
156
+ for (const field of optionalScalarFields) {
157
+ if (testCaseData[field] !== undefined)
158
+ payload[field] = testCaseData[field];
159
+ }
160
+ // Arrays / objects
161
+ if (Array.isArray(testCaseData.labels))
162
+ payload.labels = testCaseData.labels;
163
+ if (testCaseData.customFields)
164
+ payload.customFields = testCaseData.customFields;
165
+ if (testCaseData.parameters)
166
+ payload.parameters = testCaseData.parameters;
167
+ // issueLinks preferred; map deprecated issueKey if present and issueLinks absent
168
+ if (Array.isArray(testCaseData.issueLinks)) {
169
+ payload.issueLinks = testCaseData.issueLinks;
170
+ }
171
+ else if (testCaseData.issueKey) {
172
+ payload.issueLinks = [testCaseData.issueKey];
173
+ }
174
+ // Build testScript. Force type to BDD when performing a BDD update.
175
+ payload.testScript = {
176
+ type: 'BDD',
177
+ text: finalText
131
178
  };
132
- // Update test script with new BDD content
133
- payload.testScript.text = convertToGherkin(bdd_content);
134
- // Update the test case
179
+ // PUT update
135
180
  const updateResponse = await this.axiosInstance.put(`${this.jiraConfig.apiEndpoints.testcase}/${test_case_key}`, payload);
136
181
  if (updateResponse.status === 200) {
137
182
  return {
138
183
  content: [
139
184
  {
140
185
  type: 'text',
141
- text: `✅ Updated ${test_case_key} with BDD content successfully`,
186
+ text: `✅ Updated ${test_case_key} with BDD content successfully\nPayload summary: ${JSON.stringify({ textLength: finalText.length, projectKey: payload.projectKey, preservedLabels: payload.labels?.length || 0 }, null, 2)}`,
142
187
  },
143
188
  ],
144
189
  };
145
190
  }
146
- else {
147
- throw new Error(`Failed to update ${test_case_key}: ${updateResponse.status}`);
148
- }
191
+ throw new Error(`Failed to update ${test_case_key}: ${updateResponse.status}`);
149
192
  }
150
193
  catch (error) {
151
194
  throw new McpError(ErrorCode.InternalError, `Failed to update test case BDD: ${error instanceof Error ? error.message : String(error)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zephyr-scale-mcp-server",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
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",
@@ -141,16 +141,60 @@ export class ZephyrToolHandlers {
141
141
  // First, get the existing test case data
142
142
  const getResponse = await this.axiosInstance.get(`${this.jiraConfig.apiEndpoints.testcase}/${test_case_key}`);
143
143
  const testCaseData = getResponse.data;
144
+ // Convert incoming BDD markdown (fallback to raw content if conversion returns empty)
145
+ const converted = convertToGherkin(bdd_content);
146
+ const finalText = converted && converted.trim().length > 0 ? converted : bdd_content;
147
+
148
+ // Build a payload aligned with Zephyr Scale Server's update schema.
149
+ // Only include fields that exist to avoid accidental nulling; required fields must be present.
150
+ const payload: any = {};
151
+
152
+ // Required base fields (schema requires these on Server/Data Center). If missing, throw.
153
+ const requiredFields: Array<[string, any]> = [
154
+ ['projectKey', testCaseData.projectKey],
155
+ ['name', testCaseData.name],
156
+ ['status', testCaseData.status],
157
+ ['priority', testCaseData.priority]
158
+ ];
159
+
160
+ for (const [field, value] of requiredFields) {
161
+ if (value === undefined || value === null || value === '') {
162
+ throw new McpError(ErrorCode.InternalError, `Existing test case is missing required field '${field}' needed for update.`);
163
+ }
164
+ payload[field] = value;
165
+ }
144
166
 
145
- // Prepare the payload with only the fields that need updating
146
- const payload = {
147
- testScript: testCaseData.testScript,
148
- };
167
+ // Optional simple scalar/string fields
168
+ const optionalScalarFields = [
169
+ 'objective',
170
+ 'precondition',
171
+ 'folder',
172
+ 'component',
173
+ 'owner',
174
+ 'estimatedTime'
175
+ ];
176
+ for (const field of optionalScalarFields) {
177
+ if (testCaseData[field] !== undefined) payload[field] = testCaseData[field];
178
+ }
179
+
180
+ // Arrays / objects
181
+ if (Array.isArray(testCaseData.labels)) payload.labels = testCaseData.labels;
182
+ if (testCaseData.customFields) payload.customFields = testCaseData.customFields;
183
+ if (testCaseData.parameters) payload.parameters = testCaseData.parameters;
184
+ // issueLinks preferred; map deprecated issueKey if present and issueLinks absent
185
+ if (Array.isArray(testCaseData.issueLinks)) {
186
+ payload.issueLinks = testCaseData.issueLinks;
187
+ } else if (testCaseData.issueKey) {
188
+ payload.issueLinks = [testCaseData.issueKey];
189
+ }
149
190
 
150
- // Update test script with new BDD content
151
- payload.testScript.text = convertToGherkin(bdd_content);
191
+ // Build testScript. Force type to BDD when performing a BDD update.
192
+ payload.testScript = {
193
+ type: 'BDD',
194
+ text: finalText
195
+ };
152
196
 
153
- // Update the test case
197
+ // PUT update
154
198
  const updateResponse = await this.axiosInstance.put(`${this.jiraConfig.apiEndpoints.testcase}/${test_case_key}`, payload);
155
199
 
156
200
  if (updateResponse.status === 200) {
@@ -158,13 +202,13 @@ export class ZephyrToolHandlers {
158
202
  content: [
159
203
  {
160
204
  type: 'text',
161
- text: `✅ Updated ${test_case_key} with BDD content successfully`,
205
+ text: `✅ Updated ${test_case_key} with BDD content successfully\nPayload summary: ${JSON.stringify({ textLength: finalText.length, projectKey: payload.projectKey, preservedLabels: payload.labels?.length || 0 }, null, 2)}`,
162
206
  },
163
207
  ],
164
208
  };
165
- } else {
166
- throw new Error(`Failed to update ${test_case_key}: ${updateResponse.status}`);
167
209
  }
210
+
211
+ throw new Error(`Failed to update ${test_case_key}: ${updateResponse.status}`);
168
212
  } catch (error) {
169
213
  throw new McpError(
170
214
  ErrorCode.InternalError,