From a9c34af5c29afec025ccc05cb6149b94f13ea230 Mon Sep 17 00:00:00 2001 From: Piyushrathoree Date: Wed, 16 Sep 2026 23:31:11 +0530 Subject: [PATCH 1/3] test(mcp): make live MCP server tests opt-in The HTTP transport tests in mcp-client.spec.ts hit real third-party servers (mcp.deepwiki.com, mcp.context7.com) and were only skipped when CI was set. Running the suite locally without network access, or when one of those hosts is down, fails with an unhandled "TypeError: fetch failed" and takes the whole test run down with it. Gate them behind RUN_LIVE_MCP_TESTS=true instead. The invalid URL and websocket protocol checks only hit localhost / validation, so they now run unconditionally. Fixes #1348. --- source/mcp/mcp-client.spec.ts | 78 ++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/source/mcp/mcp-client.spec.ts b/source/mcp/mcp-client.spec.ts index ac8f5e1d2..6c9820cea 100644 --- a/source/mcp/mcp-client.spec.ts +++ b/source/mcp/mcp-client.spec.ts @@ -65,36 +65,38 @@ const mockTransportFactory = { console.log(`\nmcp-client.spec.ts`); -// Skip integration tests in CI. These tests hit real third-party MCP servers -// (mcp.deepwiki.com, remote.mcpservers.org, mcp.context7.com) — running them in -// CI would couple our pipeline to those services' uptime. Run them locally to -// verify HTTP transport against live servers. -const isCI = process.env.CI === 'true' || process.env.CI === '1'; -const testOrSkip = (title: string, impl: (t: any) => Promise | void) => { - if (isCI) { +// Live integration tests are opt-in because they depend on third-party MCP +// servers and network availability. Run them with RUN_LIVE_MCP_TESTS=true. +const runLiveMcpTests = + process.env.RUN_LIVE_MCP_TESTS === 'true' || + process.env.RUN_LIVE_MCP_TESTS === '1'; +const liveTest = ( + title: string, + impl: (t: any) => Promise | void, +) => { + if (!runLiveMcpTests) { test.skip(title, impl as any); - } else { - test.serial(title, async t => { - let lastErr: any; - for (let i = 0; i < 3; i++) { - const result = await (t as any).try(impl); - if (result.passed) { - result.commit(); - return; - } - - result.discard(); - lastErr = result.errors[0] || new Error('Unknown test failure'); - // These are remote integration tests, so we retry any failure - // (usually fetch failures or socket hang ups wrapped in AssertionErrors) - if (i < 2) { - await new Promise(r => setTimeout(r, 2000 * (i + 1))); - continue; - } - } - throw lastErr; - }); + return; } + + test.serial(title, async t => { + let lastErr: any; + for (let i = 0; i < 3; i++) { + const result = await (t as any).try(impl); + if (result.passed) { + result.commit(); + return; + } + + result.discard(); + lastErr = result.errors[0] || new Error('Unknown test failure'); + // These are remote integration tests, so retry transient failures. + if (i < 2) { + await new Promise(resolve => setTimeout(resolve, 2000 * (i + 1))); + } + } + throw lastErr; + }); }; // ============================================================================ @@ -734,7 +736,7 @@ test('MCPClient.getServerInfo: returns undefined when only tools exist', t => { // These tests use real remote MCP servers via HTTP transport // They test the actual connection, tool listing, and tool execution flow -testOrSkip('MCPClient.connectToServer: connects to remote HTTP MCP server', async t => { +liveTest('MCPClient.connectToServer: connects to remote HTTP MCP server', async t => { const client = new MCPClient(); // Use DeepWiki public MCP server (no auth required) @@ -769,7 +771,7 @@ testOrSkip('MCPClient.connectToServer: connects to remote HTTP MCP server', asyn t.is(client.getServerTools('test-deepwiki').length, 0); }); -testOrSkip('MCPClient.connectToServer: connects to context7 HTTP server and executes a tool', async t => { +liveTest('MCPClient.connectToServer: connects to context7 HTTP server and executes a tool', async t => { // Pair with the DeepWiki test above so a single host going dark doesn't // nuke all HTTP-transport integration coverage. context7 was picked after // remote.mcpservers.org disappeared at DNS level around mid-May 2026. @@ -805,7 +807,7 @@ testOrSkip('MCPClient.connectToServer: connects to context7 HTTP server and exec t.false(client.isServerConnected('test-context7')); }); -testOrSkip('MCPClient.connectToServers: connects to multiple HTTP servers', async t => { +liveTest('MCPClient.connectToServers: connects to multiple HTTP servers', async t => { const client = new MCPClient(); const servers = [ @@ -841,7 +843,7 @@ testOrSkip('MCPClient.connectToServers: connects to multiple HTTP servers', asyn await client.disconnect(); }); -testOrSkip('MCPClient.getAllTools: builds tools registry from connected HTTP server', async t => { +liveTest('MCPClient.getAllTools: builds tools registry from connected HTTP server', async t => { const client = new MCPClient(); const server = { @@ -872,7 +874,7 @@ testOrSkip('MCPClient.getAllTools: builds tools registry from connected HTTP ser await client.disconnect(); }); -testOrSkip('MCPClient.getNativeToolsRegistry: creates registry from connected HTTP server', async t => { +liveTest('MCPClient.getNativeToolsRegistry: creates registry from connected HTTP server', async t => { const client = new MCPClient(); const server = { @@ -903,7 +905,7 @@ testOrSkip('MCPClient.getNativeToolsRegistry: creates registry from connected HT await client.disconnect(); }); -testOrSkip('MCPClient.callTool: executes tool on connected HTTP server', async t => { +liveTest('MCPClient.callTool: executes tool on connected HTTP server', async t => { const client = new MCPClient(); const server = { @@ -934,7 +936,7 @@ testOrSkip('MCPClient.callTool: executes tool on connected HTTP server', async t await client.disconnect(); }); -testOrSkip('MCPClient.getToolMapping: returns mapping from connected HTTP server', async t => { +liveTest('MCPClient.getToolMapping: returns mapping from connected HTTP server', async t => { const client = new MCPClient(); const server = { @@ -966,7 +968,7 @@ testOrSkip('MCPClient.getToolMapping: returns mapping from connected HTTP server await client.disconnect(); }); -testOrSkip('MCPClient.getToolEntries: returns entries from connected HTTP server', async t => { +liveTest('MCPClient.getToolEntries: returns entries from connected HTTP server', async t => { const client = new MCPClient(); const server = { @@ -996,7 +998,7 @@ testOrSkip('MCPClient.getToolEntries: returns entries from connected HTTP server // Error Handling Tests with Real Servers // ============================================================================ -testOrSkip('MCPClient.connectToServer: handles invalid URL gracefully', async t => { +test('MCPClient.connectToServer: handles invalid URL gracefully', async t => { const client = new MCPClient(); const server = { @@ -1009,7 +1011,7 @@ testOrSkip('MCPClient.connectToServer: handles invalid URL gracefully', async t await t.throwsAsync(async () => await client.connectToServer(server)); }); -testOrSkip('MCPClient.connectToServer: validates websocket URL protocol', async t => { +test('MCPClient.connectToServer: validates websocket URL protocol', async t => { const client = new MCPClient(); const server = { From a97a9266ece9f1ba5faa4c3af49aa7e8cda33cf4 Mon Sep 17 00:00:00 2001 From: Piyushrathoree Date: Wed, 16 Sep 2026 23:47:37 +0530 Subject: [PATCH 2/3] docs: document opt-in live MCP tests --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ea021d391..8a0d8dfdc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -200,6 +200,12 @@ All new features and bug fixes should include appropriate tests: This command runs: Biome formatting checks, type checks, lint checks, AVA tests, Knip, security scans. + MCP live HTTP integration tests are skipped by default. Run them explicitly with: + + ```bash + RUN_LIVE_MCP_TESTS=true pnpm test:ava source/mcp/mcp-client.spec.ts + ``` + 4. **Test Requirements for PRs**: - New features **must** include passing tests in `.spec.ts/tsx` files - Bug fixes should include regression tests when possible From 355b63b8da0907a8e75b140307e388af0909248b Mon Sep 17 00:00:00 2001 From: Piyushrathoree Date: Thu, 24 Sep 2026 15:35:06 +0530 Subject: [PATCH 3/3] chore: add changeset for MCP client test updates --- .changeset/smooth-coins-teach.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/smooth-coins-teach.md diff --git a/.changeset/smooth-coins-teach.md b/.changeset/smooth-coins-teach.md new file mode 100644 index 000000000..ca0f919f0 --- /dev/null +++ b/.changeset/smooth-coins-teach.md @@ -0,0 +1,5 @@ +--- +"@nanocollective/nanocoder": patch +--- + +update test - mcp-client-spec.ts