From f9dbae0a1047a35613054d7c73221799ccbabc7e Mon Sep 17 00:00:00 2001 From: Madraka <8898640+Madraka@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:43:02 +0300 Subject: [PATCH 1/2] test(mcp-server): await the edit in the H2 approval test The test fired the edit without awaiting it, so on a loaded runner the client's retry could reach the server before the edit was written: the publish then succeeded and the test failed (main, run after #44). The answer handler now awaits the edit, which is the scenario the test describes. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com> --- packages/mcp-server/test/e2e.test.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/mcp-server/test/e2e.test.ts b/packages/mcp-server/test/e2e.test.ts index a191cd89..212e875d 100644 --- a/packages/mcp-server/test/e2e.test.ts +++ b/packages/mcp-server/test/e2e.test.ts @@ -23,7 +23,7 @@ async function connect(opts: { store: SiteGraphStore; audit: AuditLog; elicitation: boolean; - answer?: () => Answer; + answer?: () => Answer | Promise; prompts?: string[]; confirmTokenFallback?: boolean; approvals?: ApprovalStore; @@ -45,7 +45,7 @@ async function connect(opts: { if (opts.elicitation) { client.setRequestHandler("elicitation/create", async (req) => { opts.prompts?.push(req.params.message); - const action = opts.answer?.() ?? "accept"; + const action = (await opts.answer?.()) ?? "accept"; return action === "accept" ? { action, content: { confirm: true } } : { action }; }); } @@ -281,18 +281,16 @@ describe("MCP client ↔ Cloudhal server", () => { store, audit, elicitation: true, - answer: () => { + // The edit is awaited before answering: it must land before the retry, whatever the load. + answer: async () => { if (!edited) { edited = true; - void (async () => { - const page = (await store.listPages(siteId))[0]; - if (page) await store.updatePage({ ...page, title: "Changed after the question" }); - })(); + const page = (await store.listPages(siteId))[0]; + if (page) await store.updatePage({ ...page, title: "Changed after the question" }); } return "accept"; }, }); - await new Promise((r) => setTimeout(r, 0)); const r = await client.callTool({ name: "publish", arguments: { siteId } }); expect(r.isError).toBe(true); expect((r.content[0] as { text: string }).text).toMatch(/site changed meanwhile/); From 6578101c96c61435ed03e761edfc6e957abeaaac Mon Sep 17 00:00:00 2001 From: Madraka <8898640+Madraka@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:57:19 +0300 Subject: [PATCH 2/2] test(mcp-server): type the async answer as Promise Co-Authored-By: Claude Opus 5.5 Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com> --- packages/mcp-server/test/e2e.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp-server/test/e2e.test.ts b/packages/mcp-server/test/e2e.test.ts index 212e875d..5880fb38 100644 --- a/packages/mcp-server/test/e2e.test.ts +++ b/packages/mcp-server/test/e2e.test.ts @@ -282,7 +282,7 @@ describe("MCP client ↔ Cloudhal server", () => { audit, elicitation: true, // The edit is awaited before answering: it must land before the retry, whatever the load. - answer: async () => { + answer: async (): Promise => { if (!edited) { edited = true; const page = (await store.listPages(siteId))[0];