mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
* fix(mcp): stream pinned transport via undici.request + redirect interceptor Extends the Bun undici-streaming fix to createPinnedFetchWithDispatcher (providers, A2A, self-hosted-private MCP over SSE). It now routes through undiciRequestAsResponse like the guarded builder, so streaming bodies deliver under Bun. Unlike the guarded path it has no followRedirectsGuarded wrapper (it's handed straight to provider SDKs), so redirects are followed via undici's redirect interceptor composed onto the pinned Agent — every hop still dispatches through the pinned connect.lookup (resolvedIP), so a redirect can't escape to another address, matching the old fetch guarantee. secureFetchWithPinnedIP (raw Node http, tools path) is untouched. * fix(mcp): honor redirect mode + drop cross-origin credentials on pinned fetch Replaces the always-on redirect interceptor with redirect-mode-aware handling: - redirect:'manual' returns the 3xx without following (detectMcpAuthType inspects it) - redirect:'error' throws on a 3xx - default 'follow' uses followRedirectsGuarded, which drops ALL headers on a cross-origin hop (so a redirect can't disclose a provider api-key to another origin — Greptile P1) and stamps the final response.url + redirected flag. Extracts the shared Request-lift helper used by both guarded and pinned builders. * fix(mcp): don't block private IP-literal URLs on the pinned fetch path Routing the pinned fetch through followRedirectsGuarded added an initial assertGuardedRedirectTarget check the old undici.fetch path never ran, which would block a self-hosted MCP configured with a private IP-literal URL (e.g. http://10.0.0.5:3000/mcp) — its own transport. The pinned path's callers already validate the target and the private carve-out intentionally pins to a private IP, so skip the initial-target check (validateInitialTarget: false) while still validating every redirect hop. Adds a regression test. * fix(mcp): carry redirect mode from a Request input in liftFetchArgs liftFetchArgs copied method/headers/body/signal from a Request but omitted redirect, so a Request({ redirect: 'manual' }) on the pinned path defaulted to 'follow' and was transparently followed. Copy input.redirect (explicit init still wins). Adds a Request-input redirect-mode test. * fix(mcp): permit the pinned IP as a redirect target (initial + hops), block other private IPs Consolidates the pinned-path redirect policy into one mechanism. followRedirectsGuarded took validateInitialTarget to skip the initial private-IP check, but per-hop checks still blocked a self-hosted MCP redirecting to its own pinned private IP (e.g. a trailing-slash 301 to http://10.0.0.5/mcp/). Replace it with allowRedirectToIp: the pinned fetch permits exactly its own validated IP as a target — initial URL and any hop that stays on it — while every OTHER private target (e.g. the 169.254.169.254 metadata IP) stays blocked. Tests cover the same-IP hop (followed) and the metadata-IP escape (still refused).