Skip to content

fix(auth): structure protected resource discovery errors - #1305

Open
Niri7981 wants to merge 2 commits into
modelcontextprotocol:mainfrom
Niri7981:fix/structured-auth-errors
Open

Niri7981 wants to merge 2 commits into
modelcontextprotocol:mainfrom
Niri7981:fix/structured-auth-errors

Conversation

@Niri7981

Copy link
Copy Markdown

Summary

Replace protected-resource metadata discovery failures that were previously
collapsed into AuthError::MetadataError(String) with structured error variants.

Adds structured errors for:

  • discovery request / transport failures
  • invalid protected resource metadata
  • authorization servers advertised by protected resource metadata but unavailable

The transport failure variant preserves the underlying OAuthHttpClientError
as the error source instead of flattening the source chain into a string.

Invalid protected-resource metadata is further classified with structured reasons
for:

  • missing resource
  • invalid resource URL
  • resource URL fragments
  • resource mismatch
  • responses that are not metadata documents

Existing diagnostic text is preserved where practical.

Tests

  • RUSTUP_TOOLCHAIN=stable cargo test -p rmcp --features 'auth client transport-streamable-http-client' --test test_auth_error_classification
  • RUSTUP_TOOLCHAIN=stable cargo test -p rmcp --features 'auth client transport-streamable-http-client' --lib transport::auth::tests::
  • RUSTUP_TOOLCHAIN=stable cargo fmt -p rmcp -- --check
  • git diff --check

Addresses #1265.

@Niri7981
Niri7981 requested a review from a team as a code owner September 28, 2026 04:19
@github-actions github-actions Bot added T-test Testing related changes T-core Core library changes T-transport Transport layer changes labels Sep 28, 2026
@DaleSeo

DaleSeo commented Oct 5, 2026

Copy link
Copy Markdown
Member

The MSRV failure isn't from your change. It was fixed on main in #1316. Could you rebase onto the latest main and push? A re-run alone won't pick up the fix.

@Niri7981
Niri7981 force-pushed the fix/structured-auth-errors branch from aab2b72 to 78b7e28 Compare October 6, 2026 08:28
@Niri7981

Niri7981 commented Oct 6, 2026

Copy link
Copy Markdown
Author

The MSRV failure isn't from your change. It was fixed on main in #1316. Could you rebase onto the latest main and push? A re-run alone won't pick up the fix.

Rebasing onto the latest main is done and the branch has been updated. Thanks!

@chrikrah chrikrah left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Niri7981 approving at 78b7e28: the source chain test proves the OAuthHttpClientError now survives as source(), and the new tests do not compile against the base auth.rs.

$ cargo test -p rmcp --features 'auth client transport-streamable-http-client' --test test_auth_error_classification   # 78b7e28, rustc 1.96.1
test result: ok. 9 passed; 0 failed
$ cargo test -p rmcp --features 'auth client transport-streamable-http-client' --lib transport::auth::tests::
test result: ok. 202 passed; 0 failed; 265 filtered out
$ # auth.rs and transport.rs at 79437f2 (merge base), tests kept
error[E0599]: no variant named `DiscoveryRequestFailed` found for enum `AuthError`
error: could not compile `rmcp` (test "test_auth_error_classification") due to 4 previous errors
$ cargo fmt -p rmcp -- --check   # clean

non-blocking: DiscoveryRequestFailed (auth.rs:592) writes the cause chain into Display through ErrorChain. It also exposes that chain as #[source]. A reporter that walks source() after Display, such as anyhow in debug format, would print each cause twice. #1265 proposed Display without the chain. Was keeping the old text deliberate?

@DaleSeo all seven sites move, as #1265 asked. A MetadataError(msg) arm in caller code stops matching discovery failures and still compiles. VERSIONING.md puts new variants in a minor, but release-plz would cut a patch from the fix(auth): title. Should this go out as feat(auth):, with that arm change in the release notes? #1280 also targets #1265.

@Niri7981

Copy link
Copy Markdown
Author

@Niri7981 approving at 78b7e28: the source chain test proves the OAuthHttpClientError now survives as source(), and the new tests do not compile against the base auth.rs.

$ cargo test -p rmcp --features 'auth client transport-streamable-http-client' --test test_auth_error_classification   # 78b7e28, rustc 1.96.1
test result: ok. 9 passed; 0 failed
$ cargo test -p rmcp --features 'auth client transport-streamable-http-client' --lib transport::auth::tests::
test result: ok. 202 passed; 0 failed; 265 filtered out
$ # auth.rs and transport.rs at 79437f2 (merge base), tests kept
error[E0599]: no variant named `DiscoveryRequestFailed` found for enum `AuthError`
error: could not compile `rmcp` (test "test_auth_error_classification") due to 4 previous errors
$ cargo fmt -p rmcp -- --check   # clean

non-blocking: DiscoveryRequestFailed (auth.rs:592) writes the cause chain into Display through ErrorChain. It also exposes that chain as #[source]. A reporter that walks source() after Display, such as anyhow in debug format, would print each cause twice. #1265 proposed Display without the chain. Was keeping the old text deliberate?

@DaleSeo all seven sites move, as #1265 asked. A MetadataError(msg) arm in caller code stops matching discovery failures and still compiles. VERSIONING.md puts new variants in a minor, but release-plz would cut a patch from the fix(auth): title. Should this go out as feat(auth):, with that arm change in the release notes? #1280 also targets #1265.

I kept the existing diagnostic text intentionally to minimize user-visible changes, but I agree that now that the underlying error is preserved as #[source], rendering the full chain in Display can duplicate causes for reporters that walk source(). I'm happy to simplify DiscoveryRequestFailed's Display to match #1265 if that's preferred.

@chrikrah chrikrah left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Niri7981, from my side the Display without the chain is the better default now that the cause is reachable through source(). My approval at 78b7e28 stands either way.

That change leaves ErrorChain (error.rs:17) with no caller, so it has to go in the same commit: ci.yml:63 runs clippy with -D warnings. @DaleSeo's review of #1280 accepts the same text change and asks for a to_string() check next to each matches!, which the assertion at auth.rs:4358 gives you here.

$ cargo clippy -p rmcp --lib --features 'auth client transport-streamable-http-client' -- -D warnings   # rustc 1.96.1
# 78b7e28 as is: 8 errors, all feature-gated dead code from this partial feature set
# 78b7e28, #[error] on DiscoveryRequestFailed without the chain: 9 errors, the new one is
error: struct `ErrorChain` is never constructed
  --> crates/rmcp/src/error.rs:17:19
# not run: --all-features (openssl-sys needs pkg-config here)

@DaleSeo, does that text change still fit a fix(auth): title, or should it go out as feat(auth): with a release note?

This branch has not been deployed

No deployments
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.

3 participants