## Summary During OAuth discovery, when a `GET` to the MCP endpoint returns `404` or `405`, `AuthorizationManager` falls back to POSTing a synthetic JSON-RPC `initialize` request to the endpoint in order to elicit a `401` response with a `WWW-Authenticate` header. If the server actually accepts that `initialize` instead of rejecting it (e.g. authorization is optional, or `initialize` is served before authentication), the server may allocate a session and return an `Mcp-Session-Id`. The client discards any non-`401` probe response without ever sending an HTTP `DELETE` for that session, so the session is orphaned on the server. Separately, the MCP spec does not clearly indicate that clients should send `initialize` as part of OAuth discovery at all. The probe exists as a pragmatic workaround for streamable HTTP servers that reject a session-less `GET`, but it means discovery — which callers reasonably expect to be a read-only operation — can have server-side side effects. ## Current behavior At `6839cfd`, resource metadata discovery works like this: 1. [`discover_resource_metadata_url()`](https://github.com/modelcontextprotocol/rust-sdk/blob/6839cfd61314c2a3c2bc51bf5563c42b1eee2fd2/crates/rmcp/src/transport/auth.rs#L2357-L2382) first issues a `GET` against the base URL via [`fetch_resource_metadata_url()`](https://github.com/modelcontextprotocol/rust-sdk/blob/6839cfd61314c2a3c2bc51bf5563c42b1eee2fd2/crates/rmcp/src/transport/auth.rs#L2386-L2412), hoping for a `401` + `WWW-Authenticate`. 2. Many streamable HTTP servers answer a session-less `GET` with `404`/`405`, in which case [`fetch_resource_metadata_url_with_post_probe()`](https://github.com/modelcontextprotocol/rust-sdk/blob/6839cfd61314c2a3c2bc51bf5563c42b1eee2fd2/crates/rmcp/src/transport/auth.rs#L2414-L2451) POSTs [`RESOURCE_METADATA_POST_PROBE_BODY`](https://github.com/modelcontextprotocol/rust-sdk/blob/6839cfd61314c2a3c2bc51bf5563c42b1eee2fd2/crates/rmcp/src/transport/auth.rs#L34-L39) — a synthetic `initialize` request with `protocolVersion: 2024-11-05`. 3. Any response other than `401` is [logged and discarded](https://github.com/modelcontextprotocol/rust-sdk/blob/6839cfd61314c2a3c2bc51bf5563c42b1eee2fd2/crates/rmcp/src/transport/auth.rs#L2440-L2446). If the server processed the `initialize` and returned an `Mcp-Session-Id`, no `DELETE` is ever sent for it. The probe was introduced in #960 to pass the client conformance suite against servers that reject session-less `GET`s. ## Why this is a problem - Servers where authorization is optional (or where `initialize` is allowed pre-auth) accumulate orphaned sessions: one per discovery attempt, never cleaned up. - Callers that use discovery to check whether a server supports OAuth (the same usage pattern described in #1037) trigger this side effect on servers that don't require auth at all — exactly the servers that will process the probe. - The spec describes discovery via `401` + `WWW-Authenticate` (RFC 9728) and well-known metadata paths; it does not sanction `initialize` as a discovery mechanism, so servers have no reason to expect or special-case this probe. ## Possible fixes 1. **Best-effort cleanup**: when the probe response is not `401` and carries an `Mcp-Session-Id` header, send an HTTP `DELETE` for that session before discarding the response. 2. **Make the probe a last resort**: try the well-known `oauth-protected-resource` paths *before* falling back to the POST probe (today the probe runs before the well-known lookup), so most servers never receive a synthetic `initialize`. 3. **Upstream spec clarification**: raise with the spec repo whether clients are intended to send `initialize` as part of OAuth discovery at all, and what server-side session semantics apply to unauthenticated probe requests. Options 1 and 2 are complementary and could both be implemented in the SDK regardless of the spec discussion. ## Related - #960 (introduced the POST probe for conformance) - #1037 / #1041 (metadata provenance — orthogonal, but same discovery code path)