fix(client): preserve agent_uri trailing slash; widen MCP URL fallbacks#582
Draft
fix(client): preserve agent_uri trailing slash; widen MCP URL fallbacks#582
Conversation
Fixes #581 The validate_agent_uri Pydantic validator was unconditionally stripping trailing slashes from AgentConfig.agent_uri before storing it. This caused 404s against FastMCP servers mounted at /mcp/ (and any other server where path trailing-slash semantics matter) because the stored URI silently lost user intent. Three changes: 1. core.py: remove rstrip("/") from the validator; preserve the caller-supplied form exactly. 2. mcp.py: update urls_to_try so the fallback list covers both the /mcp and /mcp/ forms regardless of which the user supplied. This means a single mis-mounted server no longer requires the caller to know the exact path form in advance. 3. capability_cache.py: normalize trailing slash inside build_capability_cache_key so http://host/mcp and http://host/mcp/ resolve to the same cache entry. Without this, the same logical agent would produce different cache keys depending on whether the caller supplied a trailing slash. Tests: four parametrized AgentConfig validator cases, four parametrized URL-fallback cases via TestMCPUrlFallback, and two updated fixture assertions in test_helpers.py. https://claude.ai/code/session_01DDfduQk3gWobDVbG8Ct6jk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #581
Summary
validate_agent_uriinAgentConfigwas callingv.rstrip("/")before storing the URI, silently dropping path trailing slashes. FastMCP servers (and StarletteMount-based servers generally) mount their streamable-HTTP endpoint at/mcp/and return 404 to/mcp. The client couldn't connect to servers built with the project's own server-side helpers.Three changes:
src/adcp/types/core.py— removerstrip("/")fromvalidate_agent_uri; preserve the caller-supplied URI exactly after the scheme check.src/adcp/protocols/mcp.py— updateurls_to_tryconstruction so both/mcpand/mcp/forms are tried regardless of which the user supplied. Previously only a no-slash/mcpfallback was appended.src/adcp/signing/capability_cache.py— normalize trailing slash insidebuild_capability_cache_keysohttp://host/mcpandhttp://host/mcp/produce the same cache entry. Without this, the same logical agent would produce different cache keys depending on whether the caller supplied a trailing slash.Upgrade note
AgentConfig.agent_urinow stores the value exactly as supplied. If your code reads backconfig.agent_uriand compares it to a hard-coded no-slash string (e.g.== "https://host/mcp"), update that comparison or strip the slash at the comparison site.What was tested
pytest tests/test_client.py::test_agent_uri_preserves_user_supplied_form— 4 parametrized validator round-trip casespytest tests/test_protocols.py::TestMCPUrlFallback— 4 parametrized URL-fallback cases covering/mcp/,/mcp, bare host, host-with-slashtest_helpers.pytest_ip_pinned_transport.py::test_real_tls_handshake_still_validates_hostnameexcluded — hitsexample.com, unrelated to this change)ruff check src/: all checks passedPre-PR review:
f"{base}/" if not uri.endswith("/") else base), considerbase if uri.endswith("/") else f"{base}/"for readabilitybuild_capability_cache_keydocstring "matches JS SDK exactly" may be stale after slash normalization; nit: no test assertingkey("https://x/mcp/") == key("https://x/mcp")Session: https://claude.ai/code/session_01DDfduQk3gWobDVbG8Ct6jk
Generated by Claude Code