NewApp Monitoring is liveSign up free
AgentbeamBeta
AI agent vulnerability · MCP Python SDK

CVE-2026-59950: MCP Python SDK: WebSocket server transport does not support Host/Origin validation

highCVSS 4.0 7.6GHSA-vj7q-gjh5-988w

The deprecated WebSocket server transport in the MCP Python SDK (`mcp.server.websocket.websocket_server`) accepts connections without checking the `Host` or `Origin` header, in every version before 1.28.1. A malicious web page can connect to a local MCP server and call its tools. Upgrading to 1.28.1 only helps if you also pass `TransportSecuritySettings`.

Published Jul 16, 2026 · Analysis Oct 25, 2026

What an attacker can do

websocket_server() creates a Starlette WebSocket and calls accept(subprotocol="mcp") at once, without reading any header. The SSE and Streamable HTTP transports take a security_settings: TransportSecuritySettings and validate Host and Origin first. The WebSocket one never had that. Browsers send an Origin header on cross-origin WebSocket upgrades but do not apply a same-origin policy to the result, so a page from any site can open the socket, complete the initialize handshake and send JSON-RPC requests.

The realistic victim runs such an MCP server on localhost or a LAN address with no token or origin gate in front, then visits a hostile page. The page can list the server's tools, call them and read its resources. What that gives the attacker depends entirely on what the server exposes: files, shell access, internal APIs. The vector has user interaction (visiting the page). Some browsers ask before a public page reaches a local-network address, but that prompt is not a server-side control.

The transport is not part of the MCP specification, is deprecated, and FastMCP does not expose it. Someone had to wire it into an ASGI app by hand.

Who is affected

PyPI package mcp before 1.28.1, only if your own code exposes mcp.server.websocket.websocket_server. Servers using stdio, SSE, Streamable HTTP or FastMCP are not affected. Check for the transport and the version:

grep -rn websocket_server .
pip show mcp

mcp

Ecosystem
PyPI
Affected
< 1.28.1
Fixed in
1.28.1
Check
pip show mcp

How to fix it

Upgrade to mcp 1.28.1:

pip install --upgrade mcp==1.28.1

The upgrade alone changes nothing, because security_settings defaults to None and leaves validation off. Pass a TransportSecuritySettings to websocket_server() with enable_dns_rebinding_protection=True and set allowed_hosts and allowed_origins. Failing requests then get HTTP 403 and ValueError("Request validation failed") is raised.

The advisory recommends moving to Streamable HTTP, where FastMCP turns this protection on for localhost binds. The WebSocket transport is removed in v2.

How to tell if you were hit

Look in your web server or reverse proxy logs for WebSocket upgrade requests to the MCP endpoint whose Origin header is an unexpected site rather than your own client or none at all. Look for initialize and tool-call traffic on that socket from browser user agents. After applying the settings, rejected handshakes show as 403 responses. Also review what the exposed MCP tools can reach, since that bounds the damage of any past visit to a hostile page.

Original advisory

From the GitHub Advisory Database (CC BY 4.0).

Summary

In affected versions, the deprecated WebSocket server transport (mcp.server.websocket.websocket_server) accepted the WebSocket handshake without applying any Host or Origin header validation. The TransportSecuritySettings mechanism that the SSE and Streamable HTTP transports use for this purpose was not wired into the WebSocket transport, so there was no SDK-level way to restrict which origins could connect.

Am I affected?

Only if a developer's application server exposes mcp.server.websocket.websocket_server. This transport has never been part of the MCP specification, is marked deprecated, and is not reachable through FastMCP — a developer must have wired it into an ASGI application themselves. Servers using stdio, SSE, or Streamable HTTP are not affected by this advisory.

Details

websocket_server() constructed a Starlette WebSocket and called accept(subprotocol="mcp") immediately, with no inspection of the connection's headers. By contrast, SseServerTransport and StreamableHTTPServerTransport accept an optional security_settings: TransportSecuritySettings and run TransportSecurityMiddleware.validate_request() against the incoming Host and Origin headers before establishing a session. Because browsers attach an Origin header to cross-origin WebSocket upgrade requests but do not enforce a same-origin policy on the response, a web page served from any origin could open a WebSocket to a reachable MCP server on this transport, complete the initialize handshake, and issue JSON-RPC requests on the resulting session.

Impact

A user who runs an MCP server on this transport bound to localhost or a LAN address, without a separate authentication or origin gate in front of it, and visits a malicious web page, can have that page enumerate and invoke the server's tools and read its resources. The consequences depend entirely on what the server exposes. The transport itself requires no token or prior session. Some browsers prompt before allowing a public page to open a connection to a local-network address, which adds a user-interaction step but is not a substitute for server-side validation.

Mitigation

Upgrade to version 1.28.1 or later, in which websocket_server() accepts the same optional security_settings: TransportSecuritySettings argument as the other HTTP-based transports and validates the Host and Origin headers before accepting the handshake; a request that fails validation is rejected with HTTP 403 and ValueError("Request validation failed") is raised to the caller. As with the other transports the parameter defaults to None, which leaves validation disabled, so upgrading alone does not change behaviour: pass a TransportSecuritySettings with enable_dns_rebinding_protection=True and appropriate allowed_hosts / allowed_origins to receive the protection. The recommended path remains to migrate off this deprecated transport to Streamable HTTP, where FastMCP enables this protection automatically for localhost binds. The WebSocket transport has been removed entirely in v2.

Credited to nitish-yaddala, Nadav0077, dodge1218, gistrec, u-ktdi.

Questions

Does upgrading to mcp 1.28.1 fix CVE-2026-59950 by itself?

No. security_settings defaults to None, so validation stays off. You must pass TransportSecuritySettings with enable_dns_rebinding_protection=True and your allowed hosts and origins.

Is FastMCP affected?

No. FastMCP does not expose the WebSocket transport. Only servers where a developer wired websocket_server into an ASGI app are affected.

Does it need user interaction?

Yes. The user must visit a malicious web page while the vulnerable server is reachable from their browser, such as on localhost. No token is needed on the transport itself.

References

How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology

More MCP Python SDK vulnerabilities

All MCP Python SDK entries