targetprocess-mcp-server 2.5.2 → 2.5.4
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/README.md +14 -1
- package/build/handlers/add_test_case_step_by_id.js +17 -0
- package/build/handlers/delete_test_case_step_by_id.js +19 -0
- package/build/handlers/get_bug_workflows.js +33 -0
- package/build/handlers/get_process_workflows.js +23 -0
- package/build/handlers/get_processes.js +23 -0
- package/build/handlers/get_relation_types.js +12 -0
- package/build/handlers/get_test_case_by_id.js +37 -0
- package/build/handlers/get_test_plan_by_id.js +36 -0
- package/build/handlers/get_test_plan_test_cases_by_id.js +43 -0
- package/build/handlers/get_test_plan_test_cases_with_steps_by_id.js +49 -0
- package/build/handlers/get_user_story_workflows.js +33 -0
- package/build/handlers/get_version.js +5 -0
- package/build/handlers/update_test_case_by_id.js +22 -0
- package/build/handlers/update_test_case_step_by_id.js +35 -0
- package/build/index.js +150 -173
- package/build/tp.js +153 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,6 +109,14 @@ Cards — Write
|
|
|
109
109
|
Test Case Workflows
|
|
110
110
|
- `write_test_cases` — Fetch a card (UserStory, Bug, or Feature) by ID and trigger the full test case writing workflow: Claude analyzes the card, generates detailed test cases covering happy path, edge cases, and error scenarios, creates a linked test plan via `create_test_plan`, then calls `add_test_cases_to_test_plan`. Each test case description contains Preconditions and Test Type as HTML; steps are passed as a structured array (resourceId, optional resourceType)
|
|
111
111
|
- `add_test_cases_to_test_plan` — Add pre-generated test cases to an existing test plan. Each test case has a `name`, an HTML `description` (Preconditions and Test Type only), and a `steps` array of `{ description, result }` objects — steps are created via the TP test step API rather than embedded in the description (testPlanId, testCases array of {name, description, steps})
|
|
112
|
+
- `get_test_plan_by_id` — Get a test plan by ID, including name, plain-text description, state, project, and linked card (id)
|
|
113
|
+
- `get_test_plan_test_cases_by_id` — Get test cases for a test plan by ID, including cases from nested child test plans/containers; returns id, name, plain-text description, and containing test plan metadata, no steps (id)
|
|
114
|
+
- `get_test_plan_test_cases_with_steps_by_id` — Get test cases directly on a test plan by ID, each with its steps (id)
|
|
115
|
+
- `get_test_case_by_id` — Get a single test case by ID, including plain-text description and its steps (id)
|
|
116
|
+
- `update_test_case_by_id` — Update a test case's name and/or description (id, optional name, optional description)
|
|
117
|
+
- `add_test_case_step_by_id` — Add a new step to a test case (testCaseId, description, result)
|
|
118
|
+
- `update_test_case_step_by_id` — Update a test step's description and/or result (id, optional description, optional result)
|
|
119
|
+
- `delete_test_case_step_by_id` — Delete a test step by ID (id)
|
|
112
120
|
|
|
113
121
|
Processes & Workflows
|
|
114
122
|
- `get_processes` — Get all Targetprocess processes (no params needed)
|
|
@@ -219,7 +227,7 @@ npx vitest # watch mode
|
|
|
219
227
|
|
|
220
228
|
### Coverage
|
|
221
229
|
|
|
222
|
-
**
|
|
230
|
+
**53 of 64 tools (83%) are covered by unit tests.**
|
|
223
231
|
|
|
224
232
|
| Test file | Handlers covered |
|
|
225
233
|
|---|---|
|
|
@@ -236,3 +244,8 @@ npx vitest # watch mode
|
|
|
236
244
|
| `creation_tools.test.ts` | `create_bug`, `create_user_story`, `create_feature`, `create_task`, `update_bug`, `update_user_story_state` |
|
|
237
245
|
| `my_work_tools.test.ts` | `get_in_progress_tasks_and_bugs`, `list_my_user_stories`, `list_my_bugs`, `log_time`, `get_my_time_logs` |
|
|
238
246
|
| `entity_tools.test.ts` | `get_feature_user_stories`, `get_user_story_bugs`, `get_card_current_status` |
|
|
247
|
+
| `relation_tools.test.ts` | `get_card_relations`, `create_card_relation`, `delete_card_relation` |
|
|
248
|
+
| `epic_tools.test.ts` | `create_epic`, `get_epic_content`, `update_epic`, `get_epic_features` |
|
|
249
|
+
| `test_plan_tools.test.ts` | `get_test_plan_by_id`, `get_test_plan_test_cases_by_id`, `get_test_plan_test_cases_with_steps_by_id` |
|
|
250
|
+
| `test_case_tools.test.ts` | `get_test_case_by_id`, `update_test_case_by_id`, `add_test_case_step_by_id`, `update_test_case_step_by_id`, `delete_test_case_step_by_id` |
|
|
251
|
+
| `workflow_tools.test.ts` | `get_processes`, `get_process_workflows`, `get_bug_workflows`, `get_user_story_workflows`, `get_relation_types`, `get_version` |
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export async function handleAddTestCaseStepById(tp, params) {
|
|
2
|
+
const testStepResponse = await tp.addTestStep(params.testCaseId, {
|
|
3
|
+
description: params.description,
|
|
4
|
+
result: params.result,
|
|
5
|
+
});
|
|
6
|
+
if (!testStepResponse) {
|
|
7
|
+
return {
|
|
8
|
+
content: [{
|
|
9
|
+
type: 'text',
|
|
10
|
+
text: `Failed to add test step to test case id: ${params.testCaseId}\n JSON: ${JSON.stringify(testStepResponse, null, 2)}`
|
|
11
|
+
}],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
content: [{ type: 'text', text: JSON.stringify(testStepResponse) }],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export async function handleDeleteTestCaseStepById(tp, id) {
|
|
2
|
+
const result = await tp.deleteTestStep(id);
|
|
3
|
+
if (!result.ok) {
|
|
4
|
+
return {
|
|
5
|
+
content: [{
|
|
6
|
+
type: 'text',
|
|
7
|
+
text: `Failed to delete test step id: ${id}\n` +
|
|
8
|
+
`HTTP status: ${result.status}\n` +
|
|
9
|
+
`Response body: ${result.body}`
|
|
10
|
+
}],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
content: [{
|
|
15
|
+
type: 'text',
|
|
16
|
+
text: JSON.stringify({ deleted: true, testStepId: Number(id), testStep: result.data })
|
|
17
|
+
}],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export async function handleGetBugWorkflows(tp) {
|
|
2
|
+
const response = await tp.getBugWorkflows();
|
|
3
|
+
if (!response) {
|
|
4
|
+
return {
|
|
5
|
+
content: [{
|
|
6
|
+
type: 'text',
|
|
7
|
+
text: `Failed to get bug entity statuses, JSON: ${JSON.stringify(response, null, 2)}`
|
|
8
|
+
}],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const items = response.items || [];
|
|
12
|
+
if (items.length === 0) {
|
|
13
|
+
return {
|
|
14
|
+
content: [{
|
|
15
|
+
type: 'text',
|
|
16
|
+
text: `No status data found for workflows`
|
|
17
|
+
}],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const workflows = items.map((w) => ({
|
|
21
|
+
id: w.id,
|
|
22
|
+
name: w.name,
|
|
23
|
+
processId: w.process,
|
|
24
|
+
entityType: w.entityType,
|
|
25
|
+
entityStates: w.entityStates.map((es) => ({
|
|
26
|
+
id: es.id,
|
|
27
|
+
name: es.name,
|
|
28
|
+
})),
|
|
29
|
+
}));
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: 'text', text: JSON.stringify(workflows) }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export async function handleGetProcessWorkflows(tp, processId) {
|
|
2
|
+
const response = await tp.getProcessWorkflows({ processId });
|
|
3
|
+
if (!response) {
|
|
4
|
+
return {
|
|
5
|
+
content: [{
|
|
6
|
+
type: 'text',
|
|
7
|
+
text: `Failed to get process workflows, JSON: ${JSON.stringify(response, null, 2)}`
|
|
8
|
+
}],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const items = response.items || [];
|
|
12
|
+
if (items.length === 0) {
|
|
13
|
+
return {
|
|
14
|
+
content: [{
|
|
15
|
+
type: 'text',
|
|
16
|
+
text: `No process workflows found`,
|
|
17
|
+
}],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: 'text', text: JSON.stringify(items) }],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export async function handleGetProcesses(tp) {
|
|
2
|
+
const response = await tp.getProcesses();
|
|
3
|
+
if (!response) {
|
|
4
|
+
return {
|
|
5
|
+
content: [{
|
|
6
|
+
type: 'text',
|
|
7
|
+
text: `Failed to get processes, JSON: ${JSON.stringify(response, null, 2)}`
|
|
8
|
+
}],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const items = response.items || [];
|
|
12
|
+
if (items.length === 0) {
|
|
13
|
+
return {
|
|
14
|
+
content: [{
|
|
15
|
+
type: 'text',
|
|
16
|
+
text: `No processes found`,
|
|
17
|
+
}],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: 'text', text: JSON.stringify(items) }],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export async function handleGetRelationTypes(tp) {
|
|
2
|
+
const response = await tp.getRelationTypes();
|
|
3
|
+
if (!response) {
|
|
4
|
+
return {
|
|
5
|
+
content: [{ type: 'text', text: `Failed to get relation types` }],
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
const items = (response.Items || []).map((t) => ({ id: t.Id, name: t.Name }));
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: 'text', text: JSON.stringify(items) }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { JSDOM } from 'jsdom';
|
|
2
|
+
export async function handleGetTestCaseById(tp, id) {
|
|
3
|
+
const testCase = await tp.getTestCase(id);
|
|
4
|
+
if (!testCase) {
|
|
5
|
+
return {
|
|
6
|
+
content: [{
|
|
7
|
+
type: 'text',
|
|
8
|
+
text: `Failed to get test case id: ${id}\n JSON: ${JSON.stringify(testCase, null, 2)}`
|
|
9
|
+
}],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
let description = '';
|
|
13
|
+
if (testCase.Description) {
|
|
14
|
+
try {
|
|
15
|
+
const dom = new JSDOM(`<html><body><div id="content">${testCase.Description}</div></body></html>`);
|
|
16
|
+
description = dom.window.document.getElementById('content')?.textContent || '';
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.error('Error parsing test case description:', error);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const testCaseSteps = await tp.getTestCaseSteps(String(testCase.Id));
|
|
23
|
+
const result = {
|
|
24
|
+
id: testCase.Id,
|
|
25
|
+
name: testCase.Name,
|
|
26
|
+
description,
|
|
27
|
+
testPlan: testCase.TestPlans?.Items?.[0]?.Name ?? testCase.LinkedTestPlan?.Name,
|
|
28
|
+
steps: testCaseSteps?.Items?.map((step) => ({
|
|
29
|
+
description: step.Description,
|
|
30
|
+
result: step.Result,
|
|
31
|
+
runOrder: step.RunOrder,
|
|
32
|
+
})) || [],
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { JSDOM } from 'jsdom';
|
|
2
|
+
export async function handleGetTestPlanById(tp, id) {
|
|
3
|
+
const testPlan = await tp.getTestPlan(id);
|
|
4
|
+
if (!testPlan) {
|
|
5
|
+
return {
|
|
6
|
+
content: [{
|
|
7
|
+
type: "text",
|
|
8
|
+
text: `Failed to get test plan id: ${id}\n JSON: ${JSON.stringify(testPlan, null, 2)}`
|
|
9
|
+
}],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
let description = '';
|
|
13
|
+
if (testPlan.Description) {
|
|
14
|
+
try {
|
|
15
|
+
const dom = new JSDOM(`<html><body><div id="content">${testPlan.Description}</div></body></html>`);
|
|
16
|
+
description = dom.window.document.getElementById('content')?.textContent || '';
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.error('Error parsing test plan description:', error);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const result = {
|
|
23
|
+
id: testPlan.Id,
|
|
24
|
+
name: testPlan.Name,
|
|
25
|
+
description,
|
|
26
|
+
entityState: testPlan.EntityState?.Name,
|
|
27
|
+
project: testPlan.Project?.Name,
|
|
28
|
+
linkedUserStory: testPlan.LinkedUserStory?.Name,
|
|
29
|
+
linkedAssignable: testPlan.LinkedAssignable?.Name,
|
|
30
|
+
createDate: testPlan.CreateDate,
|
|
31
|
+
modifyDate: testPlan.ModifyDate,
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { JSDOM } from 'jsdom';
|
|
2
|
+
export async function handleGetTestPlanTestCasesById(tp, id) {
|
|
3
|
+
const response = await tp.getIndirectTestPlanTestCases(id);
|
|
4
|
+
if (!response) {
|
|
5
|
+
return {
|
|
6
|
+
content: [{
|
|
7
|
+
type: 'text',
|
|
8
|
+
text: `Failed to get test cases for test plan id: ${id}\n JSON: ${JSON.stringify(response, null, 2)}`
|
|
9
|
+
}],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const items = response.Items || [];
|
|
13
|
+
if (items.length === 0) {
|
|
14
|
+
return {
|
|
15
|
+
content: [{
|
|
16
|
+
type: 'text',
|
|
17
|
+
text: `No test cases found for test plan id: ${id}`,
|
|
18
|
+
}],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const testCases = items.map((item) => {
|
|
22
|
+
let description = '';
|
|
23
|
+
if (item.Description) {
|
|
24
|
+
try {
|
|
25
|
+
const dom = new JSDOM(`<html><body><div id="content">${item.Description}</div></body></html>`);
|
|
26
|
+
description = dom.window.document.getElementById('content')?.textContent || '';
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error('Error parsing test case description:', error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
id: item.Id,
|
|
34
|
+
name: item.Name,
|
|
35
|
+
description,
|
|
36
|
+
testPlanId: item.TestPlanId,
|
|
37
|
+
testPlanName: item.TestPlanName,
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
content: [{ type: 'text', text: JSON.stringify(testCases) }],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { JSDOM } from 'jsdom';
|
|
2
|
+
export async function handleGetTestPlanTestCasesWithStepsById(tp, id) {
|
|
3
|
+
const response = await tp.getTestPlanTestCases(id);
|
|
4
|
+
if (!response) {
|
|
5
|
+
return {
|
|
6
|
+
content: [{
|
|
7
|
+
type: 'text',
|
|
8
|
+
text: `Failed to get test cases for test plan id: ${id}\n JSON: ${JSON.stringify(response, null, 2)}`
|
|
9
|
+
}],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const items = response.Items || [];
|
|
13
|
+
if (items.length === 0) {
|
|
14
|
+
return {
|
|
15
|
+
content: [{
|
|
16
|
+
type: 'text',
|
|
17
|
+
text: `No test cases found for test plan id: ${id}`,
|
|
18
|
+
}],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const testCasesData = await Promise.all(items.map(async (item) => {
|
|
22
|
+
let description = '';
|
|
23
|
+
if (item.Description) {
|
|
24
|
+
try {
|
|
25
|
+
const dom = new JSDOM(`<html><body><div id="content">${item.Description}</div></body></html>`);
|
|
26
|
+
description = dom.window.document.getElementById('content')?.textContent || '';
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error('Error parsing test case description:', error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const testCaseSteps = await tp.getTestCaseSteps(String(item.Id));
|
|
33
|
+
return {
|
|
34
|
+
testCaseId: item.Id,
|
|
35
|
+
testCaseName: item.Name,
|
|
36
|
+
testCaseDescription: description,
|
|
37
|
+
testPlanId: item.TestPlanId,
|
|
38
|
+
testPlanName: item.TestPlanName,
|
|
39
|
+
testCaseSteps: testCaseSteps?.Items?.map((step) => ({
|
|
40
|
+
description: step.Description,
|
|
41
|
+
result: step.Result,
|
|
42
|
+
runOrder: step.RunOrder,
|
|
43
|
+
})) || [],
|
|
44
|
+
};
|
|
45
|
+
}));
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: 'text', text: JSON.stringify(testCasesData) }],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export async function handleGetUserStoryWorkflows(tp) {
|
|
2
|
+
const response = await tp.getUserStoryWorkflowsWithSubStates();
|
|
3
|
+
if (!response) {
|
|
4
|
+
return {
|
|
5
|
+
content: [{
|
|
6
|
+
type: 'text',
|
|
7
|
+
text: `Failed to get user story entity statuses, JSON: ${JSON.stringify(response, null, 2)}`
|
|
8
|
+
}],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const items = response.items || [];
|
|
12
|
+
if (items.length === 0) {
|
|
13
|
+
return {
|
|
14
|
+
content: [{
|
|
15
|
+
type: 'text',
|
|
16
|
+
text: `No status data found for workflows`
|
|
17
|
+
}],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const userStoryWorkflows = items.filter((w) => w.entityType.name === "UserStory");
|
|
21
|
+
const workflows = userStoryWorkflows.map((w) => ({
|
|
22
|
+
id: w.id,
|
|
23
|
+
processId: w.workflow.process.id,
|
|
24
|
+
entityType: w.entityType.name,
|
|
25
|
+
entityStates: w.subEntityStates.map((es) => ({
|
|
26
|
+
id: es.id,
|
|
27
|
+
name: es.name,
|
|
28
|
+
})),
|
|
29
|
+
}));
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: 'text', text: JSON.stringify(workflows) }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export async function handleUpdateTestCaseById(tp, params) {
|
|
2
|
+
if (params.name === undefined && params.description === undefined) {
|
|
3
|
+
return {
|
|
4
|
+
content: [{
|
|
5
|
+
type: 'text',
|
|
6
|
+
text: `Nothing to update for test case id: ${params.id}`
|
|
7
|
+
}],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
const testCaseResponse = await tp.updateTestCase(params);
|
|
11
|
+
if (!testCaseResponse) {
|
|
12
|
+
return {
|
|
13
|
+
content: [{
|
|
14
|
+
type: 'text',
|
|
15
|
+
text: `Failed to update test case id: ${params.id}\n JSON: ${JSON.stringify(testCaseResponse, null, 2)}`
|
|
16
|
+
}],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: 'text', text: JSON.stringify(testCaseResponse) }],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export async function handleUpdateTestCaseStepById(tp, params) {
|
|
2
|
+
if (params.description === undefined && params.result === undefined) {
|
|
3
|
+
return {
|
|
4
|
+
content: [{
|
|
5
|
+
type: 'text',
|
|
6
|
+
text: `Nothing to update for test step id: ${params.id}`
|
|
7
|
+
}],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
const existingTestStep = await tp.getTestStep(params.id);
|
|
11
|
+
if (!existingTestStep) {
|
|
12
|
+
return {
|
|
13
|
+
content: [{
|
|
14
|
+
type: 'text',
|
|
15
|
+
text: `Failed to get test step id: ${params.id}\n JSON: ${JSON.stringify(existingTestStep, null, 2)}`
|
|
16
|
+
}],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const testStepResponse = await tp.updateTestStep({
|
|
20
|
+
id: params.id,
|
|
21
|
+
description: params.description ?? existingTestStep.Description,
|
|
22
|
+
result: params.result ?? existingTestStep.Result,
|
|
23
|
+
});
|
|
24
|
+
if (!testStepResponse) {
|
|
25
|
+
return {
|
|
26
|
+
content: [{
|
|
27
|
+
type: 'text',
|
|
28
|
+
text: `Failed to update test step id: ${params.id}\n JSON: ${JSON.stringify(testStepResponse, null, 2)}`
|
|
29
|
+
}],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: 'text', text: JSON.stringify(testStepResponse) }],
|
|
34
|
+
};
|
|
35
|
+
}
|
package/build/index.js
CHANGED
|
@@ -44,6 +44,20 @@ import { handleUpdateUserStorySubState } from "./handlers/update_user_story_sub_
|
|
|
44
44
|
import { handleGetCardRelations } from "./handlers/get_card_relations.js";
|
|
45
45
|
import { handleCreateCardRelation } from "./handlers/create_card_relation.js";
|
|
46
46
|
import { handleDeleteCardRelation } from "./handlers/delete_card_relation.js";
|
|
47
|
+
import { handleGetTestPlanById } from "./handlers/get_test_plan_by_id.js";
|
|
48
|
+
import { handleGetTestPlanTestCasesById } from "./handlers/get_test_plan_test_cases_by_id.js";
|
|
49
|
+
import { handleGetTestPlanTestCasesWithStepsById } from "./handlers/get_test_plan_test_cases_with_steps_by_id.js";
|
|
50
|
+
import { handleGetTestCaseById } from "./handlers/get_test_case_by_id.js";
|
|
51
|
+
import { handleUpdateTestCaseById } from "./handlers/update_test_case_by_id.js";
|
|
52
|
+
import { handleAddTestCaseStepById } from "./handlers/add_test_case_step_by_id.js";
|
|
53
|
+
import { handleUpdateTestCaseStepById } from "./handlers/update_test_case_step_by_id.js";
|
|
54
|
+
import { handleDeleteTestCaseStepById } from "./handlers/delete_test_case_step_by_id.js";
|
|
55
|
+
import { handleGetProcessWorkflows } from "./handlers/get_process_workflows.js";
|
|
56
|
+
import { handleGetProcesses } from "./handlers/get_processes.js";
|
|
57
|
+
import { handleGetBugWorkflows } from "./handlers/get_bug_workflows.js";
|
|
58
|
+
import { handleGetUserStoryWorkflows } from "./handlers/get_user_story_workflows.js";
|
|
59
|
+
import { handleGetRelationTypes } from "./handlers/get_relation_types.js";
|
|
60
|
+
import { handleGetVersion } from "./handlers/get_version.js";
|
|
47
61
|
const server = new McpServer({
|
|
48
62
|
name: "tp",
|
|
49
63
|
version: "1.0.0"
|
|
@@ -68,7 +82,7 @@ server.registerTool('get_user_story_content', {
|
|
|
68
82
|
inputSchema: {
|
|
69
83
|
id: z.string()
|
|
70
84
|
.min(5)
|
|
71
|
-
.max(
|
|
85
|
+
.max(9)
|
|
72
86
|
.describe('TP (or tp) ID (e.g. 145789)')
|
|
73
87
|
},
|
|
74
88
|
}, async ({ id }) => handleGetUserStoryContent(tp, id));
|
|
@@ -95,7 +109,7 @@ server.registerTool('get_release_bugs', {
|
|
|
95
109
|
name: z.string()
|
|
96
110
|
.describe('Release name'),
|
|
97
111
|
results: z.number()
|
|
98
|
-
.default(
|
|
112
|
+
.default(300)
|
|
99
113
|
.optional()
|
|
100
114
|
.describe('Number of results to return, default is 100'),
|
|
101
115
|
withDescription: z.boolean()
|
|
@@ -213,7 +227,7 @@ server.registerTool('get_bug_content', {
|
|
|
213
227
|
inputSchema: {
|
|
214
228
|
id: z.string()
|
|
215
229
|
.min(5)
|
|
216
|
-
.max(
|
|
230
|
+
.max(9)
|
|
217
231
|
.describe('Bug card ID (e.g. 145789)')
|
|
218
232
|
},
|
|
219
233
|
}, async ({ id }) => handleGetBugContent(tp, id));
|
|
@@ -239,7 +253,7 @@ server.registerTool('add_comment_with_user', {
|
|
|
239
253
|
inputSchema: {
|
|
240
254
|
id: z.string()
|
|
241
255
|
.min(5)
|
|
242
|
-
.max(
|
|
256
|
+
.max(9)
|
|
243
257
|
.describe('TP card id, usually user story or bug ID (e.g. 145789)'),
|
|
244
258
|
comment: z.string()
|
|
245
259
|
.describe('Comment content to add'),
|
|
@@ -289,7 +303,7 @@ server.registerTool('add_comment', {
|
|
|
289
303
|
inputSchema: {
|
|
290
304
|
id: z.string()
|
|
291
305
|
.min(5)
|
|
292
|
-
.max(
|
|
306
|
+
.max(9)
|
|
293
307
|
.describe('TP card id, usually user story or bug ID (e.g. 145789)'),
|
|
294
308
|
comment: z.string()
|
|
295
309
|
.describe('Comment content to add'),
|
|
@@ -301,7 +315,7 @@ server.registerTool('get_user_story_comments', {
|
|
|
301
315
|
inputSchema: {
|
|
302
316
|
id: z.string()
|
|
303
317
|
.min(5)
|
|
304
|
-
.max(
|
|
318
|
+
.max(9)
|
|
305
319
|
.describe('TP user story ID (e.g. 145789)'),
|
|
306
320
|
results: z.number()
|
|
307
321
|
.default(25)
|
|
@@ -315,7 +329,7 @@ server.registerTool('get_bug_comments', {
|
|
|
315
329
|
inputSchema: {
|
|
316
330
|
id: z.string()
|
|
317
331
|
.min(5)
|
|
318
|
-
.max(
|
|
332
|
+
.max(9)
|
|
319
333
|
.describe('TP bug ID (e.g. 145789)'),
|
|
320
334
|
results: z.number()
|
|
321
335
|
.default(25)
|
|
@@ -340,7 +354,7 @@ server.registerTool('create_bug_based_on_card', {
|
|
|
340
354
|
card: z.object({
|
|
341
355
|
id: z.string()
|
|
342
356
|
.min(5)
|
|
343
|
-
.max(
|
|
357
|
+
.max(9)
|
|
344
358
|
.describe(`Usually user story id, bug ID, or feature ID (e.g. 145789)`),
|
|
345
359
|
type: z.enum(["UserStory", "Bug", "Feature"])
|
|
346
360
|
}),
|
|
@@ -399,7 +413,7 @@ server.registerTool('update_bug', {
|
|
|
399
413
|
inputSchema: {
|
|
400
414
|
id: z.string()
|
|
401
415
|
.min(5)
|
|
402
|
-
.max(
|
|
416
|
+
.max(9)
|
|
403
417
|
.describe('Bug card ID (e.g. 145789)'),
|
|
404
418
|
title: z.string()
|
|
405
419
|
.optional()
|
|
@@ -440,7 +454,7 @@ server.registerTool('update_user_story_state', {
|
|
|
440
454
|
inputSchema: {
|
|
441
455
|
id: z.string()
|
|
442
456
|
.min(5)
|
|
443
|
-
.max(
|
|
457
|
+
.max(9)
|
|
444
458
|
.describe('User story card ID (e.g. 145789)'),
|
|
445
459
|
entityStateId: z.string()
|
|
446
460
|
.optional()
|
|
@@ -464,7 +478,7 @@ server.registerTool('update_user_story', {
|
|
|
464
478
|
inputSchema: {
|
|
465
479
|
id: z.string()
|
|
466
480
|
.min(5)
|
|
467
|
-
.max(
|
|
481
|
+
.max(9)
|
|
468
482
|
.describe('User story card ID (e.g. 145789)'),
|
|
469
483
|
title: z.string()
|
|
470
484
|
.optional()
|
|
@@ -551,12 +565,12 @@ server.registerTool('create_user_story', {
|
|
|
551
565
|
.describe('Optional user story description (when provided, format as HTML)'),
|
|
552
566
|
featureId: z.string()
|
|
553
567
|
.min(5)
|
|
554
|
-
.max(
|
|
568
|
+
.max(9)
|
|
555
569
|
.optional()
|
|
556
570
|
.describe('Optional Feature ID to link this user story to (e.g. 145636)'),
|
|
557
571
|
releaseId: z.string()
|
|
558
572
|
.min(5)
|
|
559
|
-
.max(
|
|
573
|
+
.max(9)
|
|
560
574
|
.optional()
|
|
561
575
|
.describe('Optional Release ID to link this user story to (e.g. 145200)'),
|
|
562
576
|
projectId: z.string()
|
|
@@ -629,12 +643,12 @@ server.registerTool('create_formatted_user_story', {
|
|
|
629
643
|
.describe('Anything that helps understand the story context but does not fit other sections'),
|
|
630
644
|
featureId: z.string()
|
|
631
645
|
.min(5)
|
|
632
|
-
.max(
|
|
646
|
+
.max(9)
|
|
633
647
|
.optional()
|
|
634
648
|
.describe('Optional Feature ID to link this user story to (e.g. 145636)'),
|
|
635
649
|
releaseId: z.string()
|
|
636
650
|
.min(5)
|
|
637
|
-
.max(
|
|
651
|
+
.max(9)
|
|
638
652
|
.optional()
|
|
639
653
|
.describe('Optional Release ID to link this user story to (e.g. 145200)'),
|
|
640
654
|
projectId: z.string()
|
|
@@ -712,12 +726,12 @@ server.registerTool('create_feature', {
|
|
|
712
726
|
.describe('Optional feature description (when provided, format as HTML)'),
|
|
713
727
|
epicId: z.string()
|
|
714
728
|
.min(5)
|
|
715
|
-
.max(
|
|
729
|
+
.max(9)
|
|
716
730
|
.optional()
|
|
717
731
|
.describe('Optional Epic ID to link this feature to (e.g. 145636)'),
|
|
718
732
|
releaseId: z.string()
|
|
719
733
|
.min(5)
|
|
720
|
-
.max(
|
|
734
|
+
.max(9)
|
|
721
735
|
.optional()
|
|
722
736
|
.describe('Optional Release ID to link this feature to (e.g. 145200)'),
|
|
723
737
|
projectId: z.string()
|
|
@@ -739,7 +753,7 @@ server.registerTool('create_epic', {
|
|
|
739
753
|
.describe('Optional epic description (when provided, format as HTML)'),
|
|
740
754
|
releaseId: z.string()
|
|
741
755
|
.min(5)
|
|
742
|
-
.max(
|
|
756
|
+
.max(9)
|
|
743
757
|
.optional()
|
|
744
758
|
.describe('Optional Release ID to link this epic to (e.g. 145200)'),
|
|
745
759
|
projectId: z.string()
|
|
@@ -753,7 +767,7 @@ server.registerTool('get_epic_content', {
|
|
|
753
767
|
inputSchema: {
|
|
754
768
|
id: z.string()
|
|
755
769
|
.min(5)
|
|
756
|
-
.max(
|
|
770
|
+
.max(9)
|
|
757
771
|
.describe('Epic ID (e.g. 148813)'),
|
|
758
772
|
},
|
|
759
773
|
}, async ({ id }) => handleGetEpicContent(tp, id));
|
|
@@ -763,7 +777,7 @@ server.registerTool('update_epic', {
|
|
|
763
777
|
inputSchema: {
|
|
764
778
|
id: z.string()
|
|
765
779
|
.min(5)
|
|
766
|
-
.max(
|
|
780
|
+
.max(9)
|
|
767
781
|
.describe('Epic ID (e.g. 148813)'),
|
|
768
782
|
title: z.string()
|
|
769
783
|
.optional()
|
|
@@ -773,7 +787,7 @@ server.registerTool('update_epic', {
|
|
|
773
787
|
.describe('Updated epic description (format as HTML)'),
|
|
774
788
|
releaseId: z.string()
|
|
775
789
|
.min(5)
|
|
776
|
-
.max(
|
|
790
|
+
.max(9)
|
|
777
791
|
.optional()
|
|
778
792
|
.describe('Optional Release ID to link this epic to'),
|
|
779
793
|
projectId: z.string()
|
|
@@ -787,7 +801,7 @@ server.registerTool('get_epic_features', {
|
|
|
787
801
|
inputSchema: {
|
|
788
802
|
id: z.string()
|
|
789
803
|
.min(5)
|
|
790
|
-
.max(
|
|
804
|
+
.max(9)
|
|
791
805
|
.describe('Epic ID (e.g. 148813)'),
|
|
792
806
|
},
|
|
793
807
|
}, async ({ id }) => handleGetEpicFeatures(tp, id));
|
|
@@ -799,7 +813,7 @@ server.registerTool('create_test_plan', {
|
|
|
799
813
|
.describe('Test plan title — use the linked card name'),
|
|
800
814
|
resourceId: z.string()
|
|
801
815
|
.min(5)
|
|
802
|
-
.max(
|
|
816
|
+
.max(9)
|
|
803
817
|
.describe('ID of the card to link this test plan to (e.g. 145789)'),
|
|
804
818
|
resourceType: z.enum(['UserStory', 'Bug', 'Feature'])
|
|
805
819
|
.default('UserStory')
|
|
@@ -832,7 +846,7 @@ server.registerTool('get_not_covered_user_stories_in_feature', {
|
|
|
832
846
|
inputSchema: {
|
|
833
847
|
id: z.string()
|
|
834
848
|
.min(5)
|
|
835
|
-
.max(
|
|
849
|
+
.max(9)
|
|
836
850
|
.describe('TP feature ID (e.g. 145636)'),
|
|
837
851
|
},
|
|
838
852
|
}, async ({ id }) => {
|
|
@@ -921,7 +935,7 @@ server.registerTool('get_feature_user_stories', {
|
|
|
921
935
|
inputSchema: {
|
|
922
936
|
id: z.string()
|
|
923
937
|
.min(5)
|
|
924
|
-
.max(
|
|
938
|
+
.max(9)
|
|
925
939
|
.describe('TP feature ID (e.g. 145636)'),
|
|
926
940
|
},
|
|
927
941
|
}, async ({ id }) => handleGetFeatureUserStories(tp, id));
|
|
@@ -931,7 +945,7 @@ server.registerTool('get_user_story_bugs', {
|
|
|
931
945
|
inputSchema: {
|
|
932
946
|
id: z.string()
|
|
933
947
|
.min(5)
|
|
934
|
-
.max(
|
|
948
|
+
.max(9)
|
|
935
949
|
.describe('TP user story ID (e.g. 145789)'),
|
|
936
950
|
},
|
|
937
951
|
}, async ({ id }) => handleGetUserStoryBugs(tp, id));
|
|
@@ -957,7 +971,7 @@ server.registerTool('get_user_story_test_cases', {
|
|
|
957
971
|
inputSchema: {
|
|
958
972
|
resourceId: z.string()
|
|
959
973
|
.min(5)
|
|
960
|
-
.max(
|
|
974
|
+
.max(9)
|
|
961
975
|
.describe('TP UserStory ID (e.g. 145789)')
|
|
962
976
|
},
|
|
963
977
|
}, async ({ resourceId }) => {
|
|
@@ -1041,7 +1055,7 @@ server.registerTool('write_test_cases', {
|
|
|
1041
1055
|
inputSchema: {
|
|
1042
1056
|
resourceId: z.string()
|
|
1043
1057
|
.min(5)
|
|
1044
|
-
.max(
|
|
1058
|
+
.max(9)
|
|
1045
1059
|
.describe('TP card ID (e.g. 145789)'),
|
|
1046
1060
|
resourceType: z.enum(['UserStory', 'Bug', 'Feature'])
|
|
1047
1061
|
.default('UserStory')
|
|
@@ -1094,7 +1108,7 @@ server.registerTool('add_test_cases_to_test_plan', {
|
|
|
1094
1108
|
inputSchema: {
|
|
1095
1109
|
testPlanId: z.string()
|
|
1096
1110
|
.min(5)
|
|
1097
|
-
.max(
|
|
1111
|
+
.max(9)
|
|
1098
1112
|
.describe('Test plan ID to add test cases to (e.g. 145789)'),
|
|
1099
1113
|
testCases: z.array(z.object({
|
|
1100
1114
|
name: z.string()
|
|
@@ -1149,146 +1163,26 @@ server.registerTool('get_process_workflows', {
|
|
|
1149
1163
|
processId: z.string()
|
|
1150
1164
|
.describe('Process ID (e.g. 145636)'),
|
|
1151
1165
|
},
|
|
1152
|
-
}, async ({ processId }) =>
|
|
1153
|
-
const response = await tp.getProcessWorkflows({ processId });
|
|
1154
|
-
if (!response) {
|
|
1155
|
-
return {
|
|
1156
|
-
content: [{
|
|
1157
|
-
type: 'text',
|
|
1158
|
-
text: `Failed to get process workflows, JSON: ${JSON.stringify(response, null, 2)}`
|
|
1159
|
-
}],
|
|
1160
|
-
};
|
|
1161
|
-
}
|
|
1162
|
-
const items = response.items || [];
|
|
1163
|
-
if (items.length === 0) {
|
|
1164
|
-
return {
|
|
1165
|
-
content: [{
|
|
1166
|
-
type: 'text',
|
|
1167
|
-
text: `No process workflows found`,
|
|
1168
|
-
}],
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
return {
|
|
1172
|
-
content: [{
|
|
1173
|
-
type: 'text',
|
|
1174
|
-
text: JSON.stringify(items)
|
|
1175
|
-
}],
|
|
1176
|
-
};
|
|
1177
|
-
});
|
|
1166
|
+
}, async ({ processId }) => handleGetProcessWorkflows(tp, processId));
|
|
1178
1167
|
server.registerTool('get_processes', {
|
|
1179
1168
|
title: 'Get processes',
|
|
1180
1169
|
description: 'Get all Targetprocess processes',
|
|
1181
|
-
}, async (
|
|
1182
|
-
const response = await tp.getProcesses();
|
|
1183
|
-
if (!response) {
|
|
1184
|
-
return {
|
|
1185
|
-
content: [{
|
|
1186
|
-
type: 'text',
|
|
1187
|
-
text: `Failed to get processes, JSON: ${JSON.stringify(response, null, 2)}`
|
|
1188
|
-
}],
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
const items = response.items || [];
|
|
1192
|
-
if (items.length === 0) {
|
|
1193
|
-
return {
|
|
1194
|
-
content: [{
|
|
1195
|
-
type: 'text',
|
|
1196
|
-
text: `No processes found`,
|
|
1197
|
-
}],
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
return {
|
|
1201
|
-
content: [{
|
|
1202
|
-
type: 'text',
|
|
1203
|
-
text: JSON.stringify(items)
|
|
1204
|
-
}],
|
|
1205
|
-
};
|
|
1206
|
-
});
|
|
1170
|
+
}, async () => handleGetProcesses(tp));
|
|
1207
1171
|
server.registerTool('get_bug_workflows', {
|
|
1208
1172
|
title: 'Get bug workflows',
|
|
1209
1173
|
description: 'Get all Targetprocess bug workflows',
|
|
1210
|
-
}, async (
|
|
1211
|
-
const response = await tp.getBugWorkflows();
|
|
1212
|
-
if (!response) {
|
|
1213
|
-
return {
|
|
1214
|
-
content: [{
|
|
1215
|
-
type: 'text',
|
|
1216
|
-
text: `Failed to get bug entity statuses, JSON: ${JSON.stringify(response, null, 2)}`
|
|
1217
|
-
}],
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
const items = response.items || [];
|
|
1221
|
-
if (items.length === 0) {
|
|
1222
|
-
return {
|
|
1223
|
-
content: [{
|
|
1224
|
-
type: 'text',
|
|
1225
|
-
text: `No status data found for workflows`
|
|
1226
|
-
}],
|
|
1227
|
-
};
|
|
1228
|
-
}
|
|
1229
|
-
const workflows = items.map((w) => ({
|
|
1230
|
-
id: w.id,
|
|
1231
|
-
name: w.name,
|
|
1232
|
-
processId: w.process,
|
|
1233
|
-
entityType: w.entityType,
|
|
1234
|
-
entityStates: w.entityStates.map((es) => ({
|
|
1235
|
-
id: es.id,
|
|
1236
|
-
name: es.name,
|
|
1237
|
-
})),
|
|
1238
|
-
}));
|
|
1239
|
-
return {
|
|
1240
|
-
content: [{
|
|
1241
|
-
type: 'text',
|
|
1242
|
-
text: JSON.stringify(workflows)
|
|
1243
|
-
}],
|
|
1244
|
-
};
|
|
1245
|
-
});
|
|
1174
|
+
}, async () => handleGetBugWorkflows(tp));
|
|
1246
1175
|
server.registerTool('get_user_story_workflows', {
|
|
1247
1176
|
title: 'Get User Story workflows',
|
|
1248
1177
|
description: 'Get all Targetprocess user story workflows, with sub-states',
|
|
1249
|
-
}, async (
|
|
1250
|
-
const response = await tp.getUserStoryWorkflowsWithSubStates();
|
|
1251
|
-
if (!response) {
|
|
1252
|
-
return {
|
|
1253
|
-
content: [{
|
|
1254
|
-
type: 'text',
|
|
1255
|
-
text: `Failed to get user story entity statuses, JSON: ${JSON.stringify(response, null, 2)}`
|
|
1256
|
-
}],
|
|
1257
|
-
};
|
|
1258
|
-
}
|
|
1259
|
-
const items = response.items || [];
|
|
1260
|
-
if (items.length === 0) {
|
|
1261
|
-
return {
|
|
1262
|
-
content: [{
|
|
1263
|
-
type: 'text',
|
|
1264
|
-
text: `No status data found for workflows`
|
|
1265
|
-
}],
|
|
1266
|
-
};
|
|
1267
|
-
}
|
|
1268
|
-
const userStoryWorkflows = items.filter((w) => w.entityType.name === "UserStory");
|
|
1269
|
-
const workflows = userStoryWorkflows.map((w) => ({
|
|
1270
|
-
id: w.id,
|
|
1271
|
-
processId: w.workflow.process.id,
|
|
1272
|
-
entityType: w.entityType.name,
|
|
1273
|
-
entityStates: w.subEntityStates.map((es) => ({
|
|
1274
|
-
id: es.id,
|
|
1275
|
-
name: es.name,
|
|
1276
|
-
})),
|
|
1277
|
-
}));
|
|
1278
|
-
return {
|
|
1279
|
-
content: [{
|
|
1280
|
-
type: 'text',
|
|
1281
|
-
text: JSON.stringify(workflows)
|
|
1282
|
-
}],
|
|
1283
|
-
};
|
|
1284
|
-
});
|
|
1178
|
+
}, async () => handleGetUserStoryWorkflows(tp));
|
|
1285
1179
|
server.registerTool('get_card_current_status', {
|
|
1286
1180
|
title: 'Get card status',
|
|
1287
1181
|
description: 'Get the EntityState, TeamState, and assigned teams for a TP card (UserStory, Bug, or Feature) by ID',
|
|
1288
1182
|
inputSchema: {
|
|
1289
1183
|
id: z.string()
|
|
1290
1184
|
.min(5)
|
|
1291
|
-
.max(
|
|
1185
|
+
.max(9)
|
|
1292
1186
|
.describe('TP card ID (e.g. 146055)'),
|
|
1293
1187
|
resourceType: z.enum(['UserStory', 'Bug', 'Feature'])
|
|
1294
1188
|
.default('UserStory')
|
|
@@ -1305,25 +1199,14 @@ server.registerTool('get_card_relations', {
|
|
|
1305
1199
|
inputSchema: {
|
|
1306
1200
|
id: z.string()
|
|
1307
1201
|
.min(5)
|
|
1308
|
-
.max(
|
|
1202
|
+
.max(9)
|
|
1309
1203
|
.describe('TP card ID (e.g. 145789)'),
|
|
1310
1204
|
},
|
|
1311
1205
|
}, async ({ id }) => handleGetCardRelations(tp, id));
|
|
1312
1206
|
server.registerTool('get_relation_types', {
|
|
1313
1207
|
title: 'Get relation types',
|
|
1314
1208
|
description: 'Get all relation types available in this Targetprocess instance (id + name). Use this to find the correct relationType name for "create_card_relation".',
|
|
1315
|
-
}, async () =>
|
|
1316
|
-
const response = await tp.getRelationTypes();
|
|
1317
|
-
if (!response) {
|
|
1318
|
-
return {
|
|
1319
|
-
content: [{ type: 'text', text: `Failed to get relation types` }],
|
|
1320
|
-
};
|
|
1321
|
-
}
|
|
1322
|
-
const items = (response.Items || []).map((t) => ({ id: t.Id, name: t.Name }));
|
|
1323
|
-
return {
|
|
1324
|
-
content: [{ type: 'text', text: JSON.stringify(items) }],
|
|
1325
|
-
};
|
|
1326
|
-
});
|
|
1209
|
+
}, async () => handleGetRelationTypes(tp));
|
|
1327
1210
|
server.registerTool('create_card_relation', {
|
|
1328
1211
|
title: 'Create a relation between two cards',
|
|
1329
1212
|
description: `Create a relation between two TP cards (UserStory, Bug, Feature, etc.).
|
|
@@ -1332,11 +1215,11 @@ server.registerTool('create_card_relation', {
|
|
|
1332
1215
|
inputSchema: {
|
|
1333
1216
|
masterId: z.string()
|
|
1334
1217
|
.min(5)
|
|
1335
|
-
.max(
|
|
1218
|
+
.max(9)
|
|
1336
1219
|
.describe('Master card ID — the source of the relation (e.g. 145789)'),
|
|
1337
1220
|
slaveId: z.string()
|
|
1338
1221
|
.min(5)
|
|
1339
|
-
.max(
|
|
1222
|
+
.max(9)
|
|
1340
1223
|
.describe('Slave card ID — the target of the relation (e.g. 145790)'),
|
|
1341
1224
|
relationType: z.string()
|
|
1342
1225
|
.optional()
|
|
@@ -1368,7 +1251,7 @@ server.registerTool('create_task', {
|
|
|
1368
1251
|
.describe('Task title'),
|
|
1369
1252
|
userStoryId: z.string()
|
|
1370
1253
|
.min(5)
|
|
1371
|
-
.max(
|
|
1254
|
+
.max(9)
|
|
1372
1255
|
.describe('User story ID to link the task to (e.g. 145789)'),
|
|
1373
1256
|
description: z.string()
|
|
1374
1257
|
.optional()
|
|
@@ -1459,9 +1342,103 @@ server.registerTool('get_version', {
|
|
|
1459
1342
|
title: 'Get server version',
|
|
1460
1343
|
description: 'Returns the current version of the MCP server from package.json.',
|
|
1461
1344
|
inputSchema: {},
|
|
1462
|
-
}, async () => (
|
|
1463
|
-
|
|
1464
|
-
|
|
1345
|
+
}, async () => handleGetVersion(version));
|
|
1346
|
+
server.registerTool('get_test_plan_by_id', {
|
|
1347
|
+
title: 'Get test plan by ID',
|
|
1348
|
+
description: 'Get a Targetprocess Test Plan by its ID, including name, plain-text description, state, and linked card.',
|
|
1349
|
+
inputSchema: {
|
|
1350
|
+
id: z.string()
|
|
1351
|
+
.min(5)
|
|
1352
|
+
.max(9)
|
|
1353
|
+
.describe('Test plan ID (e.g. 145789)'),
|
|
1354
|
+
},
|
|
1355
|
+
}, async ({ id }) => handleGetTestPlanById(tp, id));
|
|
1356
|
+
server.registerTool('get_test_plan_test_cases_by_id', {
|
|
1357
|
+
title: 'Get test plan test cases by ID',
|
|
1358
|
+
description: 'Get test cases belonging to a Targetprocess Test Plan by plan ID, including cases in nested child test plans/containers. Returns id, name, plain-text description, and containing test plan metadata (no steps).',
|
|
1359
|
+
inputSchema: {
|
|
1360
|
+
id: z.string()
|
|
1361
|
+
.min(5)
|
|
1362
|
+
.max(9)
|
|
1363
|
+
.describe('Test plan ID (e.g. 145789)'),
|
|
1364
|
+
},
|
|
1365
|
+
}, async ({ id }) => handleGetTestPlanTestCasesById(tp, id));
|
|
1366
|
+
server.registerTool('get_test_plan_test_cases_with_steps_by_id', {
|
|
1367
|
+
title: 'Get test plan test cases with steps by ID',
|
|
1368
|
+
description: 'Get test cases belonging to a Targetprocess Test Plan by plan ID, including nested child test plans/containers and each test case steps.',
|
|
1369
|
+
inputSchema: {
|
|
1370
|
+
id: z.string()
|
|
1371
|
+
.min(5)
|
|
1372
|
+
.max(9)
|
|
1373
|
+
.describe('Test plan ID (e.g. 145789)'),
|
|
1374
|
+
},
|
|
1375
|
+
}, async ({ id }) => handleGetTestPlanTestCasesWithStepsById(tp, id));
|
|
1376
|
+
server.registerTool('get_test_case_by_id', {
|
|
1377
|
+
title: 'Get test case by ID',
|
|
1378
|
+
description: 'Get a single Targetprocess Test Case by its ID, including plain-text description and its steps.',
|
|
1379
|
+
inputSchema: {
|
|
1380
|
+
id: z.string()
|
|
1381
|
+
.min(5)
|
|
1382
|
+
.max(9)
|
|
1383
|
+
.describe('Test case ID (e.g. 145789)'),
|
|
1384
|
+
},
|
|
1385
|
+
}, async ({ id }) => handleGetTestCaseById(tp, id));
|
|
1386
|
+
server.registerTool('update_test_case_by_id', {
|
|
1387
|
+
title: 'Update test case by ID',
|
|
1388
|
+
description: 'Update a Targetprocess Test Case by its ID. Supports name and description only.',
|
|
1389
|
+
inputSchema: {
|
|
1390
|
+
id: z.string()
|
|
1391
|
+
.min(5)
|
|
1392
|
+
.max(9)
|
|
1393
|
+
.describe('Test case ID (e.g. 145789)'),
|
|
1394
|
+
name: z.string()
|
|
1395
|
+
.optional()
|
|
1396
|
+
.describe('Updated test case name'),
|
|
1397
|
+
description: z.string()
|
|
1398
|
+
.optional()
|
|
1399
|
+
.describe('Updated test case description (format as HTML or plain text)'),
|
|
1400
|
+
},
|
|
1401
|
+
}, async ({ id, name, description }) => handleUpdateTestCaseById(tp, { id, name, description }));
|
|
1402
|
+
server.registerTool('add_test_case_step_by_id', {
|
|
1403
|
+
title: 'Add test case step by test case ID',
|
|
1404
|
+
description: 'Add a new step to a Targetprocess Test Case. Despite tool name consistency, this takes testCaseId, not a step ID.',
|
|
1405
|
+
inputSchema: {
|
|
1406
|
+
testCaseId: z.string()
|
|
1407
|
+
.min(5)
|
|
1408
|
+
.max(9)
|
|
1409
|
+
.describe('Test case ID to append the step to (e.g. 145789)'),
|
|
1410
|
+
description: z.string()
|
|
1411
|
+
.describe('Step action text'),
|
|
1412
|
+
result: z.string()
|
|
1413
|
+
.describe('Expected result for this step'),
|
|
1414
|
+
},
|
|
1415
|
+
}, async ({ testCaseId, description, result }) => handleAddTestCaseStepById(tp, { testCaseId, description, result }));
|
|
1416
|
+
server.registerTool('update_test_case_step_by_id', {
|
|
1417
|
+
title: 'Update test case step by ID',
|
|
1418
|
+
description: 'Update a Targetprocess Test Step by its ID. Supports description and result only.',
|
|
1419
|
+
inputSchema: {
|
|
1420
|
+
id: z.string()
|
|
1421
|
+
.min(5)
|
|
1422
|
+
.max(9)
|
|
1423
|
+
.describe('Test step ID (e.g. 145789)'),
|
|
1424
|
+
description: z.string()
|
|
1425
|
+
.optional()
|
|
1426
|
+
.describe('Updated step action text'),
|
|
1427
|
+
result: z.string()
|
|
1428
|
+
.optional()
|
|
1429
|
+
.describe('Updated expected result for this step'),
|
|
1430
|
+
},
|
|
1431
|
+
}, async ({ id, description, result }) => handleUpdateTestCaseStepById(tp, { id, description, result }));
|
|
1432
|
+
server.registerTool('delete_test_case_step_by_id', {
|
|
1433
|
+
title: 'Delete test case step by ID',
|
|
1434
|
+
description: 'Delete a Targetprocess Test Step by its ID.',
|
|
1435
|
+
inputSchema: {
|
|
1436
|
+
id: z.string()
|
|
1437
|
+
.min(5)
|
|
1438
|
+
.max(9)
|
|
1439
|
+
.describe('Test step ID (e.g. 145789)'),
|
|
1440
|
+
},
|
|
1441
|
+
}, async ({ id }) => handleDeleteTestCaseStepById(tp, id));
|
|
1465
1442
|
async function main() {
|
|
1466
1443
|
const transport = new StdioServerTransport();
|
|
1467
1444
|
await server.connect(transport);
|
package/build/tp.js
CHANGED
|
@@ -131,6 +131,35 @@ export class TpClient {
|
|
|
131
131
|
return { ok: false, status: 0, body: String(error) };
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
+
async getAllOrNull(params) {
|
|
135
|
+
const allItems = [];
|
|
136
|
+
let skip = 0;
|
|
137
|
+
const take = 100;
|
|
138
|
+
while (true) {
|
|
139
|
+
const page = await this.get({
|
|
140
|
+
...params,
|
|
141
|
+
param: {
|
|
142
|
+
...params.param,
|
|
143
|
+
take,
|
|
144
|
+
skip,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
if (!page)
|
|
148
|
+
return null;
|
|
149
|
+
if (!page?.Items?.length)
|
|
150
|
+
break;
|
|
151
|
+
allItems.push(...page.Items);
|
|
152
|
+
if (!page.Next)
|
|
153
|
+
break;
|
|
154
|
+
skip += take;
|
|
155
|
+
}
|
|
156
|
+
return allItems;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
160
|
+
* TP
|
|
161
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
162
|
+
*/
|
|
134
163
|
async getUserStory(userStoryId) {
|
|
135
164
|
const response = await this.get({
|
|
136
165
|
pathParam: ["userStories", userStoryId],
|
|
@@ -518,7 +547,7 @@ export class TpClient {
|
|
|
518
547
|
param: {
|
|
519
548
|
"where": `Description contains '${text}' and EntityState.Name eq 'Done'`,
|
|
520
549
|
"format": "json",
|
|
521
|
-
"take": "
|
|
550
|
+
"take": "100",
|
|
522
551
|
},
|
|
523
552
|
});
|
|
524
553
|
}
|
|
@@ -531,7 +560,7 @@ export class TpClient {
|
|
|
531
560
|
},
|
|
532
561
|
});
|
|
533
562
|
}
|
|
534
|
-
async getReleaseUserStories({ name, results =
|
|
563
|
+
async getReleaseUserStories({ name, results = 100, withDescription = false }) {
|
|
535
564
|
const includeFilter = withDescription ? "[Name, Description, Id]" : "[Name, Id]";
|
|
536
565
|
return this.get({
|
|
537
566
|
pathParam: ["UserStories"],
|
|
@@ -543,7 +572,7 @@ export class TpClient {
|
|
|
543
572
|
}
|
|
544
573
|
});
|
|
545
574
|
}
|
|
546
|
-
async getReleaseOpenUserStories({ name, results =
|
|
575
|
+
async getReleaseOpenUserStories({ name, results = 300, withDescription = false }) {
|
|
547
576
|
const includeFilter = withDescription ? "[Name, Description, Id]" : "[Name, Id]";
|
|
548
577
|
return this.get({
|
|
549
578
|
pathParam: ["UserStories"],
|
|
@@ -555,7 +584,7 @@ export class TpClient {
|
|
|
555
584
|
}
|
|
556
585
|
});
|
|
557
586
|
}
|
|
558
|
-
async getReleaseOpenBugs({ name, results =
|
|
587
|
+
async getReleaseOpenBugs({ name, results = 300, withDescription = false }) {
|
|
559
588
|
const includeFilter = withDescription ? "[Name, Description, Id]" : "[Name, Id]";
|
|
560
589
|
return this.get({
|
|
561
590
|
pathParam: ["Bugs"],
|
|
@@ -579,7 +608,7 @@ export class TpClient {
|
|
|
579
608
|
}
|
|
580
609
|
});
|
|
581
610
|
}
|
|
582
|
-
async getReleaseFeatures({ name, results =
|
|
611
|
+
async getReleaseFeatures({ name, results = 100, withDescription = false }) {
|
|
583
612
|
const includeFilter = withDescription ? "[Name, Description, Id]" : "[Name, Id]";
|
|
584
613
|
return this.get({
|
|
585
614
|
pathParam: ["Features"],
|
|
@@ -644,6 +673,79 @@ export class TpClient {
|
|
|
644
673
|
},
|
|
645
674
|
});
|
|
646
675
|
}
|
|
676
|
+
toTestPlanNode(testPlan, fallbackId) {
|
|
677
|
+
const id = testPlan?.Id ?? fallbackId;
|
|
678
|
+
if (id === undefined || id === null)
|
|
679
|
+
return null;
|
|
680
|
+
const numericId = Number(id);
|
|
681
|
+
return {
|
|
682
|
+
id: String(id),
|
|
683
|
+
numericId: Number.isNaN(numericId) ? 0 : numericId,
|
|
684
|
+
name: testPlan?.Name,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
async getDirectTestPlanTestCaseItems(testPlan) {
|
|
688
|
+
const items = await this.getAllOrNull({
|
|
689
|
+
pathParam: ["testPlans", testPlan.id, "testcases"],
|
|
690
|
+
param: { "format": "json" },
|
|
691
|
+
});
|
|
692
|
+
if (!items)
|
|
693
|
+
return null;
|
|
694
|
+
return items.map((item) => ({
|
|
695
|
+
...item,
|
|
696
|
+
TestPlanId: testPlan.numericId,
|
|
697
|
+
TestPlanName: testPlan.name || item.LinkedTestPlan?.Name,
|
|
698
|
+
}));
|
|
699
|
+
}
|
|
700
|
+
async getChildTestPlanNodes(testPlanId) {
|
|
701
|
+
const items = await this.getAllOrNull({
|
|
702
|
+
pathParam: ["testPlans"],
|
|
703
|
+
param: {
|
|
704
|
+
"format": "json",
|
|
705
|
+
"where": `ParentTestPlans.Id eq ${testPlanId}`,
|
|
706
|
+
"include": "[Id,Name,ParentTestPlans[Id,Name]]",
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
if (!items)
|
|
710
|
+
return null;
|
|
711
|
+
return items
|
|
712
|
+
.map((item) => this.toTestPlanNode(item))
|
|
713
|
+
.filter((item) => Boolean(item));
|
|
714
|
+
}
|
|
715
|
+
async getIndirectTestPlanTestCases(testPlanId) {
|
|
716
|
+
const rootTestPlan = await this.getTestPlan(testPlanId);
|
|
717
|
+
const rootTestPlanNode = this.toTestPlanNode(rootTestPlan, testPlanId);
|
|
718
|
+
if (!rootTestPlanNode)
|
|
719
|
+
return null;
|
|
720
|
+
const queue = [rootTestPlanNode];
|
|
721
|
+
const visitedPlanIds = new Set();
|
|
722
|
+
const seenTestCaseIds = new Set();
|
|
723
|
+
const testCases = [];
|
|
724
|
+
while (queue.length > 0) {
|
|
725
|
+
const testPlan = queue.shift();
|
|
726
|
+
if (visitedPlanIds.has(testPlan.id))
|
|
727
|
+
continue;
|
|
728
|
+
visitedPlanIds.add(testPlan.id);
|
|
729
|
+
const directTestCases = await this.getDirectTestPlanTestCaseItems(testPlan);
|
|
730
|
+
if (!directTestCases)
|
|
731
|
+
return null;
|
|
732
|
+
for (const testCase of directTestCases) {
|
|
733
|
+
const testCaseId = String(testCase.Id);
|
|
734
|
+
if (seenTestCaseIds.has(testCaseId))
|
|
735
|
+
continue;
|
|
736
|
+
seenTestCaseIds.add(testCaseId);
|
|
737
|
+
testCases.push(testCase);
|
|
738
|
+
}
|
|
739
|
+
const childTestPlans = await this.getChildTestPlanNodes(testPlan.id);
|
|
740
|
+
if (!childTestPlans)
|
|
741
|
+
return null;
|
|
742
|
+
for (const childTestPlan of childTestPlans) {
|
|
743
|
+
if (!visitedPlanIds.has(childTestPlan.id))
|
|
744
|
+
queue.push(childTestPlan);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return { Next: "", Items: testCases };
|
|
748
|
+
}
|
|
647
749
|
async getTestPlanTestCases(testPlanId) {
|
|
648
750
|
return this.get({
|
|
649
751
|
pathParam: ["testPlans", testPlanId, "testcases"],
|
|
@@ -656,6 +758,46 @@ export class TpClient {
|
|
|
656
758
|
param: { "format": "json" },
|
|
657
759
|
});
|
|
658
760
|
}
|
|
761
|
+
async getTestCase(testCaseId) {
|
|
762
|
+
return this.get({
|
|
763
|
+
pathParam: ["testCases", testCaseId],
|
|
764
|
+
param: { "format": "json" },
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
async updateTestCase({ id, name, description }) {
|
|
768
|
+
const testCase = { "Id": id };
|
|
769
|
+
if (name !== undefined)
|
|
770
|
+
testCase["Name"] = name;
|
|
771
|
+
if (description !== undefined)
|
|
772
|
+
testCase["Description"] = description;
|
|
773
|
+
return this.post({
|
|
774
|
+
pathParam: ["testCases"],
|
|
775
|
+
param: { "format": "json" },
|
|
776
|
+
}, testCase);
|
|
777
|
+
}
|
|
778
|
+
async getTestStep(testStepId) {
|
|
779
|
+
return this.get({
|
|
780
|
+
pathParam: ["testSteps", testStepId],
|
|
781
|
+
param: { "format": "json" },
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
async updateTestStep({ id, description, result }) {
|
|
785
|
+
const testStep = { "Id": id };
|
|
786
|
+
if (description !== undefined)
|
|
787
|
+
testStep["Description"] = description;
|
|
788
|
+
if (result !== undefined)
|
|
789
|
+
testStep["Result"] = result;
|
|
790
|
+
return this.post({
|
|
791
|
+
pathParam: ["testSteps"],
|
|
792
|
+
param: { "format": "json" },
|
|
793
|
+
}, testStep);
|
|
794
|
+
}
|
|
795
|
+
async deleteTestStep(testStepId) {
|
|
796
|
+
return this.del({
|
|
797
|
+
pathParam: ["testSteps", testStepId],
|
|
798
|
+
param: { "format": "json" },
|
|
799
|
+
});
|
|
800
|
+
}
|
|
659
801
|
async getProjects() {
|
|
660
802
|
return this.get({
|
|
661
803
|
pathParam: ["Projects"],
|
|
@@ -943,6 +1085,12 @@ export class TpClient {
|
|
|
943
1085
|
param: { "format": "json" },
|
|
944
1086
|
});
|
|
945
1087
|
}
|
|
1088
|
+
async getTestPlan(testPlanId) {
|
|
1089
|
+
return this.get({
|
|
1090
|
+
pathParam: ["testPlans", testPlanId],
|
|
1091
|
+
param: { "format": "json" },
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
946
1094
|
async addAttachedFile(generalId, source) {
|
|
947
1095
|
let blob;
|
|
948
1096
|
let fileName;
|