@testkase/mcp-server 1.0.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 +206 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +268 -0
- package/build/index.js.map +1 -0
- package/build/test-config.d.ts +2 -0
- package/build/test-config.d.ts.map +1 -0
- package/build/test-config.js +84 -0
- package/build/test-config.js.map +1 -0
- package/package.json +59 -0
- package/src/index.ts +302 -0
- package/src/test-config.ts +89 -0
- package/tsconfig.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# @testkase/mcp-server
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@testkase/mcp-server)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Model Context Protocol (MCP) server for TestKase API. Enables AI agents like Claude Desktop and GitHub Copilot to interact with your TestKase testcase management system.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
⨠**Get Testcases** - Retrieve testcases with advanced filtering and search
|
|
11
|
+
š **Pagination** - Control page size and navigate through results
|
|
12
|
+
š **Advanced Filtering** - Filter by status, priority, dates, custom fields
|
|
13
|
+
š **Search** - Text search and AI-powered semantic search
|
|
14
|
+
š **Secure** - Uses Personal Access Token (PAT) authentication
|
|
15
|
+
š¤ **AI-Ready** - Works with Claude Desktop, GitHub Copilot, and other MCP clients
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
### Global Installation (Recommended)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g @testkase/mcp-server
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Local Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @testkase/mcp-server
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### 1. Get Your PAT Token
|
|
34
|
+
|
|
35
|
+
1. Log in to [TestKase](https://apiqa.testkase.com)
|
|
36
|
+
2. Navigate to **Settings ā Personal Access Tokens**
|
|
37
|
+
3. Click **"Generate New Token"**
|
|
38
|
+
4. Copy the token (starts with `xyz_`)
|
|
39
|
+
|
|
40
|
+
### 2. Configure Your AI Agent
|
|
41
|
+
|
|
42
|
+
#### For Claude Desktop
|
|
43
|
+
|
|
44
|
+
Edit your Claude config file:
|
|
45
|
+
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
46
|
+
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"testkase": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "@testkase/mcp-server"],
|
|
54
|
+
"env": {
|
|
55
|
+
"TESTKASE_API_BASE_URL": "https://apiqa.testkase.com",
|
|
56
|
+
"TESTKASE_PAT_TOKEN": "xyz_your_pat_token_here"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### For GitHub Copilot (VS Code)
|
|
64
|
+
|
|
65
|
+
Edit your Copilot config file:
|
|
66
|
+
- **Windows**: `%APPDATA%\Code\User\globalStorage\github.copilot\mcp.json`
|
|
67
|
+
- **macOS**: `~/Library/Application Support/Code/User/globalStorage/github.copilot/mcp.json`
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"testkase": {
|
|
73
|
+
"command": "npx",
|
|
74
|
+
"args": ["-y", "@testkase/mcp-server"],
|
|
75
|
+
"env": {
|
|
76
|
+
"TESTKASE_API_BASE_URL": "https://apiqa.testkase.com",
|
|
77
|
+
"TESTKASE_PAT_TOKEN": "xyz_your_pat_token_here"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Restart Your AI Agent
|
|
85
|
+
|
|
86
|
+
- **Claude Desktop**: Close and reopen completely
|
|
87
|
+
- **VS Code**: Restart VS Code
|
|
88
|
+
|
|
89
|
+
### 4. Start Using!
|
|
90
|
+
|
|
91
|
+
Ask your AI agent:
|
|
92
|
+
- "Get testcases for project PRJ-1030 in organization 1171"
|
|
93
|
+
- "Show high priority testcases from project ABC-123"
|
|
94
|
+
- "Find testcases containing 'login' in project XYZ-456"
|
|
95
|
+
|
|
96
|
+
## Available Tools
|
|
97
|
+
|
|
98
|
+
### `get_testcases`
|
|
99
|
+
|
|
100
|
+
Retrieve testcases from a project with optional filters and pagination.
|
|
101
|
+
|
|
102
|
+
**Parameters:**
|
|
103
|
+
- `projectId` (required): Project UUID
|
|
104
|
+
- `organizationId` (required): Organization ID
|
|
105
|
+
- `page`: Page number (default: 1)
|
|
106
|
+
- `limit`: Items per page (default: 20)
|
|
107
|
+
- `search`: Search term
|
|
108
|
+
- `searchType`: `"normal"` or `"ai_search"`
|
|
109
|
+
- `filters`: JSON string with filters
|
|
110
|
+
- `sortBy`: Field to sort by
|
|
111
|
+
- `sortOrder`: `"asc"` or `"desc"`
|
|
112
|
+
- And more...
|
|
113
|
+
|
|
114
|
+
## Configuration
|
|
115
|
+
|
|
116
|
+
### Environment Variables
|
|
117
|
+
|
|
118
|
+
- **`TESTKASE_API_BASE_URL`**: API base URL (default: `https://apiqa.testkase.com`)
|
|
119
|
+
- **`TESTKASE_PAT_TOKEN`**: Your Personal Access Token (required)
|
|
120
|
+
|
|
121
|
+
### Using with Different Environments
|
|
122
|
+
|
|
123
|
+
**Production:**
|
|
124
|
+
```json
|
|
125
|
+
"env": {
|
|
126
|
+
"TESTKASE_API_BASE_URL": "https://api.testkase.com",
|
|
127
|
+
"TESTKASE_PAT_TOKEN": "xyz_your_token"
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Local Development:**
|
|
132
|
+
```json
|
|
133
|
+
"env": {
|
|
134
|
+
"TESTKASE_API_BASE_URL": "http://localhost:8080",
|
|
135
|
+
"TESTKASE_PAT_TOKEN": "xyz_your_token"
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Usage Examples
|
|
140
|
+
|
|
141
|
+
### Basic Query
|
|
142
|
+
```
|
|
143
|
+
"Show me testcases for project PRJ-1030 organization 1171"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Filtered Query
|
|
147
|
+
```
|
|
148
|
+
"Get high priority active testcases from project PRJ-1030 org 1171"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Search Query
|
|
152
|
+
```
|
|
153
|
+
"Find testcases about 'authentication' in project PRJ-1030 org 1171"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Date Range
|
|
157
|
+
```
|
|
158
|
+
"Show testcases updated this week in project PRJ-1030 org 1171"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Troubleshooting
|
|
162
|
+
|
|
163
|
+
### "Authentication required" error
|
|
164
|
+
- Verify your PAT token is correct and starts with `xyz_`
|
|
165
|
+
- Check that the token hasn't been revoked
|
|
166
|
+
- Ensure it hasn't expired
|
|
167
|
+
|
|
168
|
+
### Tool not showing up
|
|
169
|
+
1. Restart your AI agent completely
|
|
170
|
+
2. Check the config file path is correct
|
|
171
|
+
3. Verify the JSON syntax is valid
|
|
172
|
+
|
|
173
|
+
### API connection issues
|
|
174
|
+
- Check that the API base URL is correct
|
|
175
|
+
- Verify the TestKase API is accessible
|
|
176
|
+
- Test with curl: `curl -H "Authorization: Bearer xyz_YOUR_TOKEN" https://apiqa.testkase.com/api/v1/projects/testcases/get-testcase/PROJECT_ID?organizationId=ORG_ID`
|
|
177
|
+
|
|
178
|
+
## Documentation
|
|
179
|
+
|
|
180
|
+
- [TestKase Documentation](https://docs.testkase.com)
|
|
181
|
+
- [Model Context Protocol](https://modelcontextprotocol.io)
|
|
182
|
+
|
|
183
|
+
## Support
|
|
184
|
+
|
|
185
|
+
- š§ Email: support@testkase.com
|
|
186
|
+
- š Website: [testkase.com](https://testkase.com)
|
|
187
|
+
|
|
188
|
+
## Security
|
|
189
|
+
|
|
190
|
+
- PAT tokens are sent securely via HTTPS
|
|
191
|
+
- Tokens are validated by TestKase's backend
|
|
192
|
+
- Each user has their own unique token
|
|
193
|
+
- Tokens can be revoked at any time
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
MIT Ā© TestKase
|
|
198
|
+
|
|
199
|
+
## Changelog
|
|
200
|
+
|
|
201
|
+
### 1.0.0 (2026-01-30)
|
|
202
|
+
- Initial release
|
|
203
|
+
- Support for `get_testcases` tool
|
|
204
|
+
- Claude Desktop and GitHub Copilot support
|
|
205
|
+
- PAT token authentication
|
|
206
|
+
- Advanced filtering and search capabilities
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import axios from "axios";
|
|
6
|
+
// Get configuration from environment variables
|
|
7
|
+
const config = {
|
|
8
|
+
apiBaseUrl: process.env.TESTKASE_API_BASE_URL || "https://apiqa.testkase.com",
|
|
9
|
+
patToken: process.env.TESTKASE_PAT_TOKEN,
|
|
10
|
+
};
|
|
11
|
+
// Tool definitions
|
|
12
|
+
const GET_TESTCASES_TOOL = {
|
|
13
|
+
name: "get_testcases",
|
|
14
|
+
description: "Retrieve testcases from a project with optional filters, pagination, and search. " +
|
|
15
|
+
"Returns a list of testcases with their details including title, status, priority, owner, and more. " +
|
|
16
|
+
"Supports filtering by status, priority, dates, and other fields. " +
|
|
17
|
+
"Use this tool to query testcases for a specific project.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
projectId: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "The unique identifier of the project (UUID format)",
|
|
24
|
+
},
|
|
25
|
+
organizationId: {
|
|
26
|
+
type: "number",
|
|
27
|
+
description: "The organization ID that owns the project",
|
|
28
|
+
},
|
|
29
|
+
page: {
|
|
30
|
+
type: "number",
|
|
31
|
+
description: "Page number for pagination (default: 1)",
|
|
32
|
+
default: 1,
|
|
33
|
+
},
|
|
34
|
+
limit: {
|
|
35
|
+
type: "number",
|
|
36
|
+
description: "Number of items per page (default: 20)",
|
|
37
|
+
default: 20,
|
|
38
|
+
},
|
|
39
|
+
selectAll: {
|
|
40
|
+
type: "boolean",
|
|
41
|
+
description: "If true, returns all testcases without pagination",
|
|
42
|
+
default: false,
|
|
43
|
+
},
|
|
44
|
+
columns: {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "Comma-separated list of columns to return. " +
|
|
47
|
+
"Example: 'id,title,priority,status,automated,updated_by,created_at,updated_at'",
|
|
48
|
+
},
|
|
49
|
+
filters: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "JSON string of filters to apply. " +
|
|
52
|
+
'Example: {"status":["active","in_progress"],"priority":["high","critical"]}',
|
|
53
|
+
},
|
|
54
|
+
search: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "Search term to filter testcases by title or summary",
|
|
57
|
+
},
|
|
58
|
+
searchType: {
|
|
59
|
+
type: "string",
|
|
60
|
+
enum: ["normal", "ai_search"],
|
|
61
|
+
description: "Type of search to perform: 'normal' for basic text search, 'ai_search' for semantic search",
|
|
62
|
+
default: "normal",
|
|
63
|
+
},
|
|
64
|
+
sortBy: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "Field to sort by (default: 'updated_at')",
|
|
67
|
+
default: "updated_at",
|
|
68
|
+
},
|
|
69
|
+
sortOrder: {
|
|
70
|
+
type: "string",
|
|
71
|
+
enum: ["asc", "desc"],
|
|
72
|
+
description: "Sort order: 'asc' or 'desc' (default: 'desc')",
|
|
73
|
+
default: "desc",
|
|
74
|
+
},
|
|
75
|
+
create_start_date: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "Filter by creation start date (ISO 8601 format: YYYY-MM-DD)",
|
|
78
|
+
},
|
|
79
|
+
create_end_date: {
|
|
80
|
+
type: "string",
|
|
81
|
+
description: "Filter by creation end date (ISO 8601 format: YYYY-MM-DD)",
|
|
82
|
+
},
|
|
83
|
+
updated_start_date: {
|
|
84
|
+
type: "string",
|
|
85
|
+
description: "Filter by update start date (ISO 8601 format: YYYY-MM-DD)",
|
|
86
|
+
},
|
|
87
|
+
updated_end_date: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "Filter by update end date (ISO 8601 format: YYYY-MM-DD)",
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
required: ["projectId", "organizationId"],
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
// Create server instance
|
|
96
|
+
const server = new Server({
|
|
97
|
+
name: "testkase-mcp-server",
|
|
98
|
+
version: "1.0.0",
|
|
99
|
+
}, {
|
|
100
|
+
capabilities: {
|
|
101
|
+
tools: {},
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
// List available tools
|
|
105
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
106
|
+
return {
|
|
107
|
+
tools: [GET_TESTCASES_TOOL],
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
// Handle tool calls
|
|
111
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
112
|
+
const { name, arguments: args } = request.params;
|
|
113
|
+
try {
|
|
114
|
+
if (name === "get_testcases") {
|
|
115
|
+
return await handleGetTestcases(args);
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error instanceof Error) {
|
|
121
|
+
return {
|
|
122
|
+
content: [
|
|
123
|
+
{
|
|
124
|
+
type: "text",
|
|
125
|
+
text: `Error: ${error.message}`,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
isError: true,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
// Handler for get_testcases tool
|
|
135
|
+
async function handleGetTestcases(args) {
|
|
136
|
+
// Validate required parameters
|
|
137
|
+
if (!args.projectId) {
|
|
138
|
+
throw new Error("projectId is required");
|
|
139
|
+
}
|
|
140
|
+
if (!args.organizationId) {
|
|
141
|
+
throw new Error("organizationId is required");
|
|
142
|
+
}
|
|
143
|
+
// Check for authentication
|
|
144
|
+
if (!config.patToken) {
|
|
145
|
+
throw new Error("Authentication required. Please set TESTKASE_PAT_TOKEN environment variable. " +
|
|
146
|
+
"Generate a Personal Access Token (PAT) from your TestKase account settings.");
|
|
147
|
+
}
|
|
148
|
+
// Validate PAT token format
|
|
149
|
+
if (!config.patToken.startsWith("xyz_")) {
|
|
150
|
+
throw new Error("Invalid PAT token format. TestKase PAT tokens must start with 'xyz_' prefix. " +
|
|
151
|
+
"Please generate a valid PAT from your TestKase account settings.");
|
|
152
|
+
}
|
|
153
|
+
// Build query parameters
|
|
154
|
+
const queryParams = new URLSearchParams();
|
|
155
|
+
queryParams.append("organizationId", args.organizationId.toString());
|
|
156
|
+
if (args.page !== undefined)
|
|
157
|
+
queryParams.append("page", args.page.toString());
|
|
158
|
+
if (args.limit !== undefined)
|
|
159
|
+
queryParams.append("limit", args.limit.toString());
|
|
160
|
+
if (args.selectAll !== undefined)
|
|
161
|
+
queryParams.append("selectAll", args.selectAll.toString());
|
|
162
|
+
if (args.columns)
|
|
163
|
+
queryParams.append("columns", args.columns);
|
|
164
|
+
if (args.filters)
|
|
165
|
+
queryParams.append("filters", args.filters);
|
|
166
|
+
if (args.search)
|
|
167
|
+
queryParams.append("search", args.search);
|
|
168
|
+
if (args.searchType)
|
|
169
|
+
queryParams.append("searchType", args.searchType);
|
|
170
|
+
if (args.sortBy)
|
|
171
|
+
queryParams.append("sortBy", args.sortBy);
|
|
172
|
+
if (args.sortOrder)
|
|
173
|
+
queryParams.append("sortOrder", args.sortOrder);
|
|
174
|
+
if (args.create_start_date)
|
|
175
|
+
queryParams.append("create_start_date", args.create_start_date);
|
|
176
|
+
if (args.create_end_date)
|
|
177
|
+
queryParams.append("create_end_date", args.create_end_date);
|
|
178
|
+
if (args.updated_start_date)
|
|
179
|
+
queryParams.append("updated_start_date", args.updated_start_date);
|
|
180
|
+
if (args.updated_end_date)
|
|
181
|
+
queryParams.append("updated_end_date", args.updated_end_date);
|
|
182
|
+
// Construct API URL
|
|
183
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase/${args.projectId}?${queryParams.toString()}`;
|
|
184
|
+
// Prepare headers with PAT token
|
|
185
|
+
// The PAT token is sent as Bearer token and will be validated by TestKase's JwtAuthGuard
|
|
186
|
+
// which checks for 'xyz_' prefix and validates against the PAT database
|
|
187
|
+
const headers = {
|
|
188
|
+
"Content-Type": "application/json",
|
|
189
|
+
"Authorization": `Bearer ${config.patToken}`,
|
|
190
|
+
};
|
|
191
|
+
try {
|
|
192
|
+
// Make API request
|
|
193
|
+
const response = await axios.get(url, { headers });
|
|
194
|
+
// Format the response
|
|
195
|
+
const data = response.data;
|
|
196
|
+
let resultText = `# Testcases for Project ${args.projectId}\n\n`;
|
|
197
|
+
if (data.data && Array.isArray(data.data)) {
|
|
198
|
+
resultText += `**Total Results:** ${data.total || data.data.length}\n`;
|
|
199
|
+
if (data.page)
|
|
200
|
+
resultText += `**Page:** ${data.page}\n`;
|
|
201
|
+
if (data.limit)
|
|
202
|
+
resultText += `**Limit:** ${data.limit}\n`;
|
|
203
|
+
resultText += `\n## Testcases (${data.data.length} items)\n\n`;
|
|
204
|
+
data.data.forEach((testcase, index) => {
|
|
205
|
+
resultText += `### ${index + 1}. ${testcase.title || "Untitled"}\n`;
|
|
206
|
+
resultText += `- **ID:** ${testcase.id}\n`;
|
|
207
|
+
if (testcase.status)
|
|
208
|
+
resultText += `- **Status:** ${testcase.status}\n`;
|
|
209
|
+
if (testcase.priority)
|
|
210
|
+
resultText += `- **Priority:** ${testcase.priority}\n`;
|
|
211
|
+
if (testcase.automated !== undefined)
|
|
212
|
+
resultText += `- **Automated:** ${testcase.automated}\n`;
|
|
213
|
+
if (testcase.summary)
|
|
214
|
+
resultText += `- **Summary:** ${testcase.summary}\n`;
|
|
215
|
+
if (testcase.owner_id)
|
|
216
|
+
resultText += `- **Owner ID:** ${testcase.owner_id}\n`;
|
|
217
|
+
if (testcase.folder_id)
|
|
218
|
+
resultText += `- **Folder ID:** ${testcase.folder_id}\n`;
|
|
219
|
+
if (testcase.created_at)
|
|
220
|
+
resultText += `- **Created:** ${testcase.created_at}\n`;
|
|
221
|
+
if (testcase.updated_at)
|
|
222
|
+
resultText += `- **Updated:** ${testcase.updated_at}\n`;
|
|
223
|
+
if (testcase.labels && Array.isArray(testcase.labels) && testcase.labels.length > 0) {
|
|
224
|
+
resultText += `- **Labels:** ${testcase.labels.join(", ")}\n`;
|
|
225
|
+
}
|
|
226
|
+
if (testcase.features && Array.isArray(testcase.features) && testcase.features.length > 0) {
|
|
227
|
+
resultText += `- **Features:** ${testcase.features.join(", ")}\n`;
|
|
228
|
+
}
|
|
229
|
+
resultText += `\n`;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
resultText += "No testcases found or unexpected response format.\n\n";
|
|
234
|
+
resultText += `**Raw Response:**\n\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\`\n`;
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
content: [
|
|
238
|
+
{
|
|
239
|
+
type: "text",
|
|
240
|
+
text: resultText,
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
if (axios.isAxiosError(error)) {
|
|
247
|
+
const axiosError = error;
|
|
248
|
+
let errorMessage = `API request failed: ${axiosError.message}`;
|
|
249
|
+
if (axiosError.response) {
|
|
250
|
+
errorMessage += `\nStatus: ${axiosError.response.status}`;
|
|
251
|
+
errorMessage += `\nResponse: ${JSON.stringify(axiosError.response.data)}`;
|
|
252
|
+
}
|
|
253
|
+
throw new Error(errorMessage);
|
|
254
|
+
}
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Start the server
|
|
259
|
+
async function main() {
|
|
260
|
+
const transport = new StdioServerTransport();
|
|
261
|
+
await server.connect(transport);
|
|
262
|
+
console.error("TestKase MCP Server running on stdio");
|
|
263
|
+
}
|
|
264
|
+
main().catch((error) => {
|
|
265
|
+
console.error("Fatal error in main():", error);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
});
|
|
268
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-config.d.ts","sourceRoot":"","sources":["../src/test-config.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Simple test script to verify MCP server configuration
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import * as dotenv from 'dotenv';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
// Load environment variables
|
|
9
|
+
dotenv.config({ path: path.join(__dirname, '..', '.env') });
|
|
10
|
+
const API_BASE_URL = process.env.TESTKASE_API_BASE_URL || 'http://localhost:3000';
|
|
11
|
+
const PAT_TOKEN = process.env.TESTKASE_PAT_TOKEN;
|
|
12
|
+
console.log('š Testing TestKase MCP Server Configuration\n');
|
|
13
|
+
console.log('Configuration:');
|
|
14
|
+
console.log(` API Base URL: ${API_BASE_URL}`);
|
|
15
|
+
console.log(` PAT Token: ${PAT_TOKEN ? PAT_TOKEN.substring(0, 10) + '...' : 'NOT SET'}\n`);
|
|
16
|
+
if (!PAT_TOKEN) {
|
|
17
|
+
console.error('ā Error: TESTKASE_PAT_TOKEN not set in .env file');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (!PAT_TOKEN.startsWith('xyz_')) {
|
|
21
|
+
console.error('ā Error: PAT token must start with "xyz_" prefix');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
console.log('ā
PAT token format is valid\n');
|
|
25
|
+
// Test API connection
|
|
26
|
+
console.log('š Testing API connection...');
|
|
27
|
+
async function testApiConnection() {
|
|
28
|
+
try {
|
|
29
|
+
// Try to hit a simple endpoint or the health check
|
|
30
|
+
const response = await axios.get(`${API_BASE_URL}/api/v1/projects/testcases/get-testcase/test-project?organizationId=1`, {
|
|
31
|
+
headers: {
|
|
32
|
+
'Authorization': `Bearer ${PAT_TOKEN}`,
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
},
|
|
35
|
+
validateStatus: () => true, // Accept any status code
|
|
36
|
+
});
|
|
37
|
+
console.log(`\nš API Response Status: ${response.status}`);
|
|
38
|
+
if (response.status === 200) {
|
|
39
|
+
console.log('ā
API connection successful!');
|
|
40
|
+
console.log('\nš Your MCP server is configured correctly!');
|
|
41
|
+
console.log('\nNext steps:');
|
|
42
|
+
console.log('1. Start the MCP server: npm start');
|
|
43
|
+
console.log('2. Configure Claude Desktop with the config from claude_desktop_config.example.json');
|
|
44
|
+
console.log('3. Restart Claude Desktop');
|
|
45
|
+
console.log('4. Ask Claude to get testcases for your project');
|
|
46
|
+
}
|
|
47
|
+
else if (response.status === 401) {
|
|
48
|
+
console.log('ā ļø Authentication failed - PAT token may be invalid, revoked, or expired');
|
|
49
|
+
console.log(' Please check:');
|
|
50
|
+
console.log(' - Token is correct and starts with xyz_');
|
|
51
|
+
console.log(' - Token has not been revoked in TestKase');
|
|
52
|
+
console.log(' - Token has not expired');
|
|
53
|
+
}
|
|
54
|
+
else if (response.status === 404) {
|
|
55
|
+
console.log('ā ļø Test project not found (this is expected if you used a test project ID)');
|
|
56
|
+
console.log('ā
But authentication worked! Your PAT token is valid.');
|
|
57
|
+
console.log('\nš Your MCP server is configured correctly!');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.log(`ā ļø Unexpected response: ${response.status}`);
|
|
61
|
+
console.log(` Response: ${JSON.stringify(response.data, null, 2)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (axios.isAxiosError(error)) {
|
|
66
|
+
if (error.code === 'ECONNREFUSED') {
|
|
67
|
+
console.log('ā Cannot connect to API server');
|
|
68
|
+
console.log(` Make sure TestKase backend is running at ${API_BASE_URL}`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.log('ā API request failed:', error.message);
|
|
72
|
+
if (error.response) {
|
|
73
|
+
console.log(` Status: ${error.response.status}`);
|
|
74
|
+
console.log(` Response: ${JSON.stringify(error.response.data, null, 2)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log('ā Unexpected error:', error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
testApiConnection();
|
|
84
|
+
//# sourceMappingURL=test-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-config.js","sourceRoot":"","sources":["../src/test-config.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@testkase/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Model Context Protocol (MCP) server for TestKase API - Enables AI agents to interact with TestKase testcase management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./build/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"testkase-mcp": "./build/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"build/",
|
|
12
|
+
"src/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"tsconfig.json"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"prepare": "npm run build",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"watch": "tsc --watch",
|
|
21
|
+
"start": "node build/index.js"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"testcase",
|
|
27
|
+
"test-management",
|
|
28
|
+
"api",
|
|
29
|
+
"testkase",
|
|
30
|
+
"ai-agent",
|
|
31
|
+
"claude",
|
|
32
|
+
"copilot",
|
|
33
|
+
"qa",
|
|
34
|
+
"testing"
|
|
35
|
+
],
|
|
36
|
+
"author": "TestKase <support@testkase.com>",
|
|
37
|
+
"homepage": "https://github.com/SolvionIT/testkase-backend/tree/QA/mcp-server",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/SolvionIT/testkase-backend.git",
|
|
41
|
+
"directory": "mcp-server"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/SolvionIT/testkase-backend/issues"
|
|
45
|
+
},
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
52
|
+
"axios": "^1.6.0",
|
|
53
|
+
"dotenv": "^16.4.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^20.10.0",
|
|
57
|
+
"typescript": "^5.3.0"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
Tool,
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
import axios, { AxiosError } from "axios";
|
|
11
|
+
|
|
12
|
+
// Configuration interface
|
|
13
|
+
interface Config {
|
|
14
|
+
apiBaseUrl: string;
|
|
15
|
+
patToken?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Get configuration from environment variables
|
|
19
|
+
const config: Config = {
|
|
20
|
+
apiBaseUrl: process.env.TESTKASE_API_BASE_URL || "https://apiqa.testkase.com",
|
|
21
|
+
patToken: process.env.TESTKASE_PAT_TOKEN,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Tool definitions
|
|
25
|
+
const GET_TESTCASES_TOOL: Tool = {
|
|
26
|
+
name: "get_testcases",
|
|
27
|
+
description:
|
|
28
|
+
"Retrieve testcases from a project with optional filters, pagination, and search. " +
|
|
29
|
+
"Returns a list of testcases with their details including title, status, priority, owner, and more. " +
|
|
30
|
+
"Supports filtering by status, priority, dates, and other fields. " +
|
|
31
|
+
"Use this tool to query testcases for a specific project.",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
projectId: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "The unique identifier of the project (UUID format)",
|
|
38
|
+
},
|
|
39
|
+
organizationId: {
|
|
40
|
+
type: "number",
|
|
41
|
+
description: "The organization ID that owns the project",
|
|
42
|
+
},
|
|
43
|
+
page: {
|
|
44
|
+
type: "number",
|
|
45
|
+
description: "Page number for pagination (default: 1)",
|
|
46
|
+
default: 1,
|
|
47
|
+
},
|
|
48
|
+
limit: {
|
|
49
|
+
type: "number",
|
|
50
|
+
description: "Number of items per page (default: 20)",
|
|
51
|
+
default: 20,
|
|
52
|
+
},
|
|
53
|
+
selectAll: {
|
|
54
|
+
type: "boolean",
|
|
55
|
+
description: "If true, returns all testcases without pagination",
|
|
56
|
+
default: false,
|
|
57
|
+
},
|
|
58
|
+
columns: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description:
|
|
61
|
+
"Comma-separated list of columns to return. " +
|
|
62
|
+
"Example: 'id,title,priority,status,automated,updated_by,created_at,updated_at'",
|
|
63
|
+
},
|
|
64
|
+
filters: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description:
|
|
67
|
+
"JSON string of filters to apply. " +
|
|
68
|
+
'Example: {"status":["active","in_progress"],"priority":["high","critical"]}',
|
|
69
|
+
},
|
|
70
|
+
search: {
|
|
71
|
+
type: "string",
|
|
72
|
+
description: "Search term to filter testcases by title or summary",
|
|
73
|
+
},
|
|
74
|
+
searchType: {
|
|
75
|
+
type: "string",
|
|
76
|
+
enum: ["normal", "ai_search"],
|
|
77
|
+
description:
|
|
78
|
+
"Type of search to perform: 'normal' for basic text search, 'ai_search' for semantic search",
|
|
79
|
+
default: "normal",
|
|
80
|
+
},
|
|
81
|
+
sortBy: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "Field to sort by (default: 'updated_at')",
|
|
84
|
+
default: "updated_at",
|
|
85
|
+
},
|
|
86
|
+
sortOrder: {
|
|
87
|
+
type: "string",
|
|
88
|
+
enum: ["asc", "desc"],
|
|
89
|
+
description: "Sort order: 'asc' or 'desc' (default: 'desc')",
|
|
90
|
+
default: "desc",
|
|
91
|
+
},
|
|
92
|
+
create_start_date: {
|
|
93
|
+
type: "string",
|
|
94
|
+
description: "Filter by creation start date (ISO 8601 format: YYYY-MM-DD)",
|
|
95
|
+
},
|
|
96
|
+
create_end_date: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "Filter by creation end date (ISO 8601 format: YYYY-MM-DD)",
|
|
99
|
+
},
|
|
100
|
+
updated_start_date: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "Filter by update start date (ISO 8601 format: YYYY-MM-DD)",
|
|
103
|
+
},
|
|
104
|
+
updated_end_date: {
|
|
105
|
+
type: "string",
|
|
106
|
+
description: "Filter by update end date (ISO 8601 format: YYYY-MM-DD)",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
required: ["projectId", "organizationId"],
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Create server instance
|
|
114
|
+
const server = new Server(
|
|
115
|
+
{
|
|
116
|
+
name: "testkase-mcp-server",
|
|
117
|
+
version: "1.0.0",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
capabilities: {
|
|
121
|
+
tools: {},
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// List available tools
|
|
127
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
128
|
+
return {
|
|
129
|
+
tools: [GET_TESTCASES_TOOL],
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Handle tool calls
|
|
134
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
135
|
+
const { name, arguments: args } = request.params;
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
if (name === "get_testcases") {
|
|
139
|
+
return await handleGetTestcases(args as any);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (error instanceof Error) {
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: `Error: ${error.message}`,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
isError: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Handler for get_testcases tool
|
|
160
|
+
async function handleGetTestcases(args: {
|
|
161
|
+
projectId: string;
|
|
162
|
+
organizationId: number;
|
|
163
|
+
page?: number;
|
|
164
|
+
limit?: number;
|
|
165
|
+
selectAll?: boolean;
|
|
166
|
+
columns?: string;
|
|
167
|
+
filters?: string;
|
|
168
|
+
search?: string;
|
|
169
|
+
searchType?: string;
|
|
170
|
+
sortBy?: string;
|
|
171
|
+
sortOrder?: string;
|
|
172
|
+
create_start_date?: string;
|
|
173
|
+
create_end_date?: string;
|
|
174
|
+
updated_start_date?: string;
|
|
175
|
+
updated_end_date?: string;
|
|
176
|
+
}) {
|
|
177
|
+
// Validate required parameters
|
|
178
|
+
if (!args.projectId) {
|
|
179
|
+
throw new Error("projectId is required");
|
|
180
|
+
}
|
|
181
|
+
if (!args.organizationId) {
|
|
182
|
+
throw new Error("organizationId is required");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check for authentication
|
|
186
|
+
if (!config.patToken) {
|
|
187
|
+
throw new Error(
|
|
188
|
+
"Authentication required. Please set TESTKASE_PAT_TOKEN environment variable. " +
|
|
189
|
+
"Generate a Personal Access Token (PAT) from your TestKase account settings."
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Validate PAT token format
|
|
194
|
+
if (!config.patToken.startsWith("xyz_")) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
"Invalid PAT token format. TestKase PAT tokens must start with 'xyz_' prefix. " +
|
|
197
|
+
"Please generate a valid PAT from your TestKase account settings."
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Build query parameters
|
|
202
|
+
const queryParams = new URLSearchParams();
|
|
203
|
+
queryParams.append("organizationId", args.organizationId.toString());
|
|
204
|
+
|
|
205
|
+
if (args.page !== undefined) queryParams.append("page", args.page.toString());
|
|
206
|
+
if (args.limit !== undefined) queryParams.append("limit", args.limit.toString());
|
|
207
|
+
if (args.selectAll !== undefined) queryParams.append("selectAll", args.selectAll.toString());
|
|
208
|
+
if (args.columns) queryParams.append("columns", args.columns);
|
|
209
|
+
if (args.filters) queryParams.append("filters", args.filters);
|
|
210
|
+
if (args.search) queryParams.append("search", args.search);
|
|
211
|
+
if (args.searchType) queryParams.append("searchType", args.searchType);
|
|
212
|
+
if (args.sortBy) queryParams.append("sortBy", args.sortBy);
|
|
213
|
+
if (args.sortOrder) queryParams.append("sortOrder", args.sortOrder);
|
|
214
|
+
if (args.create_start_date) queryParams.append("create_start_date", args.create_start_date);
|
|
215
|
+
if (args.create_end_date) queryParams.append("create_end_date", args.create_end_date);
|
|
216
|
+
if (args.updated_start_date) queryParams.append("updated_start_date", args.updated_start_date);
|
|
217
|
+
if (args.updated_end_date) queryParams.append("updated_end_date", args.updated_end_date);
|
|
218
|
+
|
|
219
|
+
// Construct API URL
|
|
220
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase/${args.projectId}?${queryParams.toString()}`;
|
|
221
|
+
|
|
222
|
+
// Prepare headers with PAT token
|
|
223
|
+
// The PAT token is sent as Bearer token and will be validated by TestKase's JwtAuthGuard
|
|
224
|
+
// which checks for 'xyz_' prefix and validates against the PAT database
|
|
225
|
+
const headers: Record<string, string> = {
|
|
226
|
+
"Content-Type": "application/json",
|
|
227
|
+
"Authorization": `Bearer ${config.patToken}`,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
// Make API request
|
|
232
|
+
const response = await axios.get(url, { headers });
|
|
233
|
+
|
|
234
|
+
// Format the response
|
|
235
|
+
const data = response.data;
|
|
236
|
+
let resultText = `# Testcases for Project ${args.projectId}\n\n`;
|
|
237
|
+
|
|
238
|
+
if (data.data && Array.isArray(data.data)) {
|
|
239
|
+
resultText += `**Total Results:** ${data.total || data.data.length}\n`;
|
|
240
|
+
if (data.page) resultText += `**Page:** ${data.page}\n`;
|
|
241
|
+
if (data.limit) resultText += `**Limit:** ${data.limit}\n`;
|
|
242
|
+
resultText += `\n## Testcases (${data.data.length} items)\n\n`;
|
|
243
|
+
|
|
244
|
+
data.data.forEach((testcase: any, index: number) => {
|
|
245
|
+
resultText += `### ${index + 1}. ${testcase.title || "Untitled"}\n`;
|
|
246
|
+
resultText += `- **ID:** ${testcase.id}\n`;
|
|
247
|
+
if (testcase.status) resultText += `- **Status:** ${testcase.status}\n`;
|
|
248
|
+
if (testcase.priority) resultText += `- **Priority:** ${testcase.priority}\n`;
|
|
249
|
+
if (testcase.automated !== undefined) resultText += `- **Automated:** ${testcase.automated}\n`;
|
|
250
|
+
if (testcase.summary) resultText += `- **Summary:** ${testcase.summary}\n`;
|
|
251
|
+
if (testcase.owner_id) resultText += `- **Owner ID:** ${testcase.owner_id}\n`;
|
|
252
|
+
if (testcase.folder_id) resultText += `- **Folder ID:** ${testcase.folder_id}\n`;
|
|
253
|
+
if (testcase.created_at) resultText += `- **Created:** ${testcase.created_at}\n`;
|
|
254
|
+
if (testcase.updated_at) resultText += `- **Updated:** ${testcase.updated_at}\n`;
|
|
255
|
+
if (testcase.labels && Array.isArray(testcase.labels) && testcase.labels.length > 0) {
|
|
256
|
+
resultText += `- **Labels:** ${testcase.labels.join(", ")}\n`;
|
|
257
|
+
}
|
|
258
|
+
if (testcase.features && Array.isArray(testcase.features) && testcase.features.length > 0) {
|
|
259
|
+
resultText += `- **Features:** ${testcase.features.join(", ")}\n`;
|
|
260
|
+
}
|
|
261
|
+
resultText += `\n`;
|
|
262
|
+
});
|
|
263
|
+
} else {
|
|
264
|
+
resultText += "No testcases found or unexpected response format.\n\n";
|
|
265
|
+
resultText += `**Raw Response:**\n\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\`\n`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
content: [
|
|
270
|
+
{
|
|
271
|
+
type: "text",
|
|
272
|
+
text: resultText,
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
};
|
|
276
|
+
} catch (error) {
|
|
277
|
+
if (axios.isAxiosError(error)) {
|
|
278
|
+
const axiosError = error as AxiosError;
|
|
279
|
+
let errorMessage = `API request failed: ${axiosError.message}`;
|
|
280
|
+
|
|
281
|
+
if (axiosError.response) {
|
|
282
|
+
errorMessage += `\nStatus: ${axiosError.response.status}`;
|
|
283
|
+
errorMessage += `\nResponse: ${JSON.stringify(axiosError.response.data)}`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
throw new Error(errorMessage);
|
|
287
|
+
}
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Start the server
|
|
293
|
+
async function main() {
|
|
294
|
+
const transport = new StdioServerTransport();
|
|
295
|
+
await server.connect(transport);
|
|
296
|
+
console.error("TestKase MCP Server running on stdio");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
main().catch((error) => {
|
|
300
|
+
console.error("Fatal error in main():", error);
|
|
301
|
+
process.exit(1);
|
|
302
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Simple test script to verify MCP server configuration
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import * as dotenv from 'dotenv';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
|
|
10
|
+
// Load environment variables
|
|
11
|
+
dotenv.config({ path: path.join(__dirname, '..', '.env') });
|
|
12
|
+
|
|
13
|
+
const API_BASE_URL = process.env.TESTKASE_API_BASE_URL || 'http://localhost:3000';
|
|
14
|
+
const PAT_TOKEN = process.env.TESTKASE_PAT_TOKEN;
|
|
15
|
+
|
|
16
|
+
console.log('š Testing TestKase MCP Server Configuration\n');
|
|
17
|
+
console.log('Configuration:');
|
|
18
|
+
console.log(` API Base URL: ${API_BASE_URL}`);
|
|
19
|
+
console.log(` PAT Token: ${PAT_TOKEN ? PAT_TOKEN.substring(0, 10) + '...' : 'NOT SET'}\n`);
|
|
20
|
+
|
|
21
|
+
if (!PAT_TOKEN) {
|
|
22
|
+
console.error('ā Error: TESTKASE_PAT_TOKEN not set in .env file');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!PAT_TOKEN.startsWith('xyz_')) {
|
|
27
|
+
console.error('ā Error: PAT token must start with "xyz_" prefix');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log('ā
PAT token format is valid\n');
|
|
32
|
+
|
|
33
|
+
// Test API connection
|
|
34
|
+
console.log('š Testing API connection...');
|
|
35
|
+
|
|
36
|
+
async function testApiConnection() {
|
|
37
|
+
try {
|
|
38
|
+
// Try to hit a simple endpoint or the health check
|
|
39
|
+
const response = await axios.get(`${API_BASE_URL}/api/v1/projects/testcases/get-testcase/test-project?organizationId=1`, {
|
|
40
|
+
headers: {
|
|
41
|
+
'Authorization': `Bearer ${PAT_TOKEN}`,
|
|
42
|
+
'Content-Type': 'application/json',
|
|
43
|
+
},
|
|
44
|
+
validateStatus: () => true, // Accept any status code
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
console.log(`\nš API Response Status: ${response.status}`);
|
|
48
|
+
|
|
49
|
+
if (response.status === 200) {
|
|
50
|
+
console.log('ā
API connection successful!');
|
|
51
|
+
console.log('\nš Your MCP server is configured correctly!');
|
|
52
|
+
console.log('\nNext steps:');
|
|
53
|
+
console.log('1. Start the MCP server: npm start');
|
|
54
|
+
console.log('2. Configure Claude Desktop with the config from claude_desktop_config.example.json');
|
|
55
|
+
console.log('3. Restart Claude Desktop');
|
|
56
|
+
console.log('4. Ask Claude to get testcases for your project');
|
|
57
|
+
} else if (response.status === 401) {
|
|
58
|
+
console.log('ā ļø Authentication failed - PAT token may be invalid, revoked, or expired');
|
|
59
|
+
console.log(' Please check:');
|
|
60
|
+
console.log(' - Token is correct and starts with xyz_');
|
|
61
|
+
console.log(' - Token has not been revoked in TestKase');
|
|
62
|
+
console.log(' - Token has not expired');
|
|
63
|
+
} else if (response.status === 404) {
|
|
64
|
+
console.log('ā ļø Test project not found (this is expected if you used a test project ID)');
|
|
65
|
+
console.log('ā
But authentication worked! Your PAT token is valid.');
|
|
66
|
+
console.log('\nš Your MCP server is configured correctly!');
|
|
67
|
+
} else {
|
|
68
|
+
console.log(`ā ļø Unexpected response: ${response.status}`);
|
|
69
|
+
console.log(` Response: ${JSON.stringify(response.data, null, 2)}`);
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (axios.isAxiosError(error)) {
|
|
73
|
+
if (error.code === 'ECONNREFUSED') {
|
|
74
|
+
console.log('ā Cannot connect to API server');
|
|
75
|
+
console.log(` Make sure TestKase backend is running at ${API_BASE_URL}`);
|
|
76
|
+
} else {
|
|
77
|
+
console.log('ā API request failed:', error.message);
|
|
78
|
+
if (error.response) {
|
|
79
|
+
console.log(` Status: ${error.response.status}`);
|
|
80
|
+
console.log(` Response: ${JSON.stringify(error.response.data, null, 2)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
console.log('ā Unexpected error:', error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
testApiConnection();
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"outDir": "./build",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "build"]
|
|
19
|
+
}
|