Skip to content

feat: transparent session re-init on HTTP 404 for streamable HTTP#743

Draft
DaleSeo wants to merge 1 commit intomainfrom
feat/streamable-http-session-reinit
Draft

feat: transparent session re-init on HTTP 404 for streamable HTTP#743
DaleSeo wants to merge 1 commit intomainfrom
feat/streamable-http-session-reinit

Conversation

@DaleSeo
Copy link
Member

@DaleSeo DaleSeo commented Mar 10, 2026

Fixes #733

Motivation and Context

The MCP spec on Session Management states that when a client gets an HTTP 404 response to a request with Mcp-Session-Id, it must start a new session by sending a fresh InitializeRequest without a session ID. Before, the streamable HTTP client would show a generic UnexpectedServerResponse error and get stuck with the old session, which goes against the spec.

This PR allows the client to detect 404 responses on session-aware requests and show them as a new SessionExpired error. It will then automatically re-initialize by replaying the original initialize handshake, setting up a new SSE stream, and retrying the original message, all without the caller noticing. It will attempt a single retry, and if the re-initialization fails, the error will propagate normally to avoid infinite loops.

How Has This Been Tested?

Added new integration tests to verify both the low-level error detection and the end-to-end transparent recovery.

Breaking Changes

StreamableHttpError gains a new SessionExpired variant. Because the enum is #[non_exhaustive], downstream code that matches on it will not break.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@github-actions github-actions bot added T-test Testing related changes T-core Core library changes T-transport Transport layer changes labels Mar 10, 2026
@DaleSeo DaleSeo changed the title feat: transparent session re-init on HTTP 404 feat: transparent session re-init on HTTP 404 for streamable HTTP Mar 10, 2026
@DaleSeo DaleSeo self-assigned this Mar 10, 2026
.expect_initialized::<C::Error>()
.await?;

let new_session_id: Option<Arc<str>> = new_session_id_str.map(|s| Arc::from(s.as_str()));

Check failure

Code scanning / CodeQL

Cleartext logging of sensitive information High

This operation writes
new_session_id_str
to a log file.

Copilot Autofix

AI about 3 hours ago

In general, to fix cleartext logging of sensitive information, you either (a) remove the sensitive data from the log output entirely, or (b) mask/replace it with a redacted or hashed version that cannot be used to compromise security. You only log high‑level status (e.g., “session reinitialized”) rather than the exact session identifier or token.

For this specific code, the data in question is new_session_id_str / new_session_id, which likely contains a session identifier. The safe approach is to ensure that, when we log reinitialization, we do not ever include the actual session id. Since the only logging machinery visible in this file is tracing::debug, and CodeQL says that the mapping operation leads to a logging sink, the least‑intrusive fix is to add a dedicated debug message that explicitly avoids printing the session id, and not log new_session_id_str directly at all. To make the intent clear and to satisfy the analyzer, we can log only whether a session id was obtained (e.g., Some vs None), without including its content. This preserves existing functionality (session handling logic is unchanged) while removing any potential for accidentally logging the raw session identifier.

Concretely, within perform_reinitialization in crates/rmcp/src/transport/streamable_http_client.rs, right after we convert new_session_id_str into new_session_id, we’ll add a debug! call that reports only the presence/absence of the ID, not the ID value. We do not need any new imports (the file already imports tracing::debug). We avoid any change to how new_session_id is used elsewhere.

Suggested changeset 1
crates/rmcp/src/transport/streamable_http_client.rs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/crates/rmcp/src/transport/streamable_http_client.rs b/crates/rmcp/src/transport/streamable_http_client.rs
--- a/crates/rmcp/src/transport/streamable_http_client.rs
+++ b/crates/rmcp/src/transport/streamable_http_client.rs
@@ -343,6 +343,10 @@
             .await?;
 
         let new_session_id: Option<Arc<str>> = new_session_id_str.map(|s| Arc::from(s.as_str()));
+        debug!(
+            "Reinitialization completed; new session id obtained: {}",
+            if new_session_id.is_some() { "yes" } else { "no" }
+        );
 
         // Start from custom_headers, then inject the negotiated MCP-Protocol-Version
         // so all subsequent requests carry the right version (MCP 2025-06-18 spec).
EOF
@@ -343,6 +343,10 @@
.await?;

let new_session_id: Option<Arc<str>> = new_session_id_str.map(|s| Arc::from(s.as_str()));
debug!(
"Reinitialization completed; new session id obtained: {}",
if new_session_id.is_some() { "yes" } else { "no" }
);

// Start from custom_headers, then inject the negotiated MCP-Protocol-Version
// so all subsequent requests carry the right version (MCP 2025-06-18 spec).
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-test Testing related changes T-transport Transport layer changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Streamable HTTP client does not re-initialize session on HTTP 404 (MCP spec violation)

1 participant