Repository navigation
fix(mcp): apply the Host/Origin guard to every HTTP transport - #746
Open
vishal-bala wants to merge 3 commits into
Open
vishal-bala wants to merge 3 commits into
vishal-bala wants to merge 3 commits into
Conversation
`run_async` added the DNS-rebinding middleware only when the transport argument was literally "sse" or "streamable-http". FastMCP serves two more cases over HTTP: "http", which is FastMCP's own default HTTP transport name, and an omitted transport, which FastMCP resolves from `fastmcp.settings.transport` (settable through FASTMCP_TRANSPORT). In both, the server bound an HTTP port with no Host or Origin validation. The `rvl mcp` CLI was not affected, because its `--transport` choices are only stdio, sse and streamable-http. An application embedding the server and calling `run_async` itself was. The HTTP transport names are now defined once, as `HTTP_TRANSPORTS` in `transport_security`, and `run_async` tests the transport after resolving an omitted one the way FastMCP does. The CLI's two checks use the same set, so adding "http" to its choices later cannot silently skip the auth warning or the bind arguments. The CLI imports the set lazily, as it does the rest of the MCP code, so `rvl` stays usable without the `mcp` extra. The regression test drives `run_async` with every transport name and with an omitted one resolving to each, and fails three of its seven cases against the previous code.
vishal-bala
marked this pull request as ready for review
October 5, 2026 13:13
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1d0371c. Configure here.
Three corrections to the Host/Origin guard change. `fastmcp.settings.transport` only exists from FastMCP 3.1, but RedisVL supports `fastmcp>=2.0.0`. On 2.x through 3.0, reading it raised `AttributeError` before anything was served, breaking the default stdio path. Those versions always resolved an omitted transport to stdio, so that is now the fallback when the setting is absent. Checked against the 2.0.0, 2.10.0, 2.14.0, 3.0.0 and 3.1.0 wheels. The guard built its host allowlist from a hardcoded 127.0.0.1:8000 whenever no host or port was passed, while FastMCP binds `fastmcp.settings.host` and `port` (FASTMCP_HOST / FASTMCP_PORT) in that case. On a settings-driven non-loopback bind the guard would have rejected legitimate Host headers. It now resolves both the way FastMCP does, with explicit arguments still taking precedence. The CLI imported the server's transport set from `redisvl.mcp`, which its tests replace with a non-package stub, so the import resolved only when the submodule happened to be cached -- six of the fourteen CLI tests failed when run alone. The CLI goes back to importing nothing from `redisvl.mcp`, and instead derives its `--transport` choices and both HTTP-only checks from one local tuple, which keeps them from drifting apart.
…t-host-guard # Conflicts: # redisvl/mcp/server.py
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Motivation
The MCP server's DNS-rebinding protection, the Host/Origin validation middleware, was only applied when
run_asyncreceived the literal transport name"sse"or"streamable-http". FastMCP serves two more cases over HTTP. One is"http", which is FastMCP's own default HTTP transport name. The other is an omitted transport, which FastMCP resolves fromfastmcp.settings.transportand therefore fromFASTMCP_TRANSPORT. In both cases the server bound an HTTP port with no Host or Origin validation.The
rvl mcpCLI was not affected, because its--transportchoices are onlystdio,sseandstreamable-http. An application that embeds the server and callsrun_asyncitself was affected.Changes
The HTTP transport names are now defined once, as
HTTP_TRANSPORTSinredisvl/mcp/transport_security.py, andrun_asyncchecks the transport after resolving an omitted one the same way FastMCP does:FastMCP's
run_http_asyncpassesmiddlewaretohttp_appwhatever the transport name, so the middleware takes effect for"http"exactly as it does for"streamable-http".The CLI's
_check_http_authand_servenow use the same set. Nothing changes for CLI users today, but if"http"is later added to the CLI's choices, it can no longer skip the unauthenticated-bind warning or the bind arguments without anyone noticing. The CLI imports the set lazily, as it does the rest of the MCP code, sorvlstays usable without themcpextra.The regression test drives
run_asyncwith each transport name, and with an omitted one resolving tostdio,httporstreamable-http. Against the previous code it fails three of its seven cases:"http", and an omitted transport resolving to either HTTP name.Note
High Risk
Fixes missing DNS-rebinding protection on HTTP serving paths; incorrect allowlist/bind resolution could still break legitimate clients, but the change tightens security rather than weakening it.
Overview
Closes a gap where the MCP server could listen over HTTP without Host/Origin (DNS-rebinding) middleware when embedders used FastMCP’s
"http"transport or lefttransportunset so it resolved to HTTP viaFASTMCP_TRANSPORT.HTTP_TRANSPORTSis defined once intransport_security.py(http,sse,streamable-http).run_asyncresolves the effective transport the same way FastMCP does (including stdio fallback on older FastMCP withoutsettings.transport), then prepends the guard for any resolved HTTP name—not onlysse/streamable-http.The guard’s host allowlist now uses the bind address FastMCP will actually use: explicit
host/portkwargs, elsefastmcp.settings(FASTMCP_HOST/FASTMCP_PORT), avoiding rejections when settings drive the bind.The
rvl mcpCLI centralizes its HTTP transport list in_HTTP_TRANSPORT_CHOICESso future CLI HTTP options stay tied to auth/bind checks (still no module-level import fromredisvl.mcp). Regression tests cover each HTTP transport, omitted transport defaults, and settings-driven bind addresses.Reviewed by Cursor Bugbot for commit 54c4959. Bugbot is set up for automated code reviews on this repo. Configure here.