From f3d6f0925243528eb022780d1851a64821763402 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 17:35:12 +0000 Subject: [PATCH 1/2] fix(mcp): align list runs and affected-tests queries with OpenAPI Serialize action_type with form explode; use status[] and completion_state[] for project runs; add pr_id. Add get-runs query test and update changelog. --- mcp-server/CHANGELOG.md | 4 ++- .../tools/actions/list-affected-tests.test.ts | 6 ++-- .../src/tools/actions/list-affected-tests.ts | 2 +- mcp-server/src/tools/runs/get-runs.test.ts | 28 +++++++++++++++++++ mcp-server/src/tools/runs/get-runs.ts | 20 +++++++++++-- 5 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 mcp-server/src/tools/runs/get-runs.test.ts diff --git a/mcp-server/CHANGELOG.md b/mcp-server/CHANGELOG.md index 4e75f25..eb74aa4 100644 --- a/mcp-server/CHANGELOG.md +++ b/mcp-server/CHANGELOG.md @@ -8,7 +8,9 @@ ### Fixed -* `currents-list-affected-tests`: serialize `action_type` query params as `action_type[]` to match OpenAPI. +* `currents-list-affected-tests`: serialize `action_type` as repeated `action_type` query keys (OpenAPI `style: form`, `explode: true`). +* `currents-get-runs`: serialize `status` and `completion_state` as `status[]` and `completion_state[]`; add optional `pr_id` query param per OpenAPI list runs. +* MCP tools intentionally omit deprecated-only OpenAPI query parameters (for example `tag[]` on find run). * `currents-update-webhook`: require at least one updatable field so the HTTP request body is never empty (matches OpenAPI `requestBody.required: true`). * `postApi`: treat HTTP 201 and empty JSON bodies like other successful POST responses. diff --git a/mcp-server/src/tools/actions/list-affected-tests.test.ts b/mcp-server/src/tools/actions/list-affected-tests.test.ts index df963db..460b90f 100644 --- a/mcp-server/src/tools/actions/list-affected-tests.test.ts +++ b/mcp-server/src/tools/actions/list-affected-tests.test.ts @@ -9,7 +9,7 @@ describe("listAffectedTestsTool", () => { vi.clearAllMocks(); }); - it("serializes action_type as action_type[] per OpenAPI", async () => { + it("serializes action_type as repeated action_type per OpenAPI (form explode)", async () => { vi.spyOn(request, "fetchApi").mockResolvedValue({ status: "OK", data: [] }); await listAffectedTestsTool.handler({ @@ -20,10 +20,10 @@ describe("listAffectedTestsTool", () => { }); expect(request.fetchApi).toHaveBeenCalledWith( - expect.stringContaining("action_type%5B%5D=skip") + expect.stringContaining("action_type=skip") ); expect(request.fetchApi).toHaveBeenCalledWith( - expect.stringContaining("action_type%5B%5D=tag") + expect.stringContaining("action_type=tag") ); }); }); diff --git a/mcp-server/src/tools/actions/list-affected-tests.ts b/mcp-server/src/tools/actions/list-affected-tests.ts index c438242..fabd3bd 100644 --- a/mcp-server/src/tools/actions/list-affected-tests.ts +++ b/mcp-server/src/tools/actions/list-affected-tests.ts @@ -74,7 +74,7 @@ const handler = async ({ } if (action_type && action_type.length > 0) { - action_type.forEach((t) => queryParams.append("action_type[]", t)); + action_type.forEach((t) => queryParams.append("action_type", t)); } if (action_id) { diff --git a/mcp-server/src/tools/runs/get-runs.test.ts b/mcp-server/src/tools/runs/get-runs.test.ts new file mode 100644 index 0000000..5f43a83 --- /dev/null +++ b/mcp-server/src/tools/runs/get-runs.test.ts @@ -0,0 +1,28 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import * as request from "../../lib/request.js"; +import { getRunsTool } from "./get-runs.js"; + +vi.mock("../../lib/request.js"); + +describe("getRunsTool", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("serializes list runs query per OpenAPI (status[], completion_state[], pr_id)", async () => { + vi.spyOn(request, "fetchApi").mockResolvedValue({ status: "OK", data: [] }); + + await getRunsTool.handler({ + projectId: "p1", + status: ["PASSED", "FAILED"], + completion_state: ["COMPLETE"], + pr_id: "42", + }); + + const url = vi.mocked(request.fetchApi).mock.calls[0][0] as string; + expect(url).toContain("status%5B%5D=PASSED"); + expect(url).toContain("status%5B%5D=FAILED"); + expect(url).toContain("completion_state%5B%5D=COMPLETE"); + expect(url).toContain("pr_id=42"); + }); +}); diff --git a/mcp-server/src/tools/runs/get-runs.ts b/mcp-server/src/tools/runs/get-runs.ts index fbb2f56..000ec28 100644 --- a/mcp-server/src/tools/runs/get-runs.ts +++ b/mcp-server/src/tools/runs/get-runs.ts @@ -42,6 +42,15 @@ const zodSchema = z.object({ .max(200) .optional() .describe("Search runs by ciBuildId or commit message. Case-insensitive."), + pr_id: z + .string() + .min(1) + .max(128) + .regex(/^[!-~]+$/) + .optional() + .describe( + "Filter runs by normalized pull request id (meta.pr.id). Printable ASCII only, max 128 characters." + ), authors: z .array(z.string()) .optional() @@ -73,6 +82,7 @@ const handler = async ({ tags, tag_operator, search, + pr_id, authors, status, completion_state, @@ -106,16 +116,22 @@ const handler = async ({ queryParams.append("search", search); } + if (pr_id) { + queryParams.append("pr_id", pr_id); + } + if (authors && authors.length > 0) { authors.forEach((a) => queryParams.append("authors[]", a)); } if (status && status.length > 0) { - status.forEach((s) => queryParams.append("status", s)); + status.forEach((s) => queryParams.append("status[]", s)); } if (completion_state && completion_state.length > 0) { - completion_state.forEach((cs) => queryParams.append("completion_state", cs)); + completion_state.forEach((cs) => + queryParams.append("completion_state[]", cs) + ); } if (date_start) { From 3c029e7136293f4b41770a5e3b9b6ba7277bb7b2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 17:36:58 +0000 Subject: [PATCH 2/2] fix(mcp): list runs status/completion_state as repeated OpenAPI keys --- mcp-server/CHANGELOG.md | 2 +- mcp-server/src/tools/runs/get-runs.test.ts | 8 ++++---- mcp-server/src/tools/runs/get-runs.ts | 6 ++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/mcp-server/CHANGELOG.md b/mcp-server/CHANGELOG.md index eb74aa4..471ff8b 100644 --- a/mcp-server/CHANGELOG.md +++ b/mcp-server/CHANGELOG.md @@ -9,7 +9,7 @@ ### Fixed * `currents-list-affected-tests`: serialize `action_type` as repeated `action_type` query keys (OpenAPI `style: form`, `explode: true`). -* `currents-get-runs`: serialize `status` and `completion_state` as `status[]` and `completion_state[]`; add optional `pr_id` query param per OpenAPI list runs. +* `currents-get-runs`: serialize `status` and `completion_state` as repeated query keys (OpenAPI `style: form`, `explode: true`); add optional `pr_id` query param per OpenAPI list runs. * MCP tools intentionally omit deprecated-only OpenAPI query parameters (for example `tag[]` on find run). * `currents-update-webhook`: require at least one updatable field so the HTTP request body is never empty (matches OpenAPI `requestBody.required: true`). * `postApi`: treat HTTP 201 and empty JSON bodies like other successful POST responses. diff --git a/mcp-server/src/tools/runs/get-runs.test.ts b/mcp-server/src/tools/runs/get-runs.test.ts index 5f43a83..2675f2f 100644 --- a/mcp-server/src/tools/runs/get-runs.test.ts +++ b/mcp-server/src/tools/runs/get-runs.test.ts @@ -9,7 +9,7 @@ describe("getRunsTool", () => { vi.clearAllMocks(); }); - it("serializes list runs query per OpenAPI (status[], completion_state[], pr_id)", async () => { + it("serializes list runs query per OpenAPI (repeated status, completion_state, pr_id)", async () => { vi.spyOn(request, "fetchApi").mockResolvedValue({ status: "OK", data: [] }); await getRunsTool.handler({ @@ -20,9 +20,9 @@ describe("getRunsTool", () => { }); const url = vi.mocked(request.fetchApi).mock.calls[0][0] as string; - expect(url).toContain("status%5B%5D=PASSED"); - expect(url).toContain("status%5B%5D=FAILED"); - expect(url).toContain("completion_state%5B%5D=COMPLETE"); + expect(url).toContain("status=PASSED"); + expect(url).toContain("status=FAILED"); + expect(url).toContain("completion_state=COMPLETE"); expect(url).toContain("pr_id=42"); }); }); diff --git a/mcp-server/src/tools/runs/get-runs.ts b/mcp-server/src/tools/runs/get-runs.ts index 000ec28..12502be 100644 --- a/mcp-server/src/tools/runs/get-runs.ts +++ b/mcp-server/src/tools/runs/get-runs.ts @@ -125,13 +125,11 @@ const handler = async ({ } if (status && status.length > 0) { - status.forEach((s) => queryParams.append("status[]", s)); + status.forEach((s) => queryParams.append("status", s)); } if (completion_state && completion_state.length > 0) { - completion_state.forEach((cs) => - queryParams.append("completion_state[]", cs) - ); + completion_state.forEach((cs) => queryParams.append("completion_state", cs)); } if (date_start) {