diff --git a/src/server.test.ts b/src/server.test.ts index 59bcc5d37..8979b831f 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -632,7 +632,13 @@ test("server shutdown waits for an active MCP tool call", async (t) => { assert.equal(shutdownFinished, false); await writeFile(join(root, "release"), ""); - await toolCall; + const response = await toolCall; + assert.equal(response.status, 200, await response.clone().text()); + const responseBody = await response.json() as { + result?: { structuredContent?: { exit_code?: number; running?: boolean } }; + }; + assert.equal(responseBody.result?.structuredContent?.exit_code, 0); + assert.equal(responseBody.result?.structuredContent?.running, false); await shutdown; assert.equal(shutdownFinished, true); }); diff --git a/src/server.ts b/src/server.ts index 0a2792f3f..58496d27a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -966,6 +966,12 @@ export function createServer( localAgentProviders, close: () => { closePromise ??= (async () => { + await toolActivities.waitForIdle(); + // Tool handlers resolving only starts the JSON-RPC response chain; + // it settles several microtasks later. Yield to the macrotask queue + // so in-flight responses are delivered before mcpHandler.close() + // rejects their still-open exchanges (HTTP 499). + await new Promise((resolve) => setImmediate(resolve)); try { await mcpHandler.close(); } catch (error) { @@ -973,7 +979,6 @@ export function createServer( error: error instanceof Error ? error.message : String(error), }); } - await toolActivities.waitForIdle(); processSessions.shutdown(); oauthProvider.close(); workspaceStore.close?.();