From cca888dfcc260a48634d00e0116077db19ae15cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E8=89=AF?= <1204183885@qq.com> Date: Sat, 31 Oct 2026 19:25:36 +0800 Subject: [PATCH] fix(core): bump MCP client and core to 2.4.0 to drop pre-bundled fast-uri 3.1.0 (#15925) * fix(core): bump MCP client and core to 2.4.0 to drop pre-bundled fast-uri 3.1.0 The MCP client 2.0.0 pre-bundles ajv with an inlined fast-uri 3.1.0, which dependency audits cannot see and overrides cannot reach. 2.4.0 re-bundles fast-uri 3.1.8, clearing CVE-2026-6321, CVE-2026-6322, CVE-2026-18446, CVE-2026-75975 and CVE-2026-76172 from the published bundle. Both packages stay exact-pinned in lockstep because the client pins core to the same version. Fixes #15919 Co-authored-by: Qwen-Coder * chore(vscode-ide-companion): regenerate NOTICES for MCP client/core 2.4.0 Co-authored-by: Qwen-Coder * fix(core): refuse redirects before the SDK, not only inside the fetch 2.4.0 resolves a same-origin 30x in its own layer, against requestInit.redirect, before the compatibility fetch is called. The Agent Plugin transport only set headers, so a configured or bearer credential was replayed to another path on the same origin. Ask the same question in one place and carry the answer into requestInit on both transport paths, and pin it with a test that fails when the option is absent. --------- Co-authored-by: Qwen-Coder Co-authored-by: Shaojin Wen Co-authored-by: yiliang114 --- packages/core/package.json | 4 +- packages/core/src/tools/mcp-client.test.ts | 25 ++++++++++ packages/core/src/tools/mcp-client.ts | 57 +++++++++++++++++---- packages/vscode-ide-companion/NOTICES.txt | 58 +++++++++++++++++++++- pnpm-lock.yaml | 35 ++++++++----- 5 files changed, 153 insertions(+), 26 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 7b42a50d0ed..4ff63aad5a1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -111,8 +111,8 @@ "@google/genai": "2.6.0", "@iarna/toml": "^2.2.5", "@jitl/quickjs-singlefile-mjs-release-sync": "0.32.0", - "@modelcontextprotocol/client": "2.0.0", - "@modelcontextprotocol/core": "2.0.0", + "@modelcontextprotocol/client": "2.4.0", + "@modelcontextprotocol/core": "2.4.0", "@modelcontextprotocol/sdk": "^1.30.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.221.0", diff --git a/packages/core/src/tools/mcp-client.test.ts b/packages/core/src/tools/mcp-client.test.ts index 5a7e5f45de0..e536acbc7a6 100644 --- a/packages/core/src/tools/mcp-client.test.ts +++ b/packages/core/src/tools/mcp-client.test.ts @@ -2377,6 +2377,31 @@ lOTTGqPpwFUbw2EMOOpFYuIyzGMIpUNMBjE2gvJiqFQ= }); }); + // The assertion above only proves the inner fetch was told to stop. + // The SDK resolves redirects outside it, against `requestInit`, so the + // transport has to carry the same answer or a same-origin 30x is + // replayed with the credential attached. + it('carries the Agent Plugin redirect refusal into the transport', async () => { + const plugin = await transportFor( + { + httpUrl: 'https://example.com/mcp', + headers: { 'X-Api-Key': 'synthetic' }, + agentPluginV1: true, + }, + 'plugin-redirect', + ); + const plain = await transportFor( + { + httpUrl: 'https://example.com/mcp', + headers: { 'X-Api-Key': 'synthetic' }, + }, + 'plain-redirect', + ); + + expect(plugin._requestInit?.redirect).toBe('manual'); + expect(plain._requestInit?.redirect).toBeUndefined(); + }); + /** * Sends `init` (the optional GET/SSE probe by default) to /mcp through * the compatibility fetch over a stub answering `body` with `status`. diff --git a/packages/core/src/tools/mcp-client.ts b/packages/core/src/tools/mcp-client.ts index 457ad4df4d4..6f4afd053db 100644 --- a/packages/core/src/tools/mcp-client.ts +++ b/packages/core/src/tools/mcp-client.ts @@ -272,6 +272,27 @@ function isStreamableHttpGetSseRequest(init?: RequestInit): boolean { .some((type) => type === 'text/event-stream'); } +/** + * Whether the Agent Plugin transport must refuse redirects: it may only reach + * the address it was configured with, and a configured or bearer credential + * makes a forwarded 30x a credential leak. + * + * The SDK resolves redirects outside the compatibility fetch, against + * `requestInit.redirect`, so both layers have to be told. Asking the same + * question in one place keeps them from disagreeing. + */ +function agentPluginForbidsRedirect( + mcpServerConfig: MCPServerConfig | undefined, + headers: Headers | undefined, +): boolean { + return ( + mcpServerConfig?.agentPluginV1 === true && + ((mcpServerConfig.headers !== undefined && + Object.keys(mcpServerConfig.headers).length > 0) || + headers?.has('authorization') === true) + ); +} + /** * Wraps fetch to preserve OAuth challenges before the SDK discards response * metadata and to normalize conventional "GET not supported" rejections of @@ -297,11 +318,10 @@ export function createStreamableHttpCompatibilityFetch( const requestHeaders = new Headers( init?.headers ?? (input instanceof Request ? input.headers : undefined), ); - const stopAgentPluginRedirect = - mcpServerConfig?.agentPluginV1 === true && - ((mcpServerConfig.headers !== undefined && - Object.keys(mcpServerConfig.headers).length > 0) || - requestHeaders.has('authorization')); + const stopAgentPluginRedirect = agentPluginForbidsRedirect( + mcpServerConfig, + requestHeaders, + ); const effectiveInit = stopAgentPluginRedirect ? { ...init, redirect: 'manual' as const } : init; @@ -1352,13 +1372,20 @@ async function createTransportWithOAuth( try { if (mcpServerConfig.httpUrl) { // Create HTTP transport with OAuth token + const oauthHeaders = { + ...mcpServerConfig.headers, + Authorization: `Bearer ${accessToken}`, + }; const oauthTransportOptions: StreamableHTTPClientTransportOptions = { fetch: createMcpStreamableHttpFetch(mcpServerName, mcpServerConfig), requestInit: { - headers: { - ...mcpServerConfig.headers, - Authorization: `Bearer ${accessToken}`, - }, + headers: oauthHeaders, + ...(agentPluginForbidsRedirect( + mcpServerConfig, + new Headers(oauthHeaders), + ) + ? { redirect: 'manual' as const } + : {}), }, }; @@ -2644,6 +2671,18 @@ export async function createTransport( }; } + // The compatibility fetch answers the request it issues, but the SDK + // resolves a redirect before that, against this field. + if ( + transportOptions.requestInit && + agentPluginForbidsRedirect( + mcpServerConfig, + new Headers(transportOptions.requestInit.headers as HeadersInit), + ) + ) { + transportOptions.requestInit.redirect = 'manual'; + } + return new StreamableHTTPClientTransport( new URL(mcpServerConfig.httpUrl), transportOptions, diff --git a/packages/vscode-ide-companion/NOTICES.txt b/packages/vscode-ide-companion/NOTICES.txt index 08a22bdd0fa..e60fab5fedc 100644 --- a/packages/vscode-ide-companion/NOTICES.txt +++ b/packages/vscode-ide-companion/NOTICES.txt @@ -5751,7 +5751,7 @@ SOFTWARE. ============================================================ -@modelcontextprotocol/client@2.0.0 +@modelcontextprotocol/client@2.4.0 (git+https://github.com/modelcontextprotocol/typescript-sdk.git) The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0. @@ -5972,6 +5972,60 @@ CC-BY-4.0. See https://creativecommons.org/licenses/by/4.0/legalcode for the full license text. +============================================================ +eventsource-parser@3.1.1 +(git+ssh://git@github.com/rexxars/eventsource-parser.git) + +MIT License + +Copyright (c) 2026 Espen Hovlandsdal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +============================================================ +jose@6.2.12 +(https://github.com/panva/jose) + +The MIT License (MIT) + +Copyright (c) 2018 Filip Skokan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + ============================================================ zod@4.4.3 (git+https://github.com/colinhacks/zod.git) @@ -6000,7 +6054,7 @@ SOFTWARE. ============================================================ -@modelcontextprotocol/core@2.0.0 +@modelcontextprotocol/core@2.4.0 (git+https://github.com/modelcontextprotocol/typescript-sdk.git) The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74032b934e9..3a8ad214980 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -990,11 +990,11 @@ importers: specifier: 0.32.0 version: 0.32.0 '@modelcontextprotocol/client': - specifier: 2.0.0 - version: 2.0.0 + specifier: 2.4.0 + version: 2.4.0 '@modelcontextprotocol/core': - specifier: 2.0.0 - version: 2.0.0 + specifier: 2.4.0 + version: 2.4.0 '@modelcontextprotocol/sdk': specifier: ^1.30.0 version: 1.30.0(supports-color@7.2.0)(zod@3.25.76) @@ -3130,12 +3130,12 @@ packages: resolution: {integrity: sha512-AAFtGyjsI4LcNhuXavc3ALIeDGqV/1CMclg285rV6h0Wpf5DObZmn9D2fTKTgRHKyGcclVEWc1pt3eJ9KEiPIg==} engines: {node: '>=18'} - '@modelcontextprotocol/client@2.0.0': - resolution: {integrity: sha512-8f1OghQ2rjzIOfqgUCP+8GiUWqRs89njoWLNqAe8kWmDePv3s1fZXseej+QXemssEuuOvLLmLO/kqM3IQHtISw==} + '@modelcontextprotocol/client@2.4.0': + resolution: {integrity: sha512-LgrD2OphHuG12vfKFKz6SOnq7bKHQmhGJIAYdD+MZBn2npHEyry6uD8zvktodtl5CQM85MO0anUyD9JwmUmxww==} engines: {node: '>=20'} - '@modelcontextprotocol/core@2.0.0': - resolution: {integrity: sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==} + '@modelcontextprotocol/core@2.4.0': + resolution: {integrity: sha512-z8QBKqdfXLPzkLQuG4xp8hrVEMm1PZ1IgxMN2sMbJiFiAKOWXnAzogXn2W4aqIBlP1ea6U5yiqVquvUNM1Cj6Q==} engines: {node: '>=20'} '@modelcontextprotocol/ext-apps@1.7.5': @@ -3511,6 +3511,7 @@ packages: '@opentelemetry/propagator-jaeger@2.10.0': resolution: {integrity: sha512-yw/IX8DL470dSMZJoE82ScfYGp7JWZ/G8kFJo35ZILUVTB2jFPTOaioN+8s09pH0RHsWNhweVZb+ZnjJJpCChg==} engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Package no longer supported. See https://github.com/open-telemetry/opentelemetry-js/pull/7077 peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -3541,12 +3542,14 @@ packages: '@opentelemetry/sdk-trace-base@2.10.0': resolution: {integrity: sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==} engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Use @opentelemetry/sdk-trace package peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' '@opentelemetry/sdk-trace-node@2.10.0': resolution: {integrity: sha512-GZK/G6oZyBLGlH1pUgeDch7D91KoHd2uotUGIkWCPi9GI5T9X0p4L7nNAMDR1BQjkRYoDqo+ddfVx9t5Uhys+Q==} engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Use @opentelemetry/sdk-trace package peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -6967,6 +6970,10 @@ packages: resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==} engines: {node: '>=20.0.0'} + eventsource-parser@3.1.1: + resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} + engines: {node: '>=18.0.0'} + eventsource@3.0.7: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} @@ -12351,17 +12358,17 @@ snapshots: '@mobilewright/protocol@0.0.53': {} - '@modelcontextprotocol/client@2.0.0': + '@modelcontextprotocol/client@2.4.0': dependencies: - '@modelcontextprotocol/core': 2.0.0 + '@modelcontextprotocol/core': 2.4.0 cross-spawn: 7.0.6 eventsource: 3.0.7 - eventsource-parser: 3.0.3 - jose: 6.1.3 + eventsource-parser: 3.1.1 + jose: 6.2.12 pkce-challenge: 5.0.0 zod: 4.4.3 - '@modelcontextprotocol/core@2.0.0': + '@modelcontextprotocol/core@2.4.0': dependencies: zod: 4.4.3 @@ -16861,6 +16868,8 @@ snapshots: eventsource-parser@3.0.3: {} + eventsource-parser@3.1.1: {} + eventsource@3.0.7: dependencies: eventsource-parser: 3.0.3