@uarlouski/testrail-mcp-server 2.6.0 → 2.7.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/dist/client/testrail.js
CHANGED
|
@@ -13,6 +13,7 @@ export class TestRailClient {
|
|
|
13
13
|
statusesPromise = null;
|
|
14
14
|
projectsPromise = null;
|
|
15
15
|
templatesPromiseMap = new Map();
|
|
16
|
+
sectionsPromiseMap = new Map();
|
|
16
17
|
constructor(baseUrl, email, apiKey) {
|
|
17
18
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
18
19
|
const auth = Buffer.from(`${email}:${apiKey}`).toString('base64');
|
|
@@ -60,7 +61,10 @@ export class TestRailClient {
|
|
|
60
61
|
return this.paginateAll(url, 'cases');
|
|
61
62
|
}
|
|
62
63
|
async getSection(sectionId) {
|
|
63
|
-
|
|
64
|
+
if (!this.sectionsPromiseMap.has(sectionId.toString())) {
|
|
65
|
+
this.sectionsPromiseMap.set(sectionId.toString(), this.get(`${API_BASE_V2}/get_section/${sectionId}`));
|
|
66
|
+
}
|
|
67
|
+
return this.sectionsPromiseMap.get(sectionId.toString());
|
|
64
68
|
}
|
|
65
69
|
async getPriorities() {
|
|
66
70
|
if (!this.prioritiesPromise) {
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ if (!parseResult.success) {
|
|
|
27
27
|
const { TESTRAIL_INSTANCE_URL, TESTRAIL_USERNAME, TESTRAIL_API_KEY, TESTRAIL_ENABLE_SHARED_STEPS, TESTRAIL_ENABLE_CASE_HISTORY, TESTRAIL_ENABLE_RAG_TOOLS, TESTRAIL_ALLOW_WRITE_OPERATIONS, TESTRAIL_ALLOW_READ_OPERATIONS, TESTRAIL_ALLOW_DELETE_OPERATIONS, TESTRAIL_ENABLE_DEPRECATED_TOOLS, TESTRAIL_DISABLED_TOOLS } = parseResult.data;
|
|
28
28
|
const server = new McpServer({
|
|
29
29
|
name: "TestRail MCP Server",
|
|
30
|
-
version: "2.
|
|
30
|
+
version: "2.7.0",
|
|
31
31
|
});
|
|
32
32
|
const client = new TestRailClient(TESTRAIL_INSTANCE_URL, TESTRAIL_USERNAME, TESTRAIL_API_KEY);
|
|
33
33
|
let tools;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import * as fs from "fs";
|
|
3
3
|
import * as path from "path";
|
|
4
|
-
import * as os from "os";
|
|
5
4
|
import { normalizeEntityId, sanitizeValue } from "../../utils/sanitizer.js";
|
|
6
5
|
import { processCustomFields } from "../../utils/mapper.js";
|
|
6
|
+
import { getBaseDirectory } from "../../utils/fs.js";
|
|
7
7
|
import { CaseFieldTypeId, getFieldType } from "./fields.js";
|
|
8
8
|
const parameters = {
|
|
9
9
|
case_ids: z.array(z.union([z.string(), z.number()])).min(1).describe("List of test case IDs (e.g. ['C123', 456])"),
|
|
@@ -144,14 +144,15 @@ export const exportCasesForRagTool = {
|
|
|
144
144
|
description: "Export test cases as formatted Markdown documents with companion JSON metadata sidecar files for Knowledge Base and RAG ingestion.",
|
|
145
145
|
parameters,
|
|
146
146
|
handler: async ({ case_ids, output_dir }, client) => {
|
|
147
|
-
const baseDir = (
|
|
148
|
-
const targetDir = output_dir
|
|
147
|
+
const baseDir = getBaseDirectory();
|
|
148
|
+
const targetDir = output_dir
|
|
149
|
+
? (path.isAbsolute(output_dir) ? output_dir : path.resolve(baseDir, output_dir))
|
|
150
|
+
: path.resolve(baseDir, `rag_export_${Date.now()}`);
|
|
149
151
|
await fs.promises.mkdir(targetDir, { recursive: true });
|
|
150
152
|
const [priorities, caseFields] = await Promise.all([
|
|
151
153
|
client.getPriorities().catch(() => []),
|
|
152
154
|
client.getCaseFields().catch(() => []),
|
|
153
155
|
]);
|
|
154
|
-
const fieldDefMap = buildFieldDefinitionMap(caseFields);
|
|
155
156
|
const writtenFiles = [];
|
|
156
157
|
for (const rawId of case_ids) {
|
|
157
158
|
const id = normalizeEntityId(rawId);
|
|
@@ -164,6 +165,9 @@ export const exportCasesForRagTool = {
|
|
|
164
165
|
}
|
|
165
166
|
}
|
|
166
167
|
const priority = priorities.find(p => p.id === testCase.priority_id)?.name || "Unknown";
|
|
168
|
+
const templateId = testCase.template_id;
|
|
169
|
+
const applicableFields = caseFields.filter(field => field.include_all || (templateId !== null && templateId !== undefined && field.template_ids.includes(templateId)));
|
|
170
|
+
const fieldDefMap = buildFieldDefinitionMap(applicableFields);
|
|
167
171
|
const customFields = processCustomFields(testCase, caseFields);
|
|
168
172
|
const { markdownFields, metadataFields } = categorizeCustomFields(customFields, fieldDefMap);
|
|
169
173
|
const markdownContent = buildMarkdownBody(testCase, sectionName, markdownFields);
|
package/dist/utils/fs.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
/**
|
|
4
|
+
* Resolves a safe, writable base directory for exports and file operations.
|
|
5
|
+
*
|
|
6
|
+
* When the MCP server is spawned by host applications (IDE daemons, npx, Claude Desktop, Cursor, Gemini),
|
|
7
|
+
* `process.cwd()` or `process.env.PWD` often resolves to the root filesystem (`"/"`), where direct writes
|
|
8
|
+
* fail with ENOENT / permission errors (especially on macOS read-only APFS volumes).
|
|
9
|
+
*
|
|
10
|
+
* This function handles multi-environment fallbacks:
|
|
11
|
+
* 1. `process.env.INIT_CWD`: Set by package managers (npm/npx/pnpm) to the original caller's working directory.
|
|
12
|
+
* 2. `process.cwd()` / `process.env.PWD`: Used if pointing to a valid non-root directory.
|
|
13
|
+
* 3. `os.homedir()`: User home directory fallback if the working directory is root or invalid.
|
|
14
|
+
* 4. `os.tmpdir()`: Final safety fallback.
|
|
15
|
+
*/
|
|
16
|
+
export function getBaseDirectory() {
|
|
17
|
+
const candidates = [
|
|
18
|
+
process.env.INIT_CWD,
|
|
19
|
+
process.cwd(),
|
|
20
|
+
process.env.PWD,
|
|
21
|
+
os.homedir(),
|
|
22
|
+
];
|
|
23
|
+
for (const candidate of candidates) {
|
|
24
|
+
if (candidate && candidate !== "/" && candidate.trim() !== "") {
|
|
25
|
+
try {
|
|
26
|
+
if (fs.existsSync(candidate)) {
|
|
27
|
+
return candidate;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Ignore and try next candidate
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return os.tmpdir();
|
|
36
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uarlouski/testrail-mcp-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"mcpName": "io.github.uarlouski/testrail-mcp-server",
|
|
5
5
|
"description": "Model Context Protocol (MCP) server for TestRail integration, enabling LLMs to fetch test cases and metadata.",
|
|
6
6
|
"main": "dist/index.js",
|