@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 ADDED
@@ -0,0 +1,206 @@
1
+ # @testkase/mcp-server
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@testkase/mcp-server.svg)](https://www.npmjs.com/package/@testkase/mcp-server)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=test-config.d.ts.map
@@ -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
+ }