testchimp-mcp-client 0.0.2 → 0.0.4

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 CHANGED
@@ -17,6 +17,15 @@ MCP (Model Context Protocol) server for [TestChimp](https://testchimp.io). Expos
17
17
  - **`create_test_scenario`** — POST `/api/mcp/create_test_scenario` (`platformFilePath` under `plans/scenarios/...`, `title`, `userStoryOrdinalId`).
18
18
  - **`update_user_story`** — POST `/api/mcp/update_user_story` (full markdown `content` with `id: US-...` in frontmatter).
19
19
  - **`update_test_scenario`** — POST `/api/mcp/update_test_scenario` (full markdown `content` with `id: TS-...` and `story: US-...` in frontmatter).
20
+ - **`get_eaas_config`** — POST `/api/mcp/get_eaas_config` (BunnyShell YAML path and project name; token excluded; `{}` when unconfigured).
21
+ - **`get_branch_specific_endpoint_config`** — POST `/api/mcp/get_branch_specific_endpoint_config` (optional `branchName`). Resolves `BASE_URL` from Branch Management (template and per-branch overrides). Use when EaaS is not used and preview URLs are configured in TestChimp. Returns `baseUrl` and `resolution` (`override` \| `template` \| `none`).
22
+ - **`provision_ephemeral_environment_and_wait`** — POST `/api/mcp/provision_ephemeral_environment` then poll `/api/mcp/get_ephemeral_environment_status` until deployed with component URLs, or return `failed` / `timeout` with `failure_phase` and `message` (optional `branchName`, `pollIntervalSeconds`, `maxWaitMinutes`). **Prefer this** for agents.
23
+ - **`provision_ephemeral_environment`** — POST `/api/mcp/provision_ephemeral_environment` (optional `branchName`). Use when manually polling or as fallback if the wait tool is unavailable.
24
+ - **`get_ephemeral_environment_status`** — POST `/api/mcp/get_ephemeral_environment_status` (`bnsEnvironmentId`).
25
+ - **`destroy_ephemeral_environment`** — POST `/api/mcp/destroy_ephemeral_environment` (`bnsEnvironmentId`).
26
+ - **`list_bunnyshell_environment_events`** — POST `/api/mcp/list_bunnyshell_environment_events` (proxies BunnyShell; response body is the API payload as returned).
27
+ - **`list_bunnyshell_workflow_jobs`** — POST `/api/mcp/list_bunnyshell_workflow_jobs` (same: raw BunnyShell response body).
28
+ - **`get_bunnyshell_workflow_job_logs`** — POST `/api/mcp/get_bunnyshell_workflow_job_logs` (raw logs response body from BunnyShell).
20
29
 
21
30
  ## Cursor
22
31
 
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Poll TestChimp MCP EaaS endpoints until BunnyShell reports deployed + component URLs,
3
+ * or a terminal failure / timeout. See plan: provision_ephemeral_environment_and_wait.
4
+ */
5
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ export interface ProvisionWaitArgs {
7
+ branchName?: string;
8
+ pollIntervalSeconds?: number;
9
+ maxWaitMinutes?: number;
10
+ }
11
+ export declare function runProvisionEphemeralEnvironmentAndWait(postMcp: (path: string, body: unknown) => Promise<string>, server: McpServer, args: ProvisionWaitArgs): Promise<string>;
@@ -0,0 +1,206 @@
1
+ function sleep(ms) {
2
+ return new Promise((resolve) => setTimeout(resolve, ms));
3
+ }
4
+ function clamp(n, min, max) {
5
+ return Math.min(max, Math.max(min, n));
6
+ }
7
+ function pickStr(obj, ...keys) {
8
+ for (const k of keys) {
9
+ const v = obj[k];
10
+ if (typeof v === "string" && v.trim() !== "")
11
+ return v;
12
+ }
13
+ return "";
14
+ }
15
+ function parseJsonObject(text) {
16
+ try {
17
+ const v = JSON.parse(text);
18
+ return v !== null && typeof v === "object" && !Array.isArray(v)
19
+ ? v
20
+ : {};
21
+ }
22
+ catch {
23
+ return {};
24
+ }
25
+ }
26
+ function componentUrlsNonEmpty(componentUrlsJson) {
27
+ const t = componentUrlsJson.trim();
28
+ if (!t)
29
+ return false;
30
+ try {
31
+ const v = JSON.parse(t);
32
+ if (v === null || typeof v !== "object" || Array.isArray(v))
33
+ return false;
34
+ return Object.keys(v).length > 0;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ /** True if BNS / aggregate status indicates we should stop polling with failure (deploy phase). */
41
+ function isTerminalDeployFailure(aggregateStatus, operationStatus, clusterStatus) {
42
+ const ag = aggregateStatus.toLowerCase().trim();
43
+ if (ag === "deployed")
44
+ return false;
45
+ const op = operationStatus.toLowerCase();
46
+ const cl = clusterStatus.toLowerCase();
47
+ if (op.includes("deploying") || op.includes("in_progress") || op.includes("in progress")) {
48
+ return false;
49
+ }
50
+ const bad = /\b(fail|failed|failure|error|deleted)\b/;
51
+ if (bad.test(op) || bad.test(cl) || bad.test(ag))
52
+ return true;
53
+ return false;
54
+ }
55
+ function lastStatusSnapshot(obj) {
56
+ return {
57
+ status: pickStr(obj, "status", "Status"),
58
+ operationStatus: pickStr(obj, "operationStatus", "operation_status"),
59
+ clusterStatus: pickStr(obj, "clusterStatus", "cluster_status"),
60
+ environmentType: pickStr(obj, "environmentType", "environment_type"),
61
+ dashboardUrl: pickStr(obj, "dashboardUrl", "dashboard_url"),
62
+ };
63
+ }
64
+ async function logInfo(server, message) {
65
+ try {
66
+ await server.sendLoggingMessage({
67
+ level: "info",
68
+ data: message,
69
+ });
70
+ }
71
+ catch {
72
+ // Client may not support logging; ignore.
73
+ }
74
+ }
75
+ export async function runProvisionEphemeralEnvironmentAndWait(postMcp, server, args) {
76
+ const pollIntervalSec = clamp(Math.round(args.pollIntervalSeconds ?? 60), 30, 120);
77
+ const maxWaitMin = clamp(Math.round(args.maxWaitMinutes ?? 25), 5, 45);
78
+ const deadline = Date.now() + maxWaitMin * 60 * 1000;
79
+ const intervalMs = pollIntervalSec * 1000;
80
+ const provisionBody = {};
81
+ if (args.branchName != null && args.branchName.trim() !== "") {
82
+ provisionBody.branchName = args.branchName.trim();
83
+ }
84
+ let bnsEnvironmentId = "";
85
+ let branch = "";
86
+ let provisionRaw = "";
87
+ try {
88
+ provisionRaw = await postMcp("/api/mcp/provision_ephemeral_environment", provisionBody);
89
+ }
90
+ catch (e) {
91
+ const errMsg = e instanceof Error ? e.message : String(e);
92
+ return JSON.stringify({
93
+ outcome: "failed",
94
+ failure_phase: "provision",
95
+ message: `Could not start ephemeral environment (create/deploy trigger failed). ${errMsg}`,
96
+ bns_environment_id: "",
97
+ branch: "",
98
+ component_urls_json: "",
99
+ last_status: {},
100
+ });
101
+ }
102
+ const prov = parseJsonObject(provisionRaw);
103
+ bnsEnvironmentId = pickStr(prov, "bnsEnvironmentId", "bns_environment_id");
104
+ branch = pickStr(prov, "branch", "branch_name");
105
+ if (!bnsEnvironmentId) {
106
+ return JSON.stringify({
107
+ outcome: "failed",
108
+ failure_phase: "provision",
109
+ message: "Provision response did not include bns_environment_id. Check BunnyShell + GitHub integration and project EaaS settings.",
110
+ bns_environment_id: "",
111
+ branch,
112
+ component_urls_json: "",
113
+ last_status: prov,
114
+ });
115
+ }
116
+ await logInfo(server, `Ephemeral env provision started (bns=${bnsEnvironmentId}, branch=${branch || "?"}). Polling up to ${maxWaitMin} min, every ${pollIntervalSec}s.`);
117
+ let pollIndex = 0;
118
+ let lastSnapshot = {};
119
+ let lastComponentUrlsJson = "";
120
+ let lastDashboardUrl = "";
121
+ while (Date.now() < deadline) {
122
+ pollIndex += 1;
123
+ let statusRaw;
124
+ try {
125
+ statusRaw = await postMcp("/api/mcp/get_ephemeral_environment_status", {
126
+ bnsEnvironmentId,
127
+ });
128
+ }
129
+ catch (e) {
130
+ const errMsg = e instanceof Error ? e.message : String(e);
131
+ return JSON.stringify({
132
+ outcome: "failed",
133
+ failure_phase: "deploy",
134
+ message: `Status check failed while waiting for deployment: ${errMsg}`,
135
+ bns_environment_id: bnsEnvironmentId,
136
+ branch,
137
+ component_urls_json: "",
138
+ last_status: lastSnapshot,
139
+ });
140
+ }
141
+ const st = parseJsonObject(statusRaw);
142
+ lastSnapshot = lastStatusSnapshot(st);
143
+ const aggregateStatus = pickStr(st, "status", "Status");
144
+ const operationStatus = pickStr(st, "operationStatus", "operation_status");
145
+ const clusterStatus = pickStr(st, "clusterStatus", "cluster_status");
146
+ const componentUrlsJson = pickStr(st, "componentUrlsJson", "component_urls_json");
147
+ const dashboardUrl = pickStr(st, "dashboardUrl", "dashboard_url");
148
+ lastComponentUrlsJson = componentUrlsJson;
149
+ lastDashboardUrl = dashboardUrl;
150
+ await logInfo(server, `Ephemeral env poll ${pollIndex}: status=${aggregateStatus} op=${operationStatus} cluster=${clusterStatus} bns=${bnsEnvironmentId}`);
151
+ const deployed = aggregateStatus.toLowerCase() === "deployed";
152
+ const urlsOk = componentUrlsNonEmpty(componentUrlsJson);
153
+ if (deployed && urlsOk) {
154
+ return JSON.stringify({
155
+ outcome: "success",
156
+ message: "Ephemeral environment is deployed and component URLs are available.",
157
+ bns_environment_id: bnsEnvironmentId,
158
+ branch,
159
+ dashboard_url: dashboardUrl,
160
+ component_urls_json: componentUrlsJson,
161
+ ready_for_seeding: true,
162
+ last_status: lastSnapshot,
163
+ });
164
+ }
165
+ if (deployed && !urlsOk) {
166
+ return JSON.stringify({
167
+ outcome: "success",
168
+ message: "Environment reports deployed but component URL map is empty or missing; check BunnyShell definition or dashboard.",
169
+ warnings: ["component_urls_json_empty_or_unparsed"],
170
+ bns_environment_id: bnsEnvironmentId,
171
+ branch,
172
+ dashboard_url: dashboardUrl,
173
+ component_urls_json: componentUrlsJson,
174
+ ready_for_seeding: false,
175
+ last_status: lastSnapshot,
176
+ });
177
+ }
178
+ if (isTerminalDeployFailure(aggregateStatus, operationStatus, clusterStatus)) {
179
+ return JSON.stringify({
180
+ outcome: "failed",
181
+ failure_phase: "deploy",
182
+ message: `BunnyShell deployment did not succeed (status=${aggregateStatus}, operation=${operationStatus}, cluster=${clusterStatus}).`,
183
+ bns_environment_id: bnsEnvironmentId,
184
+ branch,
185
+ dashboard_url: dashboardUrl,
186
+ component_urls_json: componentUrlsJson,
187
+ last_status: lastSnapshot,
188
+ });
189
+ }
190
+ const remainingMs = deadline - Date.now();
191
+ if (remainingMs <= 0)
192
+ break;
193
+ const sleepMs = Math.min(intervalMs, remainingMs);
194
+ await sleep(sleepMs);
195
+ }
196
+ return JSON.stringify({
197
+ outcome: "timeout",
198
+ failure_phase: "wait",
199
+ message: `Timed out after ${maxWaitMin} minutes waiting for ephemeral environment ${bnsEnvironmentId} to become ready.`,
200
+ bns_environment_id: bnsEnvironmentId,
201
+ branch,
202
+ dashboard_url: lastDashboardUrl,
203
+ component_urls_json: lastComponentUrlsJson,
204
+ last_status: lastSnapshot,
205
+ });
206
+ }
package/dist/index.js CHANGED
@@ -2,10 +2,12 @@
2
2
  /**
3
3
  * TestChimp MCP server — calls TestChimp /api/mcp/* with TestChimp-Api-Key only.
4
4
  * Env: TESTCHIMP_BACKEND_URL (optional), TESTCHIMP_API_KEY (required).
5
+ * Includes SmartTests coverage, plan authoring, EaaS, and TrueCoverage analytics endpoints.
5
6
  */
6
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
9
  import { z } from "zod";
10
+ import { runProvisionEphemeralEnvironmentAndWait } from "./ephemeralProvisionWait.js";
9
11
  const DEFAULT_BACKEND = "https://featureservice.testchimp.io";
10
12
  function getBackendUrl() {
11
13
  const raw = process.env.TESTCHIMP_BACKEND_URL?.trim();
@@ -74,6 +76,48 @@ const updatePlanMarkdownInput = z.object({
74
76
  /** Full markdown including YAML frontmatter and body (as written under the repo plans root). */
75
77
  content: z.string().min(1),
76
78
  });
79
+ const emptyInput = z.object({});
80
+ const getBranchSpecificEndpointConfigInput = z.object({
81
+ /** Git branch name (e.g. PR head). Required to resolve template or per-branch override. */
82
+ branchName: z.string().optional(),
83
+ });
84
+ /** Proto-shaped JSON for TrueCoverage (e.g. list_events: baseExecutionScope, comparisonExecutionScope). */
85
+ const truecoverageJsonInput = z.record(z.string(), z.unknown());
86
+ const eventMetadataKeysInput = z.object({
87
+ eventTitle: z.string().min(1),
88
+ });
89
+ const provisionEphemeralInput = z.object({
90
+ /** Git branch to deploy; omit to use the repo default branch. */
91
+ branchName: z.string().optional(),
92
+ });
93
+ const bnsEnvironmentIdInput = z.object({
94
+ /** BunnyShell environment id returned from provision_ephemeral_environment. */
95
+ bnsEnvironmentId: z.string().min(1),
96
+ });
97
+ const provisionEphemeralWaitInput = z.object({
98
+ /** Git branch to deploy; omit to use the repo default branch. */
99
+ branchName: z.string().optional(),
100
+ /** Seconds between status polls (clamped 30–120). Default 60. */
101
+ pollIntervalSeconds: z.number().optional(),
102
+ /** Max minutes to wait for deployed + URLs (clamped 5–45). Default 25. */
103
+ maxWaitMinutes: z.number().optional(),
104
+ });
105
+ const listBunnyshellEnvironmentEventsInput = z.object({
106
+ bnsEnvironmentId: z.string().min(1),
107
+ /** BunnyShell event type filter (e.g. env_deploy). */
108
+ eventType: z.string().optional(),
109
+ /** BunnyShell event status: new | in_progress | success | fail */
110
+ eventStatus: z.string().optional(),
111
+ page: z.number().int().positive().optional(),
112
+ });
113
+ const listBunnyshellWorkflowJobsInput = z.object({
114
+ bnsEnvironmentId: z.string().min(1),
115
+ page: z.number().int().positive().optional(),
116
+ });
117
+ const getBunnyshellWorkflowJobLogsInput = z.object({
118
+ bnsEnvironmentId: z.string().min(1),
119
+ workflowJobId: z.string().min(1),
120
+ });
77
121
  function textResult(json) {
78
122
  return {
79
123
  content: [{ type: "text", text: json }],
@@ -98,7 +142,7 @@ function normalizeScope(scope) {
98
142
  return out;
99
143
  }
100
144
  async function main() {
101
- const server = new McpServer({ name: "testchimp-mcp", version: "0.1.0" }, { capabilities: { tools: {} } });
145
+ const server = new McpServer({ name: "testchimp-mcp", version: "0.0.8" }, { capabilities: { tools: {}, logging: {} } });
102
146
  server.registerTool("get_requirement_coverage", {
103
147
  description: "Fetch requirement (scenario) coverage under an optional platform-rooted folder scope (tests/... or plans/...). " +
104
148
  "Use branchName (Git branch) and scope.filePaths (paths under platform tests root) rather than internal ids. " +
@@ -183,6 +227,172 @@ async function main() {
183
227
  const json = await postMcp("/api/mcp/update_test_scenario", { content: args.content });
184
228
  return textResult(json);
185
229
  });
230
+ server.registerTool("get_eaas_config", {
231
+ description: "Return the project's BunnyShell (Environment-as-a-Service) settings: ymlRepoPath and bunnyshellProjectName. " +
232
+ "Secrets (API token) are never returned. Response is {} when EaaS is not configured or has no public fields.",
233
+ inputSchema: emptyInput,
234
+ }, async () => {
235
+ const json = await postMcp("/api/mcp/get_eaas_config", {});
236
+ return textResult(json);
237
+ });
238
+ server.registerTool("get_branch_specific_endpoint_config", {
239
+ description: "Resolve BASE_URL for a Git branch from TestChimp Branch Management (URL template and per-branch overrides). " +
240
+ "Prefer this when BunnyShell EaaS is not configured and the project uses bespoke PR preview URLs. " +
241
+ "Pass branchName (e.g. the PR branch). Response includes baseUrl and resolution: override | template | none.",
242
+ inputSchema: getBranchSpecificEndpointConfigInput,
243
+ }, async (args) => {
244
+ const body = {};
245
+ if (args.branchName != null && args.branchName.trim() !== "") {
246
+ body.branchName = args.branchName.trim();
247
+ }
248
+ const json = await postMcp("/api/mcp/get_branch_specific_endpoint_config", body);
249
+ return textResult(json);
250
+ });
251
+ server.registerTool("provision_ephemeral_environment_and_wait", {
252
+ description: "Preferred: provision a BunnyShell ephemeral environment for the current Git branch, then poll until the stack is deployed and component URLs are available (typically ~5–10 minutes). " +
253
+ "Returns one JSON with outcome success|failed|timeout, failure_phase (provision|deploy|wait), user-facing message, and component_urls_json on success. " +
254
+ "Requires BunnyShell + GitHub integration. If this tool is unavailable or the host aborts long calls, fall back to provision_ephemeral_environment + polling get_ephemeral_environment_status (~1/min, max ~25m).",
255
+ inputSchema: provisionEphemeralWaitInput,
256
+ }, async (args) => {
257
+ const json = await runProvisionEphemeralEnvironmentAndWait(postMcp, server, {
258
+ branchName: args.branchName,
259
+ pollIntervalSeconds: args.pollIntervalSeconds,
260
+ maxWaitMinutes: args.maxWaitMinutes,
261
+ });
262
+ return textResult(json);
263
+ });
264
+ server.registerTool("provision_ephemeral_environment", {
265
+ description: "Create a BunnyShell ephemeral environment (create + deploy trigger only). Prefer provision_ephemeral_environment_and_wait unless you must poll manually. " +
266
+ "Requires BunnyShell + GitHub integration. Use get_ephemeral_environment_status with bnsEnvironmentId to poll until deployed.",
267
+ inputSchema: provisionEphemeralInput,
268
+ }, async (args) => {
269
+ const body = {};
270
+ if (args.branchName != null && args.branchName.trim() !== "") {
271
+ body.branchName = args.branchName.trim();
272
+ }
273
+ const json = await postMcp("/api/mcp/provision_ephemeral_environment", body);
274
+ return textResult(json);
275
+ });
276
+ server.registerTool("get_ephemeral_environment_status", {
277
+ description: "Poll BunnyShell for environment status and component_urls_json. Used for manual fallback when provision_ephemeral_environment_and_wait is not available; prefer the wait tool for normal flows.",
278
+ inputSchema: bnsEnvironmentIdInput,
279
+ }, async (args) => {
280
+ const json = await postMcp("/api/mcp/get_ephemeral_environment_status", {
281
+ bnsEnvironmentId: args.bnsEnvironmentId,
282
+ });
283
+ return textResult(json);
284
+ });
285
+ server.registerTool("destroy_ephemeral_environment", {
286
+ description: "Delete a BunnyShell environment created for this project (bnsEnvironmentId from provision).",
287
+ inputSchema: bnsEnvironmentIdInput,
288
+ }, async (args) => {
289
+ const json = await postMcp("/api/mcp/destroy_ephemeral_environment", {
290
+ bnsEnvironmentId: args.bnsEnvironmentId,
291
+ });
292
+ return textResult(json);
293
+ });
294
+ server.registerTool("list_bunnyshell_environment_events", {
295
+ description: "Troubleshooting: list BunnyShell platform events for an environment (GET /v1/events). " +
296
+ "Use after a failed or stuck ephemeral deploy. The HTTP response body is the BunnyShell payload as returned by the API (no extra wrapping). " +
297
+ "Optional filters: eventType, eventStatus (new|in_progress|success|fail), page.",
298
+ inputSchema: listBunnyshellEnvironmentEventsInput,
299
+ }, async (args) => {
300
+ const body = { bnsEnvironmentId: args.bnsEnvironmentId };
301
+ if (args.eventType != null && args.eventType.trim() !== "")
302
+ body.eventType = args.eventType.trim();
303
+ if (args.eventStatus != null && args.eventStatus.trim() !== "")
304
+ body.eventStatus = args.eventStatus.trim();
305
+ if (args.page != null)
306
+ body.page = args.page;
307
+ const json = await postMcp("/api/mcp/list_bunnyshell_environment_events", body);
308
+ return textResult(json);
309
+ });
310
+ server.registerTool("list_bunnyshell_workflow_jobs", {
311
+ description: "Troubleshooting: list BunnyShell workflow jobs for an environment. Response body is the BunnyShell API payload as returned (no extra wrapping). " +
312
+ "Use to find workflowJobId for get_bunnyshell_workflow_job_logs.",
313
+ inputSchema: listBunnyshellWorkflowJobsInput,
314
+ }, async (args) => {
315
+ const body = { bnsEnvironmentId: args.bnsEnvironmentId };
316
+ if (args.page != null)
317
+ body.page = args.page;
318
+ const json = await postMcp("/api/mcp/list_bunnyshell_workflow_jobs", body);
319
+ return textResult(json);
320
+ });
321
+ server.registerTool("get_bunnyshell_workflow_job_logs", {
322
+ description: "Troubleshooting: fetch logs for a BunnyShell workflow job (deploy/build pipeline). " +
323
+ "Response body is the BunnyShell /v1/workflow_jobs/{id}/logs payload as returned (no truncation or wrapping).",
324
+ inputSchema: getBunnyshellWorkflowJobLogsInput,
325
+ }, async (args) => {
326
+ const json = await postMcp("/api/mcp/get_bunnyshell_workflow_job_logs", {
327
+ bnsEnvironmentId: args.bnsEnvironmentId,
328
+ workflowJobId: args.workflowJobId,
329
+ });
330
+ return textResult(json);
331
+ });
332
+ server.registerTool("list_rum_environments", {
333
+ description: "List distinct RUM environment tags seen for this project (for TrueCoverage scoping). " +
334
+ "Maps to POST /api/mcp/list_rum_environments.",
335
+ inputSchema: emptyInput,
336
+ }, async () => {
337
+ const json = await postMcp("/api/mcp/list_rum_environments", {});
338
+ return textResult(json);
339
+ });
340
+ server.registerTool("get_truecoverage_events", {
341
+ description: "TrueCoverage event funnel summaries for the given execution scopes (same JSON body as platform list_events). " +
342
+ "Maps to POST /api/mcp/truecoverage_list_events.",
343
+ inputSchema: truecoverageJsonInput,
344
+ }, async (args) => {
345
+ const json = await postMcp("/api/mcp/truecoverage_list_events", args ?? {});
346
+ return textResult(json);
347
+ });
348
+ server.registerTool("get_truecoverage_event_details", {
349
+ description: "TrueCoverage drill-down for one event title (GetEventDetailsRequest JSON). " +
350
+ "Maps to POST /api/mcp/truecoverage_event_details.",
351
+ inputSchema: truecoverageJsonInput,
352
+ }, async (args) => {
353
+ const json = await postMcp("/api/mcp/truecoverage_event_details", args ?? {});
354
+ return textResult(json);
355
+ });
356
+ server.registerTool("get_truecoverage_child_event_tree", {
357
+ description: "TrueCoverage next-event tree for an event (ListChildEventTreeRequest JSON). " +
358
+ "Maps to POST /api/mcp/truecoverage_list_child_event_tree.",
359
+ inputSchema: truecoverageJsonInput,
360
+ }, async (args) => {
361
+ const json = await postMcp("/api/mcp/truecoverage_list_child_event_tree", args ?? {});
362
+ return textResult(json);
363
+ });
364
+ server.registerTool("get_truecoverage_event_transition", {
365
+ description: "TrueCoverage detailed transition summary between events (GetDetailedEventTransitionSummaryRequest JSON). " +
366
+ "Maps to POST /api/mcp/truecoverage_detailed_event_transition.",
367
+ inputSchema: truecoverageJsonInput,
368
+ }, async (args) => {
369
+ const json = await postMcp("/api/mcp/truecoverage_detailed_event_transition", args ?? {});
370
+ return textResult(json);
371
+ });
372
+ server.registerTool("get_truecoverage_event_time_series", {
373
+ description: "TrueCoverage time series for sessions or metrics (EventTimeSeriesRequest JSON). " +
374
+ "Maps to POST /api/mcp/truecoverage_event_time_series.",
375
+ inputSchema: truecoverageJsonInput,
376
+ }, async (args) => {
377
+ const json = await postMcp("/api/mcp/truecoverage_event_time_series", args ?? {});
378
+ return textResult(json);
379
+ });
380
+ server.registerTool("get_truecoverage_session_metadata_keys", {
381
+ description: "List session-level metadata keys observed for TrueCoverage. Maps to POST /api/mcp/truecoverage_session_metadata_keys.",
382
+ inputSchema: emptyInput,
383
+ }, async () => {
384
+ const json = await postMcp("/api/mcp/truecoverage_session_metadata_keys", {});
385
+ return textResult(json);
386
+ });
387
+ server.registerTool("get_truecoverage_event_metadata_keys", {
388
+ description: "List metadata keys for a given event title. Maps to POST /api/mcp/truecoverage_event_metadata_keys.",
389
+ inputSchema: eventMetadataKeysInput,
390
+ }, async (args) => {
391
+ const json = await postMcp("/api/mcp/truecoverage_event_metadata_keys", {
392
+ eventTitle: args.eventTitle,
393
+ });
394
+ return textResult(json);
395
+ });
186
396
  const transport = new StdioServerTransport();
187
397
  await server.connect(transport);
188
398
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testchimp-mcp-client",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "MCP server for TestChimp — coverage, execution history, and plan authoring",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@modelcontextprotocol/sdk": "^1.29.0",
25
- "zod": "^3.23.8"
25
+ "zod": "^4.3.6"
26
26
  },
27
27
  "devDependencies": {
28
- "@types/node": "^20.10.0",
29
- "typescript": "^5.3.0"
28
+ "@types/node": "^25.6.0",
29
+ "typescript": "^6.0.2"
30
30
  },
31
31
  "keywords": [
32
32
  "testchimp",