Skip to content

fix: preserve legacy fallback after rejected discovery - #1269

Open
JulienCote wants to merge 2 commits into
modelcontextprotocol:mainfrom
JulienCote:fix/legacy-discovery-fallback
Open

JulienCote wants to merge 2 commits into
modelcontextprotocol:mainfrom
JulienCote:fix/legacy-discovery-fallback

Conversation

@JulienCote

Copy link
Copy Markdown

Fix legacy fallback after a rejected server/discover request.

Motivation and Context

A client supporting both lifecycle models should probe with server/discover, then fall back to initialize when the server reports that it is legacy.

rmcp previously marked the connection as requiring modern per-request metadata before discovery completed. If discovery returned an error, the client correctly fell back to initialize, but the server retained the modern metadata requirement and rejected subsequent legacy requests.

This change:

  • Treats servers advertising only initialization-based protocol versions as legacy and returns MethodNotFound without dispatching discovery.
  • Keeps the server in its pre-initialization state when discovery is rejected.
  • Requires per-request metadata only after a request successfully establishes the modern lifecycle.

How Has This Been Tested?

Added separate regression tests covering:

  • A legacy-only server rejects discovery before invoking its discovery handler.
  • A rejected discovery can be followed by legacy initialize and a metadata-light tools/list.

cargo test --all-features otherwise passed 568 tests, but the unrelated Windows-specific transport::auth::tests::default_http_client_preserves_connection_failure_cause assertion failed due to the platform's connection-refused error shape.

Breaking Changes

None. This restores the expected backward-compatible lifecycle behavior.

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

The stdio backward-compatibility rules require clients to fall back on discovery errors other than recognized modern errors. The server therefore must not commit the connection to the modern lifecycle until discovery succeeds

@JulienCote
JulienCote requested a review from a team as a code owner September 14, 2026 16:44
@github-actions github-actions Bot added T-test Testing related changes T-core Core library changes T-handler Handler implementation changes T-service Service layer changes labels Sep 14, 2026
A dual-lifecycle stdio client probes a server with server/discover before deciding
whether to use the modern per-request metadata lifecycle or the legacy initialize
handshake.

rmcp previously marked the connection as requiring per-request metadata before the
discovery handler completed. When discovery returned an error identifying a legacy
server, the auto client correctly fell back to initialize on the same connection,
but the server retained the modern metadata requirement and rejected later legacy
requests.

Derive discovery support from supported_protocol_versions. Servers that advertise
only initialization-based versions return MethodNotFound without dispatching
discovery. Keep rejected discovery requests in the pre-initialization loop, and
require per-request metadata only after an inline request establishes the modern
lifecycle.

Add separate regression coverage for legacy-only version configuration and for
rejected discovery followed by initialize and a metadata-light tools/list request.
Update the HTTP discovery test server to advertise the discovery lifecycle.

Validation:

- cargo +nightly fmt --all
- cargo clippy --all-targets --all-features -- -D warnings
- affected client/server lifecycle tests pass
- cargo build --all-features
- cargo test --all-features is blocked by the unrelated Windows-only
  transport::auth::tests::default_http_client_preserves_connection_failure_cause
  assertion

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2437c70d-f526-4b60-8ee4-effca05aadd4
@JulienCote
JulienCote force-pushed the fix/legacy-discovery-fallback branch from 60b94ea to 61e1d60 Compare September 14, 2026 21:41
The reverse cancellation test uses InitializeResult only when the client
feature is enabled without local support. Import it in the matching
configuration so the all-features-except-local CI jobs compile.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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-handler Handler implementation changes T-service Service layer changes T-test Testing related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant