Skip to content

feat: add mcp:// discovery URI resolution (draft-serra-mcp-discovery-uri)#952

Open
vincenzopalazzo wants to merge 1 commit into
modelcontextprotocol:mainfrom
vincenzopalazzo:feat/mcp-discovery-uri
Open

feat: add mcp:// discovery URI resolution (draft-serra-mcp-discovery-uri)#952
vincenzopalazzo wants to merge 1 commit into
modelcontextprotocol:mainfrom
vincenzopalazzo:feat/mcp-discovery-uri

Conversation

@vincenzopalazzo

Copy link
Copy Markdown

Summary

Implements the client-side resolver for IETF draft-serra-mcp-discovery-uri: resolve an mcp://host[:port][/path] URI to a working MCP server endpoint.

This was originally submitted as a goose core PR (aaif-goose/goose#9660) but @DOsinga correctly identified that the resolver is generic plumbing with nothing goose-specific — it belongs here in rmcp.

Resolution chain:

  1. (optional fast-mode) DNS TXT _mcp.{host} for endpoint hint, _mcp-key.{host} for JWK public key used to verify manifest signatures
  2. (authoritative) GET https://{host}/.well-known/mcp-server manifest
  3. (fallback) direct handshake probe at https://{host}/mcp

Security posture: HTTPS-only; endpoint host must equal-or-be-subdomain of discovery host; detached JWS signature verified against DNS-published JWK; fail-closed when a manifest claims a signature we cannot verify.

Feature-gated behind discovery (DNS + HTTP resolver) and discovery-jws (adds JWS signature verification via jsonwebtoken).

cc @alexhancock

Review Notes

  • Production-safety review passed (2 rounds)
  • Code is fully unit-tested with mock DNS and HTTP backends (32 tests pass)
  • 3 E2E tests are #[ignore]d — they need real TLS infrastructure to test HTTPS resolution against a live server
  • Adapted to hickory-resolver 0.26 API (builder_tokio() requires system-config feature; txt_data is a field not a method)
  • All exported types are #[non_exhaustive] per rmcp's exhaustive_structs / exhaustive_enums clippy lints
  • cargo clippy -D warnings clean, cargo fmt applied

Decision Log

Hardest decision: Marking the 3 E2E tests (e2e_well_known_manifest_resolution, e2e_direct_fallback_resolution, e2e_no_server_found) as #[ignore] rather than trying to set up a self-signed TLS server in unit tests. The resolver always builds https:// URLs from mcp:// URIs (correct per spec), but spinning up a TLS-enabled axum server with self-signed certs in a unit test adds significant complexity for marginal coverage — the resolution logic is fully exercised by the 32 mock-based tests. The E2E tests are left in place for manual/integration testing.

Alternatives rejected:

  • HTTP fallback for localhost: Adding a special case to allow http:// for 127.0.0.1 / localhost would violate the spec's HTTPS-only requirement and create a security hole.
  • rustls danger_insecure_verify: Using reqwest's insecure TLS mode in tests would require a separate feature gate and is fragile.

Least confident about: The JWS canonicalization in jws.rs — it's a JCS-style approximation (recursive key sort + compact serialization) that covers typical manifests but does not implement full RFC 8785 number/unicode normalization. A mismatch fails closed (rejects a valid signature), never the reverse, but a reviewer with crypto expertise should confirm this is acceptable for the draft stage.

Test plan

  • CI passes
  • cargo test -p rmcp --features "discovery,discovery-jws" --lib discovery — 32 tests pass, 3 ignored
  • cargo clippy -p rmcp --features "discovery,discovery-jws" -- -D warnings — clean
  • cargo fmt --check — clean

…uri)

Implements the client-side resolver for IETF draft-serra-mcp-discovery-uri:
resolve an mcp://host[:port][/path] URI to a working MCP server endpoint.

Resolution chain:
1. (optional fast-mode) DNS TXT _mcp.{host} for endpoint hint,
   _mcp-key.{host} for JWK public key used to verify manifest signatures
2. (authoritative) GET https://{host}/.well-known/mcp-server manifest
3. (fallback) direct handshake probe at https://{host}/mcp

Security posture: HTTPS-only; endpoint host must equal-or-be-subdomain of
discovery host; detached JWS signature verified against DNS-published JWK;
fail-closed when a manifest claims a signature we cannot verify.

Feature-gated behind 'discovery' (DNS+HTTP resolver) and 'discovery-jws'
(adds JWS signature verification via jsonwebtoken).

Co-Authored-By: goose <goose@aaif.ai>
@vincenzopalazzo vincenzopalazzo requested a review from a team as a code owner July 2, 2026 18:47
@github-actions github-actions Bot added T-dependencies Dependencies related changes T-test Testing related changes T-config Configuration file changes T-core Core library changes labels Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-config Configuration file changes T-core Core library changes T-dependencies Dependencies related changes T-test Testing related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant