@testkase/test-management-mcp 2.4.1 → 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 CHANGED
@@ -1,88 +1,101 @@
1
- <p align="center">
2
- <a href="https://testkase.com">
3
- <img src="https://testkase.com/logo.png" alt="TestKase" width="120" />
4
- </a>
5
- </p>
6
-
7
- <h3 align="center">@testkase/test-management-mcp</h3>
8
-
9
- <p align="center">
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
- </p>
12
-
13
- <p align="center">
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://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="MIT License" /></a>
16
- </p>
17
-
18
- ---
19
-
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.
21
-
22
- > **Formerly `@testkase/mcp-server`.** Same tools and key: change the package name in your config to `@testkase/test-management-mcp`.
23
-
24
- ## Quick start
25
-
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):
28
-
29
- ```json
30
- {
31
- "mcpServers": {
32
- "testkase": {
33
- "command": "npx",
34
- "args": ["-y", "@testkase/test-management-mcp"],
35
- "env": { "TESTKASE_PAT_TOKEN": "xyz_your_token_here" }
36
- }
37
- }
38
- }
39
- ```
40
-
41
- Claude Code:
42
-
43
- ```bash
44
- claude mcp add testkase -e TESTKASE_PAT_TOKEN=xyz_your_token_here -- npx -y @testkase/test-management-mcp
45
- ```
46
-
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".
48
-
49
- ## Tools
50
-
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. |
61
-
62
- ## Your key and privacy
63
-
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.
67
-
68
- ## Settings
69
-
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
-
76
- ## Troubleshooting
77
-
78
- | Problem | Fix |
79
- |---------|-----|
80
- | "Authentication required" | Check the key starts with `xyz_` and hasn't expired. |
81
- | Tools don't show up | Restart the assistant fully; check the JSON in your config. |
82
- | Connection errors | Check `TESTKASE_API_BASE_URL` is reachable. |
83
-
84
- ## Links
85
-
86
- [TestKase](https://testkase.com) · [Docs](https://docs.testkase.com) · [`@testkase/device-farm-mcp`](https://www.npmjs.com/package/@testkase/device-farm-mcp)
87
-
88
- MIT License
1
+ <p align="center">
2
+ <a href="https://testkase.com">
3
+ <img src="https://testkase.com/logo.png" alt="TestKase" width="120" />
4
+ </a>
5
+ </p>
6
+
7
+ <h3 align="center">@testkase/test-management-mcp</h3>
8
+
9
+ <p align="center">
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
+ </p>
12
+
13
+ <p align="center">
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://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="MIT License" /></a>
16
+ </p>
17
+
18
+ ---
19
+
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.
21
+
22
+ > **Formerly `@testkase/mcp-server`.** Same tools and key: change the package name in your config to `@testkase/test-management-mcp`.
23
+
24
+ ## Quick start
25
+
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):
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "testkase": {
33
+ "command": "npx",
34
+ "args": ["-y", "@testkase/test-management-mcp"],
35
+ "env": { "TESTKASE_PAT_TOKEN": "xyz_your_token_here" }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ Claude Code:
42
+
43
+ ```bash
44
+ claude mcp add testkase -e TESTKASE_PAT_TOKEN=xyz_your_token_here -- npx -y @testkase/test-management-mcp
45
+ ```
46
+
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".
48
+
49
+ ## Tools
50
+
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. |
61
+
62
+ ## Your key and privacy
63
+
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.
67
+
68
+ ## Settings
69
+
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) |
76
+
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.
78
+
79
+ ## Troubleshooting
80
+
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. |
86
+
87
+ ## Testing (contributors)
88
+
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
+ ```
94
+
95
+ The live suite only touches its sandbox project (see `test/live/guard.mjs`), names everything `MCPTEST-…` and deletes it afterwards.
96
+
97
+ ## Links
98
+
99
+ [TestKase](https://testkase.com) · [Docs](https://docs.testkase.com) · [`@testkase/device-farm-mcp`](https://www.npmjs.com/package/@testkase/device-farm-mcp)
100
+
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"}
@@ -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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"}