@testledger/mcp 0.0.1 → 0.0.2

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +54 -104
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Test Reporter MCP Server
2
2
 
3
- MCP (Model Context Protocol) server for [Test Ledger](https://testledger.dev) that enables Claude Code to analyze flaky tests, find failure patterns, and suggest fixes.
3
+ MCP (Model Context Protocol) server for [Test Ledger](https://testledger.dev) that enables Claude Code to analyze flaky tests, find failure patterns, suggest fixes and more.
4
4
 
5
5
  ## Installation
6
6
 
package/dist/index.js CHANGED
@@ -3,33 +3,49 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
5
  // Configuration from environment
6
- const API_BASE_URL = process.env.TEST_REPORTER_API_URL;
7
- const API_KEY = process.env.TEST_REPORTER_API_KEY || "";
8
- const DEFAULT_PROJECT_ID = process.env.TEST_REPORTER_PROJECT_ID;
9
- // Helper to make API calls
6
+ const API_BASE_URL = process.env.TEST_LEDGER_API_URL;
7
+ const API_KEY = process.env.TEST_LEDGER_API_KEY || "";
8
+ const DEFAULT_PROJECT_ID = process.env.TEST_LEDGER_PROJECT_ID;
9
+ // API request timeout (25s to stay under 30s gateway limit)
10
+ const API_TIMEOUT_MS = 25000;
11
+ // Helper to make API calls with timeout
10
12
  async function apiCall(endpoint, params = {}) {
11
- const url = new URL(endpoint, API_BASE_URL);
12
- // Inject default project ID if not provided
13
- if (DEFAULT_PROJECT_ID && !params.project_id) {
14
- params.project_id = DEFAULT_PROJECT_ID;
13
+ const controller = new AbortController();
14
+ const timeout = setTimeout(() => controller.abort(), API_TIMEOUT_MS);
15
+ try {
16
+ const url = new URL(endpoint, API_BASE_URL);
17
+ // Inject default project ID if not provided
18
+ if (DEFAULT_PROJECT_ID && !params.project_id) {
19
+ params.project_id = DEFAULT_PROJECT_ID;
20
+ }
21
+ // Add query params
22
+ Object.entries(params).forEach(([key, value]) => {
23
+ if (value !== undefined && value !== null) {
24
+ url.searchParams.append(key, String(value));
25
+ }
26
+ });
27
+ const response = await fetch(url.toString(), {
28
+ headers: {
29
+ "Authorization": `Bearer ${API_KEY}`,
30
+ "Content-Type": "application/json",
31
+ },
32
+ signal: controller.signal,
33
+ });
34
+ if (!response.ok) {
35
+ const error = await response.text();
36
+ throw new Error(`API error ${response.status}: ${error}`);
37
+ }
38
+ return response.json();
15
39
  }
16
- // Add query params
17
- Object.entries(params).forEach(([key, value]) => {
18
- if (value !== undefined && value !== null) {
19
- url.searchParams.append(key, String(value));
40
+ catch (error) {
41
+ if (error instanceof Error && error.name === 'AbortError') {
42
+ throw new Error(`Request timeout after ${API_TIMEOUT_MS / 1000}s. Try reducing 'days' or 'limit' parameters.`);
20
43
  }
21
- });
22
- const response = await fetch(url.toString(), {
23
- headers: {
24
- "Authorization": `Bearer ${API_KEY}`,
25
- "Content-Type": "application/json",
26
- },
27
- });
28
- if (!response.ok) {
29
- const error = await response.text();
30
- throw new Error(`API error ${response.status}: ${error}`);
44
+ throw error;
45
+ }
46
+ finally {
47
+ clearTimeout(timeout);
31
48
  }
32
- return response.json();
33
49
  }
34
50
  // Define the tools
35
51
  const tools = [
@@ -60,38 +76,6 @@ const tools = [
60
76
  required: ["spec_file"],
61
77
  },
62
78
  },
63
- {
64
- name: "get_test_errors",
65
- description: "Get error messages and stacktraces for a test's failures, grouped by unique error. Use this to see what errors are occurring and how often.",
66
- inputSchema: {
67
- type: "object",
68
- properties: {
69
- spec_file: {
70
- type: "string",
71
- description: "The spec file path",
72
- },
73
- test_title: {
74
- type: "string",
75
- description: "Specific test title (optional)",
76
- },
77
- project_id: {
78
- type: "number",
79
- description: "Project ID to filter by (optional)",
80
- },
81
- days: {
82
- type: "number",
83
- description: "Days to look back (default: 30)",
84
- default: 30,
85
- },
86
- limit: {
87
- type: "number",
88
- description: "Maximum number of unique errors to return (default: 20)",
89
- default: 20,
90
- },
91
- },
92
- required: ["spec_file"],
93
- },
94
- },
95
79
  {
96
80
  name: "get_failure_patterns",
97
81
  description: "Analyze when and how tests fail to identify patterns. Returns failure rates by hour, day of week, version, browser/site, and duration analysis.",
@@ -153,7 +137,7 @@ const tools = [
153
137
  },
154
138
  {
155
139
  name: "get_flaky_tests",
156
- description: "Get a list of flaky tests (tests that fail then pass on retry) across the project, sorted by flakiness rate.",
140
+ description: "Get a list of flaky tests (tests that fail then pass on retry) across the project, sorted by flakiness rate. Note: This scans all tests - use smaller 'days' values for faster results.",
157
141
  inputSchema: {
158
142
  type: "object",
159
143
  properties: {
@@ -163,8 +147,8 @@ const tools = [
163
147
  },
164
148
  days: {
165
149
  type: "number",
166
- description: "Days to look back (default: 30)",
167
- default: 30,
150
+ description: "Days to look back (default: 3). Use smaller values for faster results.",
151
+ default: 3,
168
152
  },
169
153
  min_flaky_rate: {
170
154
  type: "number",
@@ -173,15 +157,15 @@ const tools = [
173
157
  },
174
158
  limit: {
175
159
  type: "number",
176
- description: "Maximum results to return (default: 50)",
177
- default: 50,
160
+ description: "Maximum results to return (default: 20)",
161
+ default: 20,
178
162
  },
179
163
  },
180
164
  },
181
165
  },
182
166
  {
183
167
  name: "get_recent_failures",
184
- description: "Get the most recent test failures for quick triage. Useful for seeing what's currently broken.",
168
+ description: "Get the most recent test failures for quick triage. Useful for seeing what's currently broken. For faster results, provide a spec_file filter.",
185
169
  inputSchema: {
186
170
  type: "object",
187
171
  properties: {
@@ -191,7 +175,7 @@ const tools = [
191
175
  },
192
176
  spec_file: {
193
177
  type: "string",
194
- description: "Filter by spec file (optional)",
178
+ description: "Filter by spec file (recommended for faster results)",
195
179
  },
196
180
  hours: {
197
181
  type: "number",
@@ -200,8 +184,8 @@ const tools = [
200
184
  },
201
185
  limit: {
202
186
  type: "number",
203
- description: "Maximum results (default: 50)",
204
- default: 50,
187
+ description: "Maximum results (default: 20)",
188
+ default: 20,
205
189
  },
206
190
  },
207
191
  },
@@ -239,34 +223,6 @@ const tools = [
239
223
  required: ["spec_file"],
240
224
  },
241
225
  },
242
- {
243
- name: "search_errors",
244
- description: "Full-text search across error messages and stacktraces. Use this to find all tests affected by a specific type of error.",
245
- inputSchema: {
246
- type: "object",
247
- properties: {
248
- query: {
249
- type: "string",
250
- description: "Search term (e.g., 'timeout', 'element not found', 'ECONNREFUSED')",
251
- },
252
- project_id: {
253
- type: "number",
254
- description: "Project ID to filter by (optional)",
255
- },
256
- days: {
257
- type: "number",
258
- description: "Days to look back (default: 30)",
259
- default: 30,
260
- },
261
- limit: {
262
- type: "number",
263
- description: "Maximum results (default: 50)",
264
- default: 50,
265
- },
266
- },
267
- required: ["query"],
268
- },
269
- },
270
226
  ];
271
227
  // Create the server
272
228
  const server = new Server({
@@ -288,28 +244,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
288
244
  let result;
289
245
  switch (name) {
290
246
  case "get_test_history":
291
- result = await apiCall("/api/tests/history", args);
292
- break;
293
- case "get_test_errors":
294
- result = await apiCall("/api/tests/errors", args);
247
+ result = await apiCall("/tests/history", args);
295
248
  break;
296
249
  case "get_failure_patterns":
297
- result = await apiCall("/api/tests/patterns", args);
250
+ result = await apiCall("/tests/patterns", args);
298
251
  break;
299
252
  case "get_correlated_failures":
300
- result = await apiCall("/api/tests/correlations", args);
253
+ result = await apiCall("/tests/correlations", args);
301
254
  break;
302
255
  case "get_flaky_tests":
303
- result = await apiCall("/api/tests/flaky", args);
256
+ result = await apiCall("/tests/flaky", args);
304
257
  break;
305
258
  case "get_recent_failures":
306
- result = await apiCall("/api/tests/recent-failures", args);
259
+ result = await apiCall("/tests/recent-failures", args);
307
260
  break;
308
261
  case "get_test_trend":
309
- result = await apiCall("/api/tests/trend", args);
310
- break;
311
- case "search_errors":
312
- result = await apiCall("/api/tests/search-errors", args);
262
+ result = await apiCall("/tests/trend", args);
313
263
  break;
314
264
  default:
315
265
  throw new Error(`Unknown tool: ${name}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testledger/mcp",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "MCP server for Test Ledger - analyze flaky tests with Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",