Skip to content

fix(server): keep discover lifecycle bootstrap-neutral - #1248

Merged
DaleSeo merged 2 commits into
modelcontextprotocol:mainfrom
faisal-shah:fix/discover-bootstrap
Oct 5, 2026
Merged

DaleSeo merged 2 commits into
modelcontextprotocol:mainfrom
faisal-shah:fix/discover-bootstrap

Conversation

@faisal-shah

Copy link
Copy Markdown
Contributor

Summary

  • keep metadata-bearing server/discover requests bootstrap-neutral
  • select the classic lifecycle on a subsequent initialize
  • select the inline lifecycle only on the first valid ordinary metadata-bearing request
  • leave malformed and unsupported bootstrap requests recoverable
  • enqueue the lifecycle-selecting inline request into the normal service loop so bidirectional handlers remain safe

Verification

  • cargo test --package rmcp --features "client transport-io macros schemars"
  • cargo clippy --package rmcp --features "client transport-io macros schemars" --lib --test test_server_initialization --test test_stateless_server_requests --test test_subscriptions -- -D warnings

@faisal-shah
faisal-shah requested a review from a team as a code owner September 7, 2026 07:07
@github-actions github-actions Bot added T-test Testing related changes T-core Core library changes T-service Service layer changes labels Sep 7, 2026
Comment thread crates/rmcp/src/service/server.rs Outdated
Comment thread crates/rmcp/src/service/server.rs Outdated
Comment thread crates/rmcp/src/service/server.rs Outdated
@tenequm

tenequm commented Oct 2, 2026

Copy link
Copy Markdown

We hit this in production: Claude Code sometimes opens a stdio session with a bare tools/list (no _meta). On 3.5.0 the server answers -32602 and then exits, so the client never falls back to initialize. This PR fixes it for us. Any chance of moving it forward? Happy to help address the review comments.

@DaleSeo

DaleSeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

@faisal-shah Since a user is hitting this in production, I went ahead and pushed the review fixes.

@tenequm We'll include this fix in the next release.

@DaleSeo
DaleSeo force-pushed the fix/discover-bootstrap branch from 68b1f4a to c76ff8f Compare October 5, 2026 16:54
@DaleSeo
DaleSeo merged commit 4a226c7 into modelcontextprotocol:main Oct 5, 2026
24 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 5, 2026
fastrevmd-lab added a commit to mechubsec/rustsdcmcp that referenced this pull request Oct 6, 2026
…lure (#218)

A client that probes `server/discover` before `initialize` (e.g. Claude
Code) permanently latched rmcp's internal `request_metadata_required`
flag on the session, even after falling back to the classic
(`initialize`-negotiated) lifecycle. Every later request without
per-request `_meta` -- including an ordinary `tools/list` -- was then
rejected with -32602, so the client loaded zero tools.

Upstream fix: modelcontextprotocol/rust-sdk#1248, shipped in rmcp 3.5.1.

Adds a stdio regression test driving the exact discover -> initialize ->
notifications/initialized -> tools/list sequence over an in-memory
duplex transport; it reproduces the -32602 failure against the prior
rmcp 3.5.0 pin and passes against 3.5.1.

Closes #217

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
meta-codesync Bot pushed a commit to facebookexperimental/dapper that referenced this pull request Oct 13, 2026
Summary:
Claude Code can time out a discovery probe and fall back to legacy initialization. In `rmcp` 3.5.0 the probe latches modern request-metadata validation, so subsequent legacy catalog requests fail and Claude exposes no tools. Import official `rmcp` and `rmcp-macros` 3.5.1 through Reindeer, including upstream PR #1248: modelcontextprotocol/rust-sdk#1248. Discovery remains neutral until initialization or a valid non-discovery request selects the lifecycle.

The SDK also supplies missing modern catalog/resource cache hints centrally, allowing downstream duplicate cache-hint handlers to be removed. Legacy proxy result completion and application-specific subscription/socket fixes remain separate.

Only `rmcp` and `rmcp-macros` change versions in `Cargo.lock`; no transitive dependency versions change. Vendored sources match the official crate checksums.

Update Symphony startup assertions for the SDK-provided modern cache fields in the same import, and repair documentation links to the renamed vendored sources.

Reviewed By: dtolnay

Differential Revision: D124891804

fbshipit-source-id: bfc1183f0d8f37b37660684fb7ab4ec2ea6380ba
tenequm added a commit to tenequm/pond that referenced this pull request Oct 13, 2026
…350)

## What broke

Claude Code intermittently showed pond as `connected - tools fetch
failed`, with no tools and no resources:

```
request _meta is missing or has malformed required fields: io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientCapabilities
```

Claude Code opens a stdio server with a `server/discover` probe. `pond
mcp` opens the store before it reads stdin, so on a slow open (3.3 s
here, against 1.4-2.2 s on the good starts) the probe reply misses
Claude Code's wait and it falls back to a classic `initialize`. Both
requests then reach pond. On rmcp 3.5.0 the discover probe committed the
connection to the 2026-07-28 inline lifecycle, so pond answered
`initialize` at 2025-11-25 and then rejected the classic `tools/list`
and `resources/list` that followed, for lacking 2026-07-28 `_meta`.
Claude Code's MCP log shows the sequence:

```
Connection established ... "protocolEra":"legacy","negotiatedProtocolVersion":"2025-11-25"
stdio server answered the server/discover probe after it had connected legacy ...
Failed to fetch resources: request _meta is missing or has malformed required fields: ...
Failed to fetch tools: request _meta is missing or has malformed required fields: ...
```

Reproduced on the released 0.19.6 two ways: a `claude -p` run against
`pond mcp` behind a 4 s startup delay (no pond tools), and a script that
writes discover, initialize, initialized and tools/list back to back
(`tools/list` gets `-32602`). This is not the #334 failure: there the
first request lacked `_meta`; here it is a complete discover probe.

## Fix

- rmcp 3.5.0 -> 3.5.1. It ships
[rust-sdk#1248](modelcontextprotocol/rust-sdk#1248),
which keeps a discover probe from selecting a lifecycle, so a following
`initialize` selects the classic one. The same change answers a
malformed first request with `-32602` and keeps reading.
- That second half is what `RecoverableOpener` (#334) did by hand, and
its doc said to drop it once #1248 shipped, so it is removed. `serve_io`
passes the plain transport again.

## Tests

- New `discover_probe_then_initialize_serves_classic_requests`:
discover, initialize, initialized and tools/list written back to back.
`tools/list` must return tools.
- `malformed_opener_leaves_initialize_fallback_open` is kept as the
guard for what the wrapper covered, now against rmcp's own handling.
Both tests share an `exchange` helper that writes raw frames and
collects replies by id.

## Verified

- `cargo test --lib transport::mcp::` (12 passed), `cargo clippy
--all-targets -- -D warnings`, `cargo fmt --check`.
- The back-to-back script against this build: `tools/list` returns the 4
tools (`-32602` on 0.19.6).
- `claude -p` (Claude Code 2.1.296) against this build behind the same 4
s startup delay: all 4 pond tools present, and the MCP log reads
"answered the server/discover probe after its bound, so it was only slow
to start and speaks the new protocol".

## Release note

`pond mcp` no longer loses its tools when Claude Code's discovery probe
and its `initialize` fallback both reach a slow-starting server, which
showed pond as connected with "tools fetch failed" until a manual
reconnect. No action needed.
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-service Service layer changes T-test Testing related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants