Skip to content

fix: add init_timeout for streamable-http sessions#811

Merged
DaleSeo merged 1 commit intomainfrom
fix/issue-808-init-timeout
May 1, 2026
Merged

fix: add init_timeout for streamable-http sessions#811
DaleSeo merged 1 commit intomainfrom
fix/issue-808-init-timeout

Conversation

@DaleSeo
Copy link
Copy Markdown
Member

@DaleSeo DaleSeo commented Apr 17, 2026

Fixes #808.

Motivation and Context

The LocalSessionWorker::run() function waited for the first initialize request using just self.event_rx.recv().await without a timeout. Any HTTP POST that created a session but didn’t follow up could keep a worker task running indefinitely. The only way to free it was through a server shutdown, which eventually led to memory exhaustion.

The post-init loop already had a keep_alive timeout in place, but there was an issue with the pre-init path that created an asymmetric gap.

How Has This Been Tested?

Added two integration tests

Breaking Changes

No API breaks. Both SessionConfig and LocalSessionWorkerError are #[non_exhaustive].

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

@DaleSeo DaleSeo self-assigned this Apr 17, 2026
@github-actions github-actions Bot added T-test Testing related changes T-core Core library changes T-transport Transport layer changes labels Apr 17, 2026
@DaleSeo DaleSeo marked this pull request as ready for review April 17, 2026 01:49
@DaleSeo DaleSeo requested a review from a team as a code owner April 17, 2026 01:49
@DaleSeo DaleSeo force-pushed the fix/issue-808-init-timeout branch from 3ec3a2a to ddd3da7 Compare April 17, 2026 14:13
"get initialize request",
)
})?;
let init_timeout = self.session_config.init_timeout.unwrap_or(Duration::MAX);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why the _or(Duration::MAX) here. Aren't we setting a default init timeout of 60s?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexhancock You're right. The default is 60s but users can set it to None to disable the timeout. unwrap_or(Duration::MAX) handles that opt-out case. This mirrors how keep_alive is handled a few lines below.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but I was thinking we should not have

pub init_timeout: Option<Duration>
pub init_timeout: Duration

seems more clear to me in this situation where it has a builtin default that is always set. It would make it so if the user wants to make it really long they could do the Duration::MAX

@DaleSeo DaleSeo merged commit c1e0ead into main May 1, 2026
17 checks passed
@DaleSeo DaleSeo deleted the fix/issue-808-init-timeout branch May 1, 2026 13:20
@github-actions github-actions Bot mentioned this pull request May 1, 2026
jrmelsha added a commit to UserGeneratedLLC/rmcp-rust-sdk that referenced this pull request May 1, 2026
Brings in upstream rmcp v1.6.0:
- fix(http): fall back to :authority for HTTP/2 (modelcontextprotocol#827)
- fix: add init_timeout for streamable-http sessions (modelcontextprotocol#811)
- feat(http): log Host/Origin rejections (modelcontextprotocol#826)

Conflict resolution:
- crates/rmcp/CHANGELOG.md: kept fork's bare-boolean Unreleased entry,
  inserted upstream's 1.6.0 release section beneath it
- crates/rmcp/src/transport/streamable_http_server/tower.rs:
  auto-merged upstream's Host/Origin/HTTP-2 logging additions; kept
  fork's tracing::debug! for "Resume failed" (ab4ccdb) over upstream's
  tracing::warn! revert
- Cargo.toml workspace bumped to 1.6.0; fork's newer dep versions
  (pastey 0.2.2, schemars 1.2, reqwest 0.13.3, url 2.5,
  process-wrap 9.1, chrono 0.4.44) preserved; rand stays removed
  (ed5868d) since fork doesn't use it
- docs.rs anthropic-ext feature retained
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.

Sessions stuck in pre-init state have no timeout, leak indefinitely

2 participants