Skip to content

fix(mcp): apply the Host/Origin guard to every HTTP transport - #746

Draft
vishal-bala wants to merge 1 commit into
mainfrom
fix/mcp-http-transport-host-guard
Draft

vishal-bala wants to merge 1 commit into
mainfrom
fix/mcp-http-transport-host-guard

Conversation

@vishal-bala

Copy link
Copy Markdown
Collaborator

Motivation

The MCP server's DNS-rebinding protection, the Host/Origin validation middleware, was only applied when run_async received 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 from fastmcp.settings.transport and therefore from FASTMCP_TRANSPORT. In both cases 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 that embeds the server and calls run_async itself was affected.

Changes

The HTTP transport names are now defined once, as HTTP_TRANSPORTS in redisvl/mcp/transport_security.py, and run_async checks the transport after resolving an omitted one the same way FastMCP does:

resolved = transport if transport is not None else fastmcp.settings.transport
if resolved in HTTP_TRANSPORTS:
    ...  # prepend the Host/Origin middleware

FastMCP's run_http_async passes middleware to http_app whatever the transport name, so the middleware takes effect for "http" exactly as it does for "streamable-http".

The CLI's _check_http_auth and _serve now 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, so rvl stays usable without the mcp extra.

The regression test drives run_async with each transport name, and with an omitted one resolving to stdio, http or streamable-http. Against the previous code it fails three of its seven cases: "http", and an omitted transport resolving to either HTTP name.

`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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant