From ee29c34bd78eb7862446b7dda07cb05af21cb43e Mon Sep 17 00:00:00 2001 From: Bob Dickinson Date: Thu, 5 Feb 2026 16:34:25 -0800 Subject: [PATCH] Fixed test failures induced by MCP SDK update (added session id generator to Streamable test server). Cleanup up some port usage to avoid potential issues (always use port 0 for servers). Added stdout/stderr to cli status assertion failures to make CLI test failures easier to debug from test output. --- cli/__tests__/helpers/assertions.ts | 16 ++++++++-- cli/__tests__/helpers/fixtures.ts | 8 ++--- cli/__tests__/helpers/test-server-http.ts | 37 ++++++++++++++--------- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/cli/__tests__/helpers/assertions.ts b/cli/__tests__/helpers/assertions.ts index e3ed9d02b..3631c00bf 100644 --- a/cli/__tests__/helpers/assertions.ts +++ b/cli/__tests__/helpers/assertions.ts @@ -1,18 +1,30 @@ import { expect } from "vitest"; import type { CliResult } from "./cli-runner.js"; +function formatCliOutput(result: CliResult): string { + const out = result.stdout?.trim() || "(empty)"; + const err = result.stderr?.trim() || "(empty)"; + return `stdout: ${out}\nstderr: ${err}`; +} + /** * Assert that CLI command succeeded (exit code 0) */ export function expectCliSuccess(result: CliResult) { - expect(result.exitCode).toBe(0); + expect( + result.exitCode, + `CLI exited with code ${result.exitCode}. ${formatCliOutput(result)}`, + ).toBe(0); } /** * Assert that CLI command failed (non-zero exit code) */ export function expectCliFailure(result: CliResult) { - expect(result.exitCode).not.toBe(0); + expect( + result.exitCode, + `CLI unexpectedly exited with code ${result.exitCode}. ${formatCliOutput(result)}`, + ).not.toBe(0); } /** diff --git a/cli/__tests__/helpers/fixtures.ts b/cli/__tests__/helpers/fixtures.ts index 5914f485c..c7f10fc7a 100644 --- a/cli/__tests__/helpers/fixtures.ts +++ b/cli/__tests__/helpers/fixtures.ts @@ -1,7 +1,7 @@ -import fs from "fs"; -import path from "path"; -import os from "os"; -import crypto from "crypto"; +import * as fs from "fs"; +import * as path from "path"; +import * as os from "os"; +import * as crypto from "crypto"; import { getTestMcpServerCommand } from "./test-server-stdio.js"; /** diff --git a/cli/__tests__/helpers/test-server-http.ts b/cli/__tests__/helpers/test-server-http.ts index 4626ef516..d5eadc3ff 100644 --- a/cli/__tests__/helpers/test-server-http.ts +++ b/cli/__tests__/helpers/test-server-http.ts @@ -6,6 +6,7 @@ import type { Request, Response } from "express"; import express from "express"; import { createServer as createHttpServer, Server as HttpServer } from "http"; import { createServer as createNetServer } from "net"; +import { randomUUID } from "crypto"; import * as z from "zod/v4"; import type { ServerConfig } from "./test-fixtures.js"; @@ -187,22 +188,24 @@ export class TestServerHttp { } /** - * Start the server with the specified transport + * Start the server with the specified transport. + * When requestedPort is omitted, uses port 0 so the OS assigns a unique port (avoids EADDRINUSE when tests run in parallel). */ async start( transport: "http" | "sse", requestedPort?: number, ): Promise { - const port = requestedPort - ? await findAvailablePort(requestedPort) - : await findAvailablePort(transport === "http" ? 3001 : 3000); - - this.url = `http://localhost:${port}`; + const port = + requestedPort !== undefined ? await findAvailablePort(requestedPort) : 0; if (transport === "http") { - return this.startHttp(port); + const actualPort = await this.startHttp(port); + this.url = `http://localhost:${actualPort}`; + return actualPort; } else { - return this.startSse(port); + const actualPort = await this.startSse(port); + this.url = `http://localhost:${actualPort}`; + return actualPort; } } @@ -213,8 +216,10 @@ export class TestServerHttp { // Create HTTP server this.httpServer = createHttpServer(app); - // Create StreamableHTTP transport - this.transport = new StreamableHTTPServerTransport({}); + // Create StreamableHTTP transport (stateful so it can handle multiple requests per session) + this.transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + }); // Set up Express route to handle MCP requests app.post("/mcp", async (req: Request, res: Response) => { @@ -292,10 +297,12 @@ export class TestServerHttp { // Connect transport to server await this.mcpServer.connect(this.transport); - // Start listening + // Start listening (port 0 = OS assigns a unique port) return new Promise((resolve, reject) => { this.httpServer!.listen(port, () => { - resolve(port); + const assignedPort = (this.httpServer!.address() as { port: number }) + ?.port; + resolve(assignedPort ?? port); }); this.httpServer!.on("error", reject); }); @@ -371,10 +378,12 @@ export class TestServerHttp { // Note: SSE transport is created per request, so we don't store a single instance this.transport = undefined; - // Start listening + // Start listening (port 0 = OS assigns a unique port) return new Promise((resolve, reject) => { this.httpServer!.listen(port, () => { - resolve(port); + const assignedPort = (this.httpServer!.address() as { port: number }) + ?.port; + resolve(assignedPort ?? port); }); this.httpServer!.on("error", reject); });