testchimp-mcp-client 0.0.3 → 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
@@ -18,9 +18,14 @@ MCP (Model Context Protocol) server for [TestChimp](https://testchimp.io). Expos
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
20
  - **`get_eaas_config`** — POST `/api/mcp/get_eaas_config` (BunnyShell YAML path and project name; token excluded; `{}` when unconfigured).
21
- - **`provision_ephemeral_environment`** — POST `/api/mcp/provision_ephemeral_environment` (optional `branchName`).
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.
22
24
  - **`get_ephemeral_environment_status`** — POST `/api/mcp/get_ephemeral_environment_status` (`bnsEnvironmentId`).
23
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).
24
29
 
25
30
  ## Cursor
26
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();
@@ -75,6 +77,15 @@ const updatePlanMarkdownInput = z.object({
75
77
  content: z.string().min(1),
76
78
  });
77
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
+ });
78
89
  const provisionEphemeralInput = z.object({
79
90
  /** Git branch to deploy; omit to use the repo default branch. */
80
91
  branchName: z.string().optional(),
@@ -83,6 +94,30 @@ const bnsEnvironmentIdInput = z.object({
83
94
  /** BunnyShell environment id returned from provision_ephemeral_environment. */
84
95
  bnsEnvironmentId: z.string().min(1),
85
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
+ });
86
121
  function textResult(json) {
87
122
  return {
88
123
  content: [{ type: "text", text: json }],
@@ -107,7 +142,7 @@ function normalizeScope(scope) {
107
142
  return out;
108
143
  }
109
144
  async function main() {
110
- 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: {} } });
111
146
  server.registerTool("get_requirement_coverage", {
112
147
  description: "Fetch requirement (scenario) coverage under an optional platform-rooted folder scope (tests/... or plans/...). " +
113
148
  "Use branchName (Git branch) and scope.filePaths (paths under platform tests root) rather than internal ids. " +
@@ -200,9 +235,35 @@ async function main() {
200
235
  const json = await postMcp("/api/mcp/get_eaas_config", {});
201
236
  return textResult(json);
202
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
+ });
203
264
  server.registerTool("provision_ephemeral_environment", {
204
- description: "Create a BunnyShell ephemeral environment for the TestChimp project from the configured Git repo + YAML path. " +
205
- "Requires BunnyShell + GitHub integration in project settings. Poll with get_ephemeral_environment_status using bnsEnvironmentId.",
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.",
206
267
  inputSchema: provisionEphemeralInput,
207
268
  }, async (args) => {
208
269
  const body = {};
@@ -213,7 +274,7 @@ async function main() {
213
274
  return textResult(json);
214
275
  });
215
276
  server.registerTool("get_ephemeral_environment_status", {
216
- description: "Poll BunnyShell for environment status and definition. When deployed, environmentSpec may contain URLs/components JSON.",
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.",
217
278
  inputSchema: bnsEnvironmentIdInput,
218
279
  }, async (args) => {
219
280
  const json = await postMcp("/api/mcp/get_ephemeral_environment_status", {
@@ -230,6 +291,108 @@ async function main() {
230
291
  });
231
292
  return textResult(json);
232
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
+ });
233
396
  const transport = new StdioServerTransport();
234
397
  await server.connect(transport);
235
398
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testchimp-mcp-client",
3
- "version": "0.0.3",
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",