@testkase/test-management-mcp 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -136
- package/build/compact.d.ts +5 -0
- package/build/compact.d.ts.map +1 -0
- package/build/compact.js +106 -0
- package/build/compact.js.map +1 -0
- package/build/index.js +324 -432
- package/build/index.js.map +1 -1
- package/nul +2 -0
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -7,60 +7,24 @@
|
|
|
7
7
|
<h3 align="center">@testkase/test-management-mcp</h3>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
|
-
Official MCP server for <a href="https://testkase.com">TestKase
|
|
10
|
+
Official MCP server for <a href="https://testkase.com">TestKase Test Management</a>: let your AI assistant manage test cases, cycles, plans and reports.
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
<p align="center">
|
|
14
14
|
<a href="https://www.npmjs.com/package/@testkase/test-management-mcp"><img src="https://img.shields.io/npm/v/@testkase/test-management-mcp.svg?style=flat-square" alt="npm version" /></a>
|
|
15
|
-
<a href="https://www.npmjs.com/package/@testkase/test-management-mcp"><img src="https://img.shields.io/npm/dm/@testkase/test-management-mcp.svg?style=flat-square" alt="npm downloads" /></a>
|
|
16
15
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="MIT License" /></a>
|
|
17
16
|
</p>
|
|
18
17
|
|
|
19
18
|
---
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
Works with Claude, GitHub Copilot, Cursor and any [MCP](https://modelcontextprotocol.io) client. Pairs with [`@testkase/device-farm-mcp`](https://www.npmjs.com/package/@testkase/device-farm-mcp) for real phones and browsers.
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
> **Formerly `@testkase/mcp-server`.** Same tools and key: change the package name in your config to `@testkase/test-management-mcp`.
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
## Quick start
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
### 1. Get a PAT token
|
|
30
|
-
|
|
31
|
-
Log in to [TestKase](https://www.testkase.com) > **API Keys** > **Generate New Token**
|
|
32
|
-
|
|
33
|
-
### 2. Add to your AI agent
|
|
34
|
-
|
|
35
|
-
<details>
|
|
36
|
-
<summary><strong>Claude Desktop</strong></summary>
|
|
37
|
-
|
|
38
|
-
Edit your config file:
|
|
39
|
-
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
40
|
-
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
41
|
-
|
|
42
|
-
```json
|
|
43
|
-
{
|
|
44
|
-
"mcpServers": {
|
|
45
|
-
"testkase": {
|
|
46
|
-
"command": "npx",
|
|
47
|
-
"args": ["-y", "@testkase/test-management-mcp"],
|
|
48
|
-
"env": {
|
|
49
|
-
"TESTKASE_API_BASE_URL": "https://api.testkase.com",
|
|
50
|
-
"TESTKASE_PAT_TOKEN": "xyz_your_token_here"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
</details>
|
|
57
|
-
|
|
58
|
-
<details>
|
|
59
|
-
<summary><strong>GitHub Copilot (VS Code)</strong></summary>
|
|
60
|
-
|
|
61
|
-
Edit your config file:
|
|
62
|
-
- **Windows:** `%APPDATA%\Code\User\globalStorage\github.copilot\mcp.json`
|
|
63
|
-
- **macOS:** `~/Library/Application Support/Code/User/globalStorage/github.copilot/mcp.json`
|
|
26
|
+
1. **Get an access key:** TestKase > **API Keys** > **Create API Key** (starts with `xyz_`).
|
|
27
|
+
2. **Add it to your assistant** (Claude Desktop, Copilot, Cursor: same block in their MCP config file):
|
|
64
28
|
|
|
65
29
|
```json
|
|
66
30
|
{
|
|
@@ -68,122 +32,70 @@ Edit your config file:
|
|
|
68
32
|
"testkase": {
|
|
69
33
|
"command": "npx",
|
|
70
34
|
"args": ["-y", "@testkase/test-management-mcp"],
|
|
71
|
-
"env": {
|
|
72
|
-
"TESTKASE_API_BASE_URL": "https://api.testkase.com",
|
|
73
|
-
"TESTKASE_PAT_TOKEN": "xyz_your_token_here"
|
|
74
|
-
}
|
|
35
|
+
"env": { "TESTKASE_PAT_TOKEN": "xyz_your_token_here" }
|
|
75
36
|
}
|
|
76
37
|
}
|
|
77
38
|
}
|
|
78
39
|
```
|
|
79
|
-
</details>
|
|
80
40
|
|
|
81
|
-
|
|
82
|
-
<summary><strong>Claude Code (CLI)</strong></summary>
|
|
41
|
+
Claude Code:
|
|
83
42
|
|
|
84
43
|
```bash
|
|
85
|
-
claude mcp add testkase -e
|
|
86
|
-
```
|
|
87
|
-
</details>
|
|
88
|
-
|
|
89
|
-
<details>
|
|
90
|
-
<summary><strong>Cursor</strong></summary>
|
|
91
|
-
|
|
92
|
-
Add to `.cursor/mcp.json` in your project root:
|
|
93
|
-
|
|
94
|
-
```json
|
|
95
|
-
{
|
|
96
|
-
"mcpServers": {
|
|
97
|
-
"testkase": {
|
|
98
|
-
"command": "npx",
|
|
99
|
-
"args": ["-y", "@testkase/test-management-mcp"],
|
|
100
|
-
"env": {
|
|
101
|
-
"TESTKASE_API_BASE_URL": "https://api.testkase.com",
|
|
102
|
-
"TESTKASE_PAT_TOKEN": "xyz_your_token_here"
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
</details>
|
|
109
|
-
|
|
110
|
-
### 3. Restart your agent and start talking
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
"List my projects"
|
|
114
|
-
"Create a login test case in PRJ-1001 with steps"
|
|
115
|
-
"Run TEST-1 as pass in cycle TCYCLE-5"
|
|
116
|
-
"Show me the execution summary for PRJ-1001"
|
|
44
|
+
claude mcp add testkase -e TESTKASE_PAT_TOKEN=xyz_your_token_here -- npx -y @testkase/test-management-mcp
|
|
117
45
|
```
|
|
118
46
|
|
|
119
|
-
|
|
47
|
+
3. **Restart your assistant and ask:** "List my projects", "Create a login test case in PRJ-1001 with steps", "Mark TEST-1 passed in cycle TCYCLE-5", "Show the execution summary".
|
|
120
48
|
|
|
121
49
|
## Tools
|
|
122
50
|
|
|
123
|
-
| Tool |
|
|
124
|
-
|
|
125
|
-
| `list_projects`
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `execute_tests` | Record execution results — manual (default) or as a CI/automation run |
|
|
134
|
-
| `manage_test_plan` | Full plan lifecycle — CRUD, link/unlink cycles, view test cases |
|
|
135
|
-
| `get_report` | Pull from 40+ report types (see below) |
|
|
136
|
-
|
|
137
|
-
### Reporting
|
|
138
|
-
|
|
139
|
-
`get_report` covers execution, coverage, trends, team, defect, and insight report types (insights are computed from your data and use no AI credits):
|
|
140
|
-
|
|
141
|
-
| Category | Report Types |
|
|
142
|
-
|----------|-------------|
|
|
143
|
-
| **Execution** | `execution_summary`, `execution_by_cycle`, `execution_by_tester`, `execution_by_priority`, `execution_by_environment`, `execution_by_folder`, `execution_by_automation` |
|
|
144
|
-
| **Coverage** | `requirement_coverage`, `traceability_matrix`, `failed_requirements`, `uncovered_requirements`, `testcase_coverage`, `unlinked_testcases` |
|
|
145
|
-
| **Trends** | `execution_trend`, `execution_burnup`, `execution_burndown`, `test_creation`, `requirement_coverage_trend`, `execution_velocity` |
|
|
146
|
-
| **Comparison** | `cycle_comparison`, `created_vs_executed`, `scorecard_by_folder`, `scorecard_by_tester` |
|
|
147
|
-
| **Team** | `tester_workload`, `testcase_distribution`, `tester_effectiveness` |
|
|
148
|
-
| **Defects** | `defects_by_cycle`, `defects_by_tester`, `defects_by_folder`, `defect_hotspots` |
|
|
149
|
-
| **Insights** | `predictive_failure`, `smart_prioritization`, `testcase_quality`, `stale_tests`, `flaky_tests`, `suite_optimization` |
|
|
150
|
-
| **Risk & Release** | `release_readiness`, `risk_heatmap_folder`, `risk_heatmap_feature`, `requirement_risk_matrix`, `cycle_health`, `project_health` |
|
|
51
|
+
| Tool | What it does |
|
|
52
|
+
|------|--------------|
|
|
53
|
+
| `list_projects` / `get_project_structure` | Your projects, and their folders, labels, members and fields. |
|
|
54
|
+
| `search_testcases` / `get_testcase` | Find test cases; read one with its steps. |
|
|
55
|
+
| `manage_testcase` | Create, bulk-create, update or delete test cases and their steps. |
|
|
56
|
+
| `manage_folder` | Create, rename, move or delete folders. |
|
|
57
|
+
| `search_test_cycles` / `manage_test_cycle` | Find and manage test cycles; link and assign test cases. |
|
|
58
|
+
| `execute_tests` | Record results, manual or as a CI run. |
|
|
59
|
+
| `manage_test_plan` | Manage test plans and their cycles. |
|
|
60
|
+
| `get_report` | 40+ reports: execution, coverage, trends, team, defects, insights, risk and release. |
|
|
151
61
|
|
|
152
|
-
|
|
62
|
+
## Your key and privacy
|
|
153
63
|
|
|
154
|
-
|
|
64
|
+
- The key stays on your machine and goes only to TestKase.
|
|
65
|
+
- It acts as you, with your project permissions.
|
|
66
|
+
- Test data you ask about is passed to your AI assistant.
|
|
155
67
|
|
|
156
|
-
|
|
157
|
-
|----------|----------|---------|-------------|
|
|
158
|
-
| `TESTKASE_PAT_TOKEN` | Yes | — | Personal Access Token (starts with `xyz_`) |
|
|
159
|
-
| `TESTKASE_API_BASE_URL` | No | `https://api.testkase.com` | API endpoint |
|
|
160
|
-
| `TESTKASE_ORGANIZATION_ID` | No | your first organization | Set this if you belong to more than one organization |
|
|
161
|
-
| `TESTKASE_APP_URL` | No | `https://test-management.testkase.com` | Base for the links returned with results |
|
|
68
|
+
## Settings
|
|
162
69
|
|
|
163
|
-
|
|
70
|
+
| Variable | Required | Default |
|
|
71
|
+
|----------|----------|---------|
|
|
72
|
+
| `TESTKASE_PAT_TOKEN` | Yes | |
|
|
73
|
+
| `TESTKASE_API_BASE_URL` | No | `https://api.testkase.com` |
|
|
74
|
+
| `TESTKASE_ORGANIZATION_ID` | No | your first organization (set it if you belong to several) |
|
|
75
|
+
| `TESTKASE_TIMEOUT_MS` | No | `60000` (raise it for very large reports) |
|
|
164
76
|
|
|
165
|
-
|
|
77
|
+
Replies are compact by default to save tokens: lists return the key fields, plus one `url` pattern with `{id}`. Add `verbose: true` to any read call to get the full API reply.
|
|
166
78
|
|
|
167
|
-
|
|
168
|
-
|---------|----------|
|
|
169
|
-
| "Authentication required" | Verify your token starts with `xyz_` and hasn't expired |
|
|
170
|
-
| Tool not showing up | Restart your AI agent completely; check JSON syntax in config |
|
|
171
|
-
| API connection errors | Verify `TESTKASE_API_BASE_URL` is reachable |
|
|
79
|
+
## Troubleshooting
|
|
172
80
|
|
|
173
|
-
|
|
81
|
+
| Problem | Fix |
|
|
82
|
+
|---------|-----|
|
|
83
|
+
| "Authentication required" | Check the key starts with `xyz_` and hasn't expired. |
|
|
84
|
+
| Tools don't show up | Restart the assistant fully; check the JSON in your config. |
|
|
85
|
+
| Connection errors | Check `TESTKASE_API_BASE_URL` is reachable. |
|
|
174
86
|
|
|
175
|
-
##
|
|
87
|
+
## Testing (contributors)
|
|
176
88
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
89
|
+
```bash
|
|
90
|
+
npm test # offline: fake API, every tool and action
|
|
91
|
+
TESTKASE_PAT_TOKEN=xyz_... npm run test:live # real API: create → execute → report → delete in one sandbox project
|
|
92
|
+
npm run tokens -- test/results/tokens-2.4.1.json test/results/tokens-2.5.0.json
|
|
93
|
+
```
|
|
180
94
|
|
|
181
|
-
|
|
95
|
+
The live suite only touches its sandbox project (see `test/live/guard.mjs`), names everything `MCPTEST-…` and deletes it afterwards.
|
|
182
96
|
|
|
183
|
-
|
|
97
|
+
## Links
|
|
184
98
|
|
|
185
|
-
|
|
99
|
+
[TestKase](https://testkase.com) · [Docs](https://docs.testkase.com) · [`@testkase/device-farm-mcp`](https://www.npmjs.com/package/@testkase/device-farm-mcp)
|
|
186
100
|
|
|
187
|
-
|
|
188
|
-
Built by <a href="https://testkase.com">TestKase</a>
|
|
189
|
-
</p>
|
|
101
|
+
MIT License
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function compact(value: any): any;
|
|
2
|
+
export declare function pick(obj: any, keys: string[]): any;
|
|
3
|
+
export declare function shapeList(data: any, keys: string[], urlIdKey?: string): any;
|
|
4
|
+
export declare function flattenFolders(nodes: any[], parentPath?: string): any[];
|
|
5
|
+
//# sourceMappingURL=compact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compact.d.ts","sourceRoot":"","sources":["../src/compact.ts"],"names":[],"mappings":"AAcA,wBAAgB,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAevC;AAcD,wBAAgB,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CASlD;AAID,wBAAgB,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,OA0BrE;AAGD,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,UAAU,SAAK,GAAG,GAAG,EAAE,CAWnE"}
|
package/build/compact.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Reply trimming: fewer tokens for the AI, same information it needs to act
|
|
2
|
+
const isEmpty = (v) => v === null ||
|
|
3
|
+
v === undefined ||
|
|
4
|
+
(typeof v === "string" && v.trim() === "") ||
|
|
5
|
+
(Array.isArray(v) && v.length === 0) ||
|
|
6
|
+
(typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 0);
|
|
7
|
+
const BOILERPLATE = /success/i;
|
|
8
|
+
// Drops null/blank/empty values and, at the top level only, the backend's {status:"success", message, data}
|
|
9
|
+
// envelope. A message is kept when it says something ("Linked 2, 1 already linked"), dropped when it is
|
|
10
|
+
// boilerplate ("Folders fetched successfully"). A reply with nothing left becomes {ok:true}.
|
|
11
|
+
export function compact(value) {
|
|
12
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || value.status !== "success")
|
|
13
|
+
return clean(value);
|
|
14
|
+
const { status, message, data, ...meta } = value;
|
|
15
|
+
const out = {};
|
|
16
|
+
if (typeof message === "string" && message.trim() && !BOILERPLATE.test(message))
|
|
17
|
+
out.message = message;
|
|
18
|
+
Object.assign(out, clean(meta));
|
|
19
|
+
if (data !== undefined) {
|
|
20
|
+
const inner = clean(data);
|
|
21
|
+
if (inner && typeof inner === "object" && !Array.isArray(inner)) {
|
|
22
|
+
for (const [k, v] of Object.entries(inner))
|
|
23
|
+
if (!(k in out))
|
|
24
|
+
out[k] = v;
|
|
25
|
+
}
|
|
26
|
+
else if (!isEmpty(inner) || inner === false || inner === 0) {
|
|
27
|
+
out.data = inner;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return Object.keys(out).length ? out : { ok: true };
|
|
31
|
+
}
|
|
32
|
+
function clean(value) {
|
|
33
|
+
if (Array.isArray(value))
|
|
34
|
+
return value.map(clean).filter((v) => !isEmpty(v));
|
|
35
|
+
if (!value || typeof value !== "object")
|
|
36
|
+
return value;
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const [k, v] of Object.entries(value)) {
|
|
39
|
+
const c = clean(v);
|
|
40
|
+
if (!isEmpty(c))
|
|
41
|
+
out[k] = c;
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
// keys: "name" or "out=path.to.value"; keeps only non-empty values
|
|
46
|
+
export function pick(obj, keys) {
|
|
47
|
+
const out = {};
|
|
48
|
+
for (const spec of keys) {
|
|
49
|
+
const [outKey, path] = spec.includes("=") ? spec.split("=") : [spec, spec];
|
|
50
|
+
const v = path.split(".").reduce((o, k) => (o == null ? undefined : o[k]), obj);
|
|
51
|
+
const c = clean(v);
|
|
52
|
+
if (!isEmpty(c))
|
|
53
|
+
out[outKey] = c;
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
// Finds the list in a backend reply, keeps the given fields per item, and replaces
|
|
58
|
+
// the per-item url with one template (e.g. ".../testcases/{id}") to save tokens
|
|
59
|
+
export function shapeList(data, keys, urlIdKey) {
|
|
60
|
+
const holders = [data, data?.data];
|
|
61
|
+
let arr;
|
|
62
|
+
let meta = {};
|
|
63
|
+
for (const h of holders) {
|
|
64
|
+
if (!h || typeof h !== "object")
|
|
65
|
+
continue;
|
|
66
|
+
const found = Array.isArray(h) ? h : [h.data, h.rows, h.items, h.projects, h.testcases].find(Array.isArray);
|
|
67
|
+
if (found) {
|
|
68
|
+
arr = found;
|
|
69
|
+
meta = Array.isArray(h) ? {} : h;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Unknown shape: better the whole (compacted) reply than an empty list that reads as "nothing found"
|
|
74
|
+
if (!arr)
|
|
75
|
+
return compact(data);
|
|
76
|
+
const out = {};
|
|
77
|
+
const total = meta.total ?? meta.totalCount ?? data?.total;
|
|
78
|
+
if (total !== undefined)
|
|
79
|
+
out.total = Number(total);
|
|
80
|
+
for (const k of ["page", "limit"]) {
|
|
81
|
+
const v = meta[k] ?? data?.[k];
|
|
82
|
+
if (v !== undefined)
|
|
83
|
+
out[k] = v;
|
|
84
|
+
}
|
|
85
|
+
const sample = arr.find((x) => x?.url && urlIdKey && x[urlIdKey]);
|
|
86
|
+
if (sample)
|
|
87
|
+
out.url = String(sample.url).replace(String(sample[urlIdKey]), `{${urlIdKey}}`);
|
|
88
|
+
out.items = arr.map((x) => pick(x, keys));
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
// Folder tree → flat list with a readable path
|
|
92
|
+
export function flattenFolders(nodes, parentPath = "") {
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const n of nodes ?? []) {
|
|
95
|
+
const path = parentPath ? `${parentPath} / ${n.name}` : n.name;
|
|
96
|
+
const row = { id: Number(n.id), name: n.name };
|
|
97
|
+
if (n.parentId != null && n.parentId !== "")
|
|
98
|
+
row.parent_id = Number(n.parentId);
|
|
99
|
+
row.path = path;
|
|
100
|
+
out.push(row);
|
|
101
|
+
if (Array.isArray(n.children))
|
|
102
|
+
out.push(...flattenFolders(n.children, path));
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=compact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compact.js","sourceRoot":"","sources":["../src/compact.ts"],"names":[],"mappings":"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"}
|