blog.rust.gay

RustSec issues three November advisories for rmcp, the Rust MCP SDK

On November 9, 2026, the RustSec Advisory Database published three advisories for rmcp, the Rust SDK for the Model Context Protocol. The issues cover different parts of the library: a memory-exhaustion bug in the Streamable HTTP server transport, an SSRF filter in the OAuth client that checked host strings instead of resolved addresses, and unbounded response-body buffering in the built-in Streamable HTTP clients. None of the flaws are identical, and they were fixed in different releases, but together they are a useful snapshot of what can go wrong when a Rust networking library sits between AI tooling, HTTP endpoints, and third-party servers.

This post focuses on what the advisories actually say. It does not add claims beyond the published RustSec records, the upstream pull request and commits they reference, and the general behavior described by the Model Context Protocol's HTTP transport. Where an operational recommendation follows from the advisory text, it is framed as mitigation rather than as a guarantee.

What rmcp is and why it matters

rmcp is the Rust crate that implements Model Context Protocol functionality for Rust applications. MCP is a protocol for connecting language-model hosts and agents to tools, resources, and prompts exposed by servers. In practice, that means an MCP implementation can sit in sensitive places: a local agent process talking to a remote tool server, a desktop host embedding MCP clients, or a service exposing tools over HTTP.

The RustSec advisories published on November 9 concern the Streamable HTTP transport and the OAuth client. That context matters because the affected code paths are not just in-memory abstractions. They involve accepting network requests, validating protocol messages, following metadata discovered from servers, and reading HTTP responses. In those paths, resource limits, address validation, and trust boundaries are as important as type safety.

Rust prevents many classes of memory corruption by default, but it does not automatically prevent denial-of-service conditions caused by unbounded allocation, nor does it make URL validation correct. The rmcp advisories are a reminder that safe Rust code still needs explicit limits and careful security checks when it handles untrusted network input.

RUSTSEC-2026-0461: an unbounded tool schema cache in the server transport

RUSTSEC-2026-0461 describes a high-severity denial-of-service issue in StreamableHttpService. The advisory was reported on October 20, 2026 and issued on November 9, 2026. RustSec assigns it CVSS 3.1 score 7.5 (High) with vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H. The affected package is rmcp on crates.io.

According to the advisory, from version 3.0.0 until 3.6.0, StreamableHttpService cached the result of ServerHandler::get_tool in a process-wide map keyed by the tool name of incoming tools/call requests. The cache had no size limit and no eviction policy. It also cached unknown tool names. An entry was added while request headers were validated, before the request could be rejected, so requests that were ultimately answered with HTTP 400 still grew the map.

The attack described by RustSec is straightforward: a client that can reach the endpoint can send tools/call requests with distinct tool names, each up to the default 4 MiB body limit, until the server runs out of memory. The advisory also notes that rmcp does not authenticate requests itself, so servers without an authentication layer in front of the endpoint are exposed to unauthenticated clients.

The issue is limited to servers that use the Streamable HTTP server transport and receive requests at protocol version 2026-07-28 or later. RustSec states that the flaw was corrected in 3.6.0 by removing the cache. After the fix, the transport calls ServerHandler::get_tool on each request.

The upstream reference listed by RustSec is modelcontextprotocol/rust-sdk#1349. The practical lesson is not that caching is inherently unsafe; it is that a cache keyed by attacker-controlled input must have bounds, eviction, and a clear trust model. Caching before authorization or validation decisions can also turn rejected traffic into state growth.

If upgrading is not immediately possible, the advisory suggests putting authentication in front of the endpoint or rejecting tools/call requests for unknown tool names before they reach StreamableHttpService. Those are compensating controls, not fixes in the library itself.

RUSTSEC-2026-0457: OAuth discovery checked host strings, not resolved addresses

RUSTSEC-2026-0457 covers a client-side SSRF issue in rmcp's OAuth client. It was reported on October 22, 2026 and issued on November 9, 2026. The affected range is 2.0.0 until 3.7.0, with the fix in version 3.7.0. RustSec categorizes it as a vulnerability with keywords including SSRF, OAuth, and MCP, and links it to GHSA-5p52-q7pq-9fq2 and the upstream commit ac808ab.

The root cause described by RustSec is a mismatch between the security check and the network behavior. The OAuth client decided whether it may fetch an authorization server's metadata URL by inspecting only the host string. It rejected literal loopback, private, and link-local IP addresses and known cloud metadata hostnames, but it did not resolve other hostnames. A hostname that resolves to an internal address passed the check.

That gap creates a path for SSRF during OAuth discovery. A malicious MCP server can return protected-resource metadata that names an authorization server whose hostname resolves to an internal address. The client then sends GET requests to that address during discovery. RustSec states that responses are not returned to the attacker directly, but unauthenticated internal HTTP services are reachable. Cloud metadata endpoints that require headers or IMDSv2 are described as largely unaffected.

The same release fixed two related bypasses. Bracketed IPv6 literals such as http://[::1]:6379/ were not recognized as internal because the check kept the brackets from the URL host. In addition, token_endpoint and registration_endpoint from authorization server metadata were not checked at all, and token requests follow redirects. A malicious authorization server could make the client send POST requests to an internal address.

The fix in 3.7.0 changes the enforcement model. AuthorizationManager::new now builds its HTTP client with a DNS resolver that applies the address policy to every resolved address, and it checks every IP-literal URL and followed redirect against the same policy. Loopback and private addresses are allowed only when the MCP server's own URL names one of that class. Deployments that reach their authorization server through a hostname that resolves to a private address should use AuthorizationManager::new_allowing_private_addresses, according to RustSec.

There is an important caveat for integrators: clients supplied through new_with_oauth_http_client are not covered by the resolved-address checks. RustSec says such clients must enforce their own outbound address policy. For teams that customize HTTP clients, that means the security property is not automatic just because rmcp was upgraded; the custom client path still needs review.

RUSTSEC-2026-0462: Streamable HTTP clients buffered responses without a limit

The third advisory, RUSTSEC-2026-0462, affects the client side of Streamable HTTP. Reported on November 5, 2026 and issued on November 9, 2026, it is rated CVSS 3.1 6.5 (Medium) with vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H. The issue affects rmcp before 3.9.0 and is marked unaffected below 0.2.0. The fix is in 3.9.0, and the upstream commit reference is 3e0156c.

RustSec explains that the built-in Streamable HTTP clients read non-streaming HTTP response bodies into memory with no size limit. Both the reqwest client (transport-streamable-http-client-reqwest) and the Unix socket client (transport-streamable-http-client-unix-socket) were affected. application/json responses to POST requests were read in full before parsing, and non-2xx responses were read in full for error reporting.

The failure mode is a classic unbounded-buffer denial of service. A malicious or compromised MCP server can answer with a body that never ends, for example a chunked application/json response that streams whitespace. The client buffers it until the process runs out of memory. RustSec notes that this works during initialize, before any authentication, and that on desktop or agent hosts it can terminate the whole host application. The advisory also states that server-sent event streams are not affected because each event is already capped by max_sse_event_size and events are not accumulated.

In 3.9.0, successful JSON bodies are limited to 16 MiB and non-success bodies to 64 KiB. Bytes are counted while streaming, after decompression. An oversized Content-Length is rejected before the body is read, and an oversized body is rejected with StreamableHttpError::ResponseBodyTooLarge instead of being truncated. Those details matter because they show the fix addressing several common pitfalls: trusting declared lengths, buffering before enforcing limits, and silently truncating data.

For users who cannot upgrade, RustSec recommends using a custom StreamableHttpClient that enforces a response body limit or connecting only to trusted MCP servers. Again, the trust recommendation is narrower than a code fix: MCP clients often connect to configurable servers, and a "trusted server" assumption can weaken over time.

Why the three advisories belong in the same conversation

Taken individually, each advisory describes a specific bug. Together, they show how protocol implementations accumulate risk at boundaries. The server-side cache bug is about attacker-controlled keys and state growth. The OAuth bug is about validating names instead of resolved network destinations. The client buffering bug is about reading untrusted data without a cap.

All three are also shaped by the deployment model of MCP. A server transport exposed on a network may not be authenticated by the library. An OAuth-capable client may discover endpoints from metadata supplied by a server it is trying to talk to. A client library embedded in an agent or desktop application may treat a remote MCP server as a routine dependency, even though that server can influence memory usage during initialization.

For Rust developers, the useful takeaway is not "avoid MCP" or "Rust networking is unsafe." The more precise takeaway is that protocol and HTTP code needs explicit resource budgets and boundary checks, even when the implementation is memory-safe in the Rust sense. A process-wide HashMap with no eviction, a hostname allowlist without DNS resolution checks, and a full-body read with no maximum are ordinary bugs. In network-facing infrastructure, ordinary bugs become availability or SSRF vulnerabilities.

What maintainers and operators should do

The first step is version triage. The three advisories do not share the same patched release: RUSTSEC-2026-0461 is fixed in rmcp 3.6.0, RUSTSEC-2026-0457 is fixed in 3.7.0, and RUSTSEC-2026-0462 is fixed in 3.9.0. An application that updated only far enough to clear one issue may still be exposed to another, depending on which rmcp features and transports it uses.

The second step is feature review. RustSec's descriptions include scope details: the server cache issue affects Streamable HTTP servers receiving protocol version 2026-07-28 or later requests; the OAuth issue affects clients using the vulnerable OAuth discovery flow; the response buffering issue affects the built-in Streamable HTTP clients, not SSE streams. Teams should map those conditions to their actual deployment rather than assuming every rmcp user has the same exposure.

The third step is compensating control review. Authentication in front of a Streamable HTTP endpoint can reduce exposure to the server cache issue. Custom OAuth HTTP clients must enforce outbound address policy themselves after 3.7.0 if they bypass AuthorizationManager::new. Custom Streamable HTTP clients need explicit body limits if they cannot rely on the built-in 3.9.0 behavior. These checks belong in application threat models because libraries cannot enforce every policy chosen by an integrator.

Finally, treat MCP server endpoints and MCP dependencies as part of the security boundary. The November 9 rmcp advisories are not proof of a widespread compromise, and RustSec does not describe active exploitation. They do show that AI-adjacent Rust infrastructure has the same exposure patterns as other network services: unbounded state, metadata-driven requests, and large or malicious responses. Dependency updates, HTTP limits, and egress controls are routine work, but they are the work that keeps routine bugs from becoming incidents.

Conclusion

The November 9, 2026 RustSec batch for rmcp is notable less for a single dramatic exploit than for the range of problems it exposes: server-side memory exhaustion from an unbounded cache, client-side SSRF from host-string validation during OAuth discovery, and client-side memory exhaustion from unbounded HTTP response buffering. Each issue has a published advisory, affected version range, and fixed release. The right response is to upgrade to a release that contains all relevant fixes for the components in use, verify any custom HTTP client behavior, and keep authentication and network egress controls in front of MCP services where appropriate.

For Rust teams building or embedding MCP tools, the broader lesson is simple: protocol correctness and memory safety are necessary, but they are not substitutes for limits. When code accepts requests, resolves metadata, or consumes responses from other parties, it needs explicit ceilings and address checks. RustSec's rmcp advisories make those requirements concrete.

Sources


← back to posts