@uarlouski/testrail-mcp-server 1.3.0 → 1.4.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 +178 -48
- package/dist/client/testrail.js +184 -0
- package/{src/index.ts → dist/index.js} +25 -35
- package/{src/tools/add_attachment_to_run.ts → dist/tools/add_attachment_to_run.js} +10 -20
- package/{src/tools/add_results.ts → dist/tools/add_results.js} +3 -7
- package/{src/tools/add_run.ts → dist/tools/add_run.js} +5 -10
- package/{src/tools/create_case.ts → dist/tools/create_case.js} +2 -8
- package/{src/tools/get_case.ts → dist/tools/get_case.js} +7 -14
- package/{src/tools/get_case_fields.ts → dist/tools/get_case_fields.js} +8 -37
- package/{src/tools/get_cases.ts → dist/tools/get_cases.js} +18 -22
- package/{src/tools/get_projects.ts → dist/tools/get_projects.js} +1 -6
- package/{src/tools/get_sections.ts → dist/tools/get_sections.js} +2 -7
- package/{src/tools/get_statuses.ts → dist/tools/get_statuses.js} +2 -7
- package/{src/tools/get_templates.ts → dist/tools/get_templates.js} +2 -7
- package/{src/tools/get_tests.ts → dist/tools/get_tests.js} +5 -10
- package/{src/tools/update_case.ts → dist/tools/update_case.js} +3 -8
- package/{src/tools/update_cases.ts → dist/tools/update_cases.js} +2 -7
- package/dist/types/custom.js +1 -0
- package/{src/types/testrail.ts → dist/types/testrail.js} +6 -43
- package/{src/utils/mapper.ts → dist/utils/mapper.js} +16 -36
- package/{src/utils/sanitizer.ts → dist/utils/sanitizer.js} +4 -8
- package/package.json +8 -2
- package/.env.example +0 -3
- package/.github/workflows/ci.yml +0 -46
- package/.github/workflows/publish.yml +0 -35
- package/CHANGELOG.md +0 -38
- package/jest.config.js +0 -24
- package/src/client/testrail.ts +0 -227
- package/src/types/custom.ts +0 -19
- package/test/client/testrail.test.ts +0 -545
- package/test/tools/add_results.test.ts +0 -94
- package/test/tools/add_run.test.ts +0 -83
- package/test/tools/create_case.test.ts +0 -101
- package/test/tools/get_case.test.ts +0 -185
- package/test/tools/get_case_fields.test.ts +0 -132
- package/test/tools/get_cases.test.ts +0 -104
- package/test/tools/get_projects.test.ts +0 -38
- package/test/tools/get_sections.test.ts +0 -39
- package/test/tools/get_statuses.test.ts +0 -56
- package/test/tools/get_templates.test.ts +0 -38
- package/test/tools/get_tests.test.ts +0 -47
- package/test/tools/update_case.test.ts +0 -102
- package/test/tools/update_cases.test.ts +0 -89
- package/test/utils/mapper.test.ts +0 -112
- package/test/utils/sanitizer.test.ts +0 -143
- package/tsconfig.json +0 -24
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* in an object or array. Useful for cleaning up TestRail markdown content before sending
|
|
4
4
|
* to LLMs.
|
|
5
5
|
*/
|
|
6
|
-
export function sanitizeValue(value
|
|
6
|
+
export function sanitizeValue(value) {
|
|
7
7
|
if (typeof value === 'string') {
|
|
8
8
|
return value.replace(/\s*style\s*=\s*["'][^"']*["']/gi, '');
|
|
9
9
|
}
|
|
@@ -11,7 +11,7 @@ export function sanitizeValue(value: any): any {
|
|
|
11
11
|
return value.map(sanitizeValue);
|
|
12
12
|
}
|
|
13
13
|
if (value !== null && typeof value === 'object') {
|
|
14
|
-
const result
|
|
14
|
+
const result = {};
|
|
15
15
|
for (const [key, val] of Object.entries(value)) {
|
|
16
16
|
result[key] = sanitizeValue(val);
|
|
17
17
|
}
|
|
@@ -19,24 +19,21 @@ export function sanitizeValue(value: any): any {
|
|
|
19
19
|
}
|
|
20
20
|
return value;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
22
|
/**
|
|
24
23
|
* Recursively removes null and undefined values from objects and arrays.
|
|
25
24
|
* For arrays, filters nested object items as well.
|
|
26
25
|
*/
|
|
27
|
-
export function removeNullish(value
|
|
26
|
+
export function removeNullish(value) {
|
|
28
27
|
if (value == null) {
|
|
29
28
|
return value;
|
|
30
29
|
}
|
|
31
|
-
|
|
32
30
|
if (Array.isArray(value)) {
|
|
33
31
|
return value
|
|
34
32
|
.map(item => removeNullish(item))
|
|
35
33
|
.filter(item => item != null);
|
|
36
34
|
}
|
|
37
|
-
|
|
38
35
|
if (typeof value === 'object') {
|
|
39
|
-
const result
|
|
36
|
+
const result = {};
|
|
40
37
|
for (const [key, val] of Object.entries(value)) {
|
|
41
38
|
const cleanedVal = removeNullish(val);
|
|
42
39
|
if (cleanedVal != null) {
|
|
@@ -45,6 +42,5 @@ export function removeNullish(value: any): any {
|
|
|
45
42
|
}
|
|
46
43
|
return result;
|
|
47
44
|
}
|
|
48
|
-
|
|
49
45
|
return value;
|
|
50
46
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uarlouski/testrail-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server for TestRail integration, enabling LLMs to fetch test cases and metadata.",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"testrail-mcp-server": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
6
12
|
"type": "module",
|
|
7
13
|
"scripts": {
|
|
8
14
|
"build": "rimraf dist && tsc",
|
package/.env.example
DELETED
package/.github/workflows/ci.yml
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: ["main"]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: ["main"]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
build-and-test:
|
|
11
|
-
name: Build and Test (Node ${{ matrix.node-version }})
|
|
12
|
-
runs-on: ubuntu-latest
|
|
13
|
-
|
|
14
|
-
strategy:
|
|
15
|
-
matrix:
|
|
16
|
-
node-version: [18.x, 20.x, 22.x]
|
|
17
|
-
|
|
18
|
-
steps:
|
|
19
|
-
- name: Checkout code
|
|
20
|
-
uses: actions/checkout@v4
|
|
21
|
-
|
|
22
|
-
- name: Setup Node.js ${{ matrix.node-version }}
|
|
23
|
-
uses: actions/setup-node@v4
|
|
24
|
-
with:
|
|
25
|
-
node-version: ${{ matrix.node-version }}
|
|
26
|
-
cache: "npm"
|
|
27
|
-
|
|
28
|
-
- name: Install dependencies
|
|
29
|
-
run: npm ci
|
|
30
|
-
|
|
31
|
-
- name: Run tests
|
|
32
|
-
run: npm test
|
|
33
|
-
|
|
34
|
-
- name: Run tests with coverage
|
|
35
|
-
run: npm run test:coverage
|
|
36
|
-
|
|
37
|
-
- name: Build project
|
|
38
|
-
run: npm run build
|
|
39
|
-
|
|
40
|
-
- name: Upload coverage reports
|
|
41
|
-
uses: actions/upload-artifact@v4
|
|
42
|
-
if: matrix.node-version == '20.x'
|
|
43
|
-
with:
|
|
44
|
-
name: coverage-report
|
|
45
|
-
path: coverage/
|
|
46
|
-
retention-days: 7
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
name: Publish to NPM
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types: [published]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
publish:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
permissions:
|
|
11
|
-
contents: read
|
|
12
|
-
id-token: write
|
|
13
|
-
steps:
|
|
14
|
-
- name: Checkout code
|
|
15
|
-
uses: actions/checkout@v4
|
|
16
|
-
|
|
17
|
-
- name: Setup Node.js
|
|
18
|
-
uses: actions/setup-node@v4
|
|
19
|
-
with:
|
|
20
|
-
node-version: "20.x"
|
|
21
|
-
registry-url: "https://registry.npmjs.org"
|
|
22
|
-
|
|
23
|
-
- name: Install dependencies
|
|
24
|
-
run: npm ci
|
|
25
|
-
|
|
26
|
-
- name: Run tests
|
|
27
|
-
run: npm test
|
|
28
|
-
|
|
29
|
-
- name: Build package
|
|
30
|
-
run: npm run build
|
|
31
|
-
|
|
32
|
-
- name: Publish to NPM
|
|
33
|
-
run: npm publish --provenance --access public
|
|
34
|
-
env:
|
|
35
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/CHANGELOG.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## [1.3.0] - 2026-02-18
|
|
4
|
-
|
|
5
|
-
### Added
|
|
6
|
-
|
|
7
|
-
- **Test run and result management tools**
|
|
8
|
-
- `add_run` - Create a new test run in TestRail
|
|
9
|
-
- `get_tests` - Get tests for a test run, optionally filtered by status
|
|
10
|
-
- `add_results` - Add one or more test results to a test run
|
|
11
|
-
- `get_statuses` - Get all available test statuses (e.g. Passed, Failed, Blocked)
|
|
12
|
-
- Add `add_attachment_to_run` tool to attach files to test runs
|
|
13
|
-
|
|
14
|
-
## [1.2.0] - 2026-02-14
|
|
15
|
-
|
|
16
|
-
### Changed
|
|
17
|
-
|
|
18
|
-
- Optimize MCP call responses by removing non-essential fields and nullish values
|
|
19
|
-
- Migrated from `axios` to native `fetch` API for reduced dependency footprint
|
|
20
|
-
|
|
21
|
-
## [1.1.0] - 2026-02-09
|
|
22
|
-
|
|
23
|
-
### Added
|
|
24
|
-
|
|
25
|
-
- **Configuration via environment variables**
|
|
26
|
-
- `TESTRAIL_INSTANCE_URL` - Your TestRail instance URL
|
|
27
|
-
- `TESTRAIL_USERNAME` - Your TestRail username
|
|
28
|
-
- `TESTRAIL_API_KEY` - [How to get your API key](https://support.testrail.com/hc/en-us/articles/7077039051412-Accessing-the-TestRail-API)
|
|
29
|
-
|
|
30
|
-
- **Test case management tools**
|
|
31
|
-
- `get_projects` - Discover available projects in your TestRail instance
|
|
32
|
-
- `get_case` - Fetch detailed test case info including custom fields
|
|
33
|
-
- `get_cases` - Query cases with filtering and pagination support
|
|
34
|
-
- `get_case_fields` - Explore available fields and dropdown options
|
|
35
|
-
- `get_templates` - List project templates to understand case structures
|
|
36
|
-
- `get_sections` - Navigate the test case hierarchy
|
|
37
|
-
- `create_case` - Create new test cases with full custom field support
|
|
38
|
-
- `update_case` / `update_cases` - Modify single or bulk update cases
|
package/jest.config.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
preset: 'ts-jest/presets/default-esm',
|
|
3
|
-
testEnvironment: 'node',
|
|
4
|
-
extensionsToTreatAsEsm: ['.ts'],
|
|
5
|
-
moduleNameMapper: {
|
|
6
|
-
'^(\\.{1,2}/.*)\\.js$': '$1',
|
|
7
|
-
},
|
|
8
|
-
transform: {
|
|
9
|
-
'^.+\\.tsx?$': [
|
|
10
|
-
'ts-jest',
|
|
11
|
-
{
|
|
12
|
-
useESM: true,
|
|
13
|
-
},
|
|
14
|
-
],
|
|
15
|
-
},
|
|
16
|
-
coverageThreshold: {
|
|
17
|
-
global: {
|
|
18
|
-
branches: 95,
|
|
19
|
-
functions: 95,
|
|
20
|
-
lines: 95,
|
|
21
|
-
statements: 95,
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
};
|
package/src/client/testrail.ts
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { Case, Section, Priority, CaseType, CaseField, Template, Project, Run, Status, Test, Result, Attachment } from "../types/testrail.js";
|
|
2
|
-
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
|
|
5
|
-
interface PaginatedCasesResponse {
|
|
6
|
-
cases: Case[];
|
|
7
|
-
_links: { next: string | null };
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface PaginatedTestsResponse {
|
|
11
|
-
tests: Test[];
|
|
12
|
-
_links: { next: string | null };
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const API_INDEX = '/index.php?';
|
|
16
|
-
const API_BASE_V2 = `${API_INDEX}/api/v2`;
|
|
17
|
-
|
|
18
|
-
export class TestRailClient {
|
|
19
|
-
private baseUrl: string;
|
|
20
|
-
private headers: HeadersInit;
|
|
21
|
-
private auth: string;
|
|
22
|
-
private prioritiesPromise: Promise<Priority[]> | null = null;
|
|
23
|
-
private caseTypesPromise: Promise<CaseType[]> | null = null;
|
|
24
|
-
private caseFieldsPromise: Promise<CaseField[]> | null = null;
|
|
25
|
-
private statusesPromise: Promise<Status[]> | null = null;
|
|
26
|
-
private projectsPromise: Promise<Project[]> | null = null;
|
|
27
|
-
private templatesPromiseMap: Map<string, Promise<Template[]>> = new Map();
|
|
28
|
-
|
|
29
|
-
constructor(baseUrl: string, email: string, apiKey: string) {
|
|
30
|
-
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
31
|
-
const auth = Buffer.from(`${email}:${apiKey}`).toString('base64');
|
|
32
|
-
this.auth = `Basic ${auth}`;
|
|
33
|
-
this.headers = {
|
|
34
|
-
"Content-Type": "application/json",
|
|
35
|
-
"Authorization": this.auth,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async getCase(caseId: string): Promise<Case> {
|
|
40
|
-
return this.get<Case>(`${API_BASE_V2}/get_case/${caseId}`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async getCases(projectId: string, sectionId?: string, filter?: Record<string, string>): Promise<Case[]> {
|
|
44
|
-
let url = `${API_BASE_V2}/get_cases/${projectId}`;
|
|
45
|
-
|
|
46
|
-
if (sectionId) {
|
|
47
|
-
url += `§ion_id=${sectionId}`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (filter) {
|
|
51
|
-
for (const [key, value] of Object.entries(filter)) {
|
|
52
|
-
url += `&${key}=${encodeURIComponent(value)}`;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const allCases: Case[] = [];
|
|
57
|
-
let nextUrl: string | null = url;
|
|
58
|
-
|
|
59
|
-
while (nextUrl) {
|
|
60
|
-
const response: PaginatedCasesResponse = await this.get<PaginatedCasesResponse>(nextUrl);
|
|
61
|
-
|
|
62
|
-
allCases.push(...response.cases);
|
|
63
|
-
|
|
64
|
-
nextUrl = response._links?.next;
|
|
65
|
-
if (nextUrl) {
|
|
66
|
-
nextUrl = `${API_INDEX}${nextUrl}`;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return allCases;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async getSection(sectionId: string): Promise<Section> {
|
|
74
|
-
return this.get<Section>(`${API_BASE_V2}/get_section/${sectionId}`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async getPriorities(): Promise<Priority[]> {
|
|
78
|
-
if (!this.prioritiesPromise) {
|
|
79
|
-
this.prioritiesPromise = this.get<Priority[]>(`${API_BASE_V2}/get_priorities`);
|
|
80
|
-
}
|
|
81
|
-
return this.prioritiesPromise;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async getCaseTypes(): Promise<CaseType[]> {
|
|
85
|
-
if (!this.caseTypesPromise) {
|
|
86
|
-
this.caseTypesPromise = this.get<CaseType[]>(`${API_BASE_V2}/get_case_types`);
|
|
87
|
-
}
|
|
88
|
-
return this.caseTypesPromise;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async getCaseFields(): Promise<CaseField[]> {
|
|
92
|
-
if (!this.caseFieldsPromise) {
|
|
93
|
-
this.caseFieldsPromise = this.get<CaseField[]>(`${API_BASE_V2}/get_case_fields`);
|
|
94
|
-
}
|
|
95
|
-
return this.caseFieldsPromise;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async getStatuses(): Promise<Status[]> {
|
|
99
|
-
if (!this.statusesPromise) {
|
|
100
|
-
this.statusesPromise = this.get<Status[]>(`${API_BASE_V2}/get_statuses`);
|
|
101
|
-
}
|
|
102
|
-
return this.statusesPromise;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async getTemplates(projectId: string): Promise<Template[]> {
|
|
106
|
-
if (!this.templatesPromiseMap.has(projectId)) {
|
|
107
|
-
this.templatesPromiseMap.set(
|
|
108
|
-
projectId,
|
|
109
|
-
this.get<Template[]>(`${API_BASE_V2}/get_templates/${projectId}`)
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
return this.templatesPromiseMap.get(projectId)!;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async updateCase(caseId: string, fields: Record<string, any>): Promise<Case> {
|
|
116
|
-
return this.post<Case>(`${API_BASE_V2}/update_case/${caseId}`, fields);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async updateCases(suiteId: number, caseIds: number[], fields: Record<string, any>): Promise<Case[]> {
|
|
120
|
-
const response = await this.post<{ updated_cases: Case[] }>(`${API_BASE_V2}/update_cases/${suiteId}`, {
|
|
121
|
-
case_ids: caseIds,
|
|
122
|
-
...fields,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
return response.updated_cases;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async createCase(sectionId: string, fields: Record<string, any>): Promise<Case> {
|
|
129
|
-
return this.post<Case>(`${API_BASE_V2}/add_case/${sectionId}`, fields);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async addRun(projectId: string, fields: Record<string, any>): Promise<Run> {
|
|
133
|
-
return this.post<Run>(`${API_BASE_V2}/add_run/${projectId}`, fields);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async getSections(projectId: string): Promise<Section[]> {
|
|
137
|
-
const response = await this.get<{ sections: Section[] }>(`${API_BASE_V2}/get_sections/${projectId}`);
|
|
138
|
-
return response.sections;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async getProjects(): Promise<Project[]> {
|
|
142
|
-
if (!this.projectsPromise) {
|
|
143
|
-
this.projectsPromise = this.get<{ projects: Project[] }>(`${API_BASE_V2}/get_projects`)
|
|
144
|
-
.then(response => response.projects);
|
|
145
|
-
}
|
|
146
|
-
return this.projectsPromise;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
async getTests(runId: number, statusId?: number[]): Promise<Test[]> {
|
|
151
|
-
let url = `${API_BASE_V2}/get_tests/${runId}`;
|
|
152
|
-
|
|
153
|
-
if (statusId && statusId.length > 0) {
|
|
154
|
-
url += `&status_id=${statusId.join(',')}`;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const allTests: Test[] = [];
|
|
158
|
-
let nextUrl: string | null = url;
|
|
159
|
-
|
|
160
|
-
while (nextUrl) {
|
|
161
|
-
const response: PaginatedTestsResponse = await this.get<PaginatedTestsResponse>(nextUrl);
|
|
162
|
-
allTests.push(...response.tests);
|
|
163
|
-
|
|
164
|
-
nextUrl = response._links?.next;
|
|
165
|
-
if (nextUrl) {
|
|
166
|
-
nextUrl = `${API_INDEX}${nextUrl}`;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return allTests;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
async addResults(runId: number, results: Array<Record<string, any>>): Promise<Result[]> {
|
|
175
|
-
return this.post<Result[]>(`${API_BASE_V2}/add_results/${runId}`, { results });
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async addAttachmentToRun(runId: number, filePath: string, filename: string): Promise<Attachment> {
|
|
179
|
-
const fileBuffer = await fs.promises.readFile(filePath);
|
|
180
|
-
const blob = new Blob([fileBuffer]);
|
|
181
|
-
const formData = new FormData();
|
|
182
|
-
formData.append('attachment', blob, filename);
|
|
183
|
-
|
|
184
|
-
const headers: HeadersInit = {
|
|
185
|
-
'Authorization': this.auth,
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
return this._executeRequest<Attachment>('POST', `${API_BASE_V2}/add_attachment_to_run/${runId}`, headers, formData);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
private async get<T>(endpoint: string): Promise<T> {
|
|
192
|
-
return this.executeRequest<T>('GET', endpoint);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
private async post<T>(endpoint: string, data: Record<string, any>): Promise<T> {
|
|
196
|
-
return this.executeRequest<T>('POST', endpoint, data);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
private async executeRequest<T>(method: 'GET' | 'POST', endpoint: string, data?: Record<string, any>): Promise<T> {
|
|
200
|
-
let jsonData = undefined;
|
|
201
|
-
if (data) {
|
|
202
|
-
jsonData = JSON.stringify(data);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return this._executeRequest<T>(method, endpoint, this.headers, jsonData);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
private async _executeRequest<T>(method: 'GET' | 'POST', endpoint: string, headers: HeadersInit, body?: any): Promise<T> {
|
|
209
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
210
|
-
const params: RequestInit = {
|
|
211
|
-
method,
|
|
212
|
-
headers,
|
|
213
|
-
body,
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
const response = await fetch(url, params);
|
|
217
|
-
|
|
218
|
-
if (!response.ok) {
|
|
219
|
-
let errorMessage = `TestRail: ${response.status} ${response.statusText}`;
|
|
220
|
-
const errorText = await response.text();
|
|
221
|
-
errorMessage += ` - ${errorText}`;
|
|
222
|
-
throw new Error(errorMessage);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return await response.json() as T;
|
|
226
|
-
}
|
|
227
|
-
}
|
package/src/types/custom.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
export interface TestCaseResponse {
|
|
4
|
-
id: number;
|
|
5
|
-
title: string;
|
|
6
|
-
section: string;
|
|
7
|
-
type: string;
|
|
8
|
-
priority: string;
|
|
9
|
-
labels: string[];
|
|
10
|
-
references: string | null;
|
|
11
|
-
[key: string]: any;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ToolDefinition<T extends z.ZodRawShape, Context = any> {
|
|
15
|
-
name: string;
|
|
16
|
-
description: string;
|
|
17
|
-
parameters: T;
|
|
18
|
-
handler: (args: z.infer<z.ZodObject<T>>, context: Context) => Promise<Record<string, any>>;
|
|
19
|
-
}
|