From ce5791dd03b780aa52ad62d52b84ca95ec693722 Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 20 Aug 2026 23:52:35 +0000 Subject: [PATCH] test(connections): route MCP fixtures through test transport Co-Authored-By: Paperclip --- server/src/__tests__/tool-access-service.test.ts | 7 ++++++- server/src/routes/tool-access.ts | 3 +++ server/src/services/tool-access.ts | 14 +++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/server/src/__tests__/tool-access-service.test.ts b/server/src/__tests__/tool-access-service.test.ts index af4147f2c03..91f311610df 100644 --- a/server/src/__tests__/tool-access-service.test.ts +++ b/server/src/__tests__/tool-access-service.test.ts @@ -156,7 +156,12 @@ function createRouteApp( }; next(); }); - app.use("/api", toolAccessRoutes(db, { toolGateway, ...deployment })); + app.use("/api", toolAccessRoutes(db, { + toolGateway, + remoteHttpEndpointLookup: async () => [{ address: "8.8.8.8", family: 4 }], + remoteHttpRequest: async (url, init) => fetch(url, init), + ...deployment, + })); app.use(errorHandler); return app; } diff --git a/server/src/routes/tool-access.ts b/server/src/routes/tool-access.ts index 71e366deb95..c22890ea353 100644 --- a/server/src/routes/tool-access.ts +++ b/server/src/routes/tool-access.ts @@ -112,6 +112,9 @@ export function toolAccessRoutes( authPublicBaseUrl?: string | null; trustedLocalStdioRuntimeHost?: string | null; toolGateway?: ToolGatewayService; + /** Test-only seams forwarded to the tool access service. */ + remoteHttpEndpointLookup?: NonNullable[1]>["remoteHttpEndpointLookup"]; + remoteHttpRequest?: NonNullable[1]>["remoteHttpRequest"]; } = {}, ) { const router = Router(); diff --git a/server/src/services/tool-access.ts b/server/src/services/tool-access.ts index 7fc81cb5880..f7e74c28cc4 100644 --- a/server/src/services/tool-access.ts +++ b/server/src/services/tool-access.ts @@ -1828,6 +1828,12 @@ export function toolAccessService(db: Db, options: ToolAccessServiceOptions = {} }; } + async function requestRemoteHttpEndpoint(endpoint: URL, init: RequestInit): Promise { + return options.remoteHttpRequest + ? options.remoteHttpRequest(endpoint.toString(), { ...init, redirect: "manual" }) + : guardedRemoteHttpFetch(endpoint, init, remoteHttpFetchOptions()); + } + /** * Fetch an operator-supplied remote URL with the egress guard bound to the * connection itself. @@ -1843,9 +1849,7 @@ export function toolAccessService(db: Db, options: ToolAccessServiceOptions = {} const method = (init.method ?? "GET").toUpperCase(); for (let redirectCount = 0; redirectCount <= MAX_REMOTE_HTTP_REDIRECTS; redirectCount += 1) { const endpoint = parseRemoteHttpEndpoint(currentUrl, (message, code) => badRequest(message, { code })); - const response = options.remoteHttpRequest - ? await options.remoteHttpRequest(endpoint.toString(), { ...init, redirect: "manual" }) - : await guardedRemoteHttpFetch(endpoint, init, remoteHttpFetchOptions()); + const response = await requestRemoteHttpEndpoint(endpoint, init); const location = REMOTE_HTTP_REDIRECT_STATUSES.has(response.status) ? response.headers?.get?.("location") ?? null : null; @@ -3947,7 +3951,7 @@ export function toolAccessService(db: Db, options: ToolAccessServiceOptions = {} // Pinned to the address the guard approved: `config.url` is operator-supplied, // so a second DNS resolution here would reopen the rebinding window that // PAP-17098 closed for the OAuth endpoints. - const response = await guardedRemoteHttpFetch(remoteEndpoint(connection.config), { + const response = await requestRemoteHttpEndpoint(new URL(remoteEndpoint(connection.config)), { method: "POST", // MCP Streamable HTTP requires advertising that we accept both a JSON body // and an SSE stream; spec-compliant servers 406 without it (see mcp-http.ts). @@ -3958,7 +3962,7 @@ export function toolAccessService(db: Db, options: ToolAccessServiceOptions = {} method: "tools/list", params: {}, }), - }, remoteHttpFetchOptions()); + }); if (!response.ok) { const authenticate = response.headers.get("www-authenticate") ?? ""; if (response.status === 401 && /bearer|oauth|authorization/i.test(authenticate)) {