In affected versions, the SDK's server-side bearer authentication had no way to check that a token was issued for your server (its audience). Where one authorization server issues tokens for several services, a valid token for another service was also accepted by your MCP server.
In affected versions, the SDK's Streamable HTTP and SSE client transports followed HTTP redirects to any host. If the configured MCP endpoint answered with a redirect to a different origin: custom headers set on the client (for example an X-API-Key credential) were sent to that origin, and on a 307 or 308 redirect the request body was re-sent there too. The SDK's OAuth client providers followed redirects on their own …
In affected versions, when a connected MCP server declared an outputSchema for a tool, the SDK's client validated each call_tool result with jsonschema's default resolver, which fetches any $ref it cannot resolve locally by opening the URL. A server could therefore put an http://, https:// or file:// reference in a tool's output schema and make the client: request that URL, or read that local file, from the client's own network …
In affected versions, a Streamable HTTP server in stateful mode (the default) never reclaimed session state: sessions did not expire; a client DELETE ended the session but left its entry registered; a session whose first request was refused stayed registered with a running task. Each session also kept its parsed initialize parameters, and the number of sessions was unlimited. A client that could reach the endpoint could grow server memory …
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.
In affected versions, the SSE and Streamable HTTP server transports routed incoming requests to an existing session based only on the session identifier, without verifying that the request was authenticated as the same principal that created the session. Anyone who learned or guessed a session ID could send JSON-RPC messages on that session, regardless of which bearer token the request carried.
In affected versions, the default request handlers installed by the experimental tasks feature (server.experimental.enable_tasks()) did not check which session created a task before acting on it. On a server with more than one connected client, any client could observe, read results from, and cancel tasks belonging to other clients.