feat(a2a): add public_url param to agent card for production deployments#621
Merged
feat(a2a): add public_url param to agent card for production deployments#621
Conversation
Fixes #616 Adds `public_url` to `_build_agent_card`, `create_a2a_server`, `serve`, and `ServeConfig` so adopters running behind a load balancer or reverse proxy can advertise the correct public URL in `/.well-known/agent-card.json` instead of leaking `http://localhost:{port}/`. Also checks the `PUBLIC_URL` environment variable as a zero-code-change fallback for Cloud Run / Fly.io / Railway deployments. https://claude.ai/code/session_01NXgGie4ARyxg3hWBYo5tG6
- Add public_url docstring to serve() (was missing despite being present in create_a2a_server) - Normalise trailing slash so callers can pass "https://x.com" or "https://x.com/" - Remove redundant `or None` from resolved_public_url assignment - Remove dead handler_ref loop from test https://claude.ai/code/session_01NXgGie4ARyxg3hWBYo5tG6
- Assert trailing-slash normalisation (URL without trailing slash) - Assert create_a2a_server defaults to localhost when PUBLIC_URL unset https://claude.ai/code/session_01NXgGie4ARyxg3hWBYo5tG6
1fcdf68 to
c41e6f7
Compare
Contributor
Author
Pre-merge expert pass (fresh independent review, post-rebase)
Suggested follow-ups (separate issue, not blocking this PR):
Admin-merging once CI re-completes post-rebase. |
Contributor
Author
|
Acknowledged. Three independent expert passes (code-reviewer, security-reviewer, ad-tech-protocol-expert) all approved with no blockers — good shape to merge. The four follow-up items are worth filing as separate issues so they don't get lost:
Triaged by Claude Code. Generated by Claude Code |
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 #616
Summary
_build_agent_cardhardcodeshttp://localhost:{port}/in the A2A agent card at server-init time. In production behind a load balancer the public URL comes from outside the container — the static localhost URL leaks into/.well-known/agent-card.json, causing every A2A request from SDK clients that use the card for discovery to fail withfetch failed.This PR adds a
public_urlparameter to replace that default across the full serving stack.Changes:
public_url: str | None = Noneadded to_build_agent_card,create_a2a_server,serve,ServeConfig,_serve_a2a,_build_mcp_and_a2a_app,_serve_mcp_and_a2apublic_urlreplaceshttp://localhost:{port}/in allsupportedInterfacesURL entries; trailing slash is normalised so both"https://x.com"and"https://x.com/"workPUBLIC_URLenv var fallback increate_a2a_server— zero-code-change for Cloud Run / Fly.io / Railway; kwarg takes precedence when both are setpublic_urladded to_a2a_onlyinServeConfig.__post_init__so the cross-transport warning fires correctlyNonepreserves existinghttp://localhost:{port}/behaviourOut of scope: dynamic
trust_forwarded_headersper-request URL derivation (option B/C from the issue). Both the DX and code-review experts flagged thread-safety and header-trust-boundary concerns; deferred to a follow-up issue.What was tested
ruff check— cleanmypy— no new errors in changed files (pre-existing errors in unrelated generated types)pytest tests/test_a2a_server.py— 45/45 pass (5 new tests added)pytest tests/(unit tier, excluding integration/conformance) — 3759 passed, 18 skipped, 1 xfailedNew tests:
test_build_agent_card_public_url_overrides_localhost— unit, verifies allsupported_interfacesURLs use the overridetest_build_agent_card_public_url_none_uses_localhost— unit, verifies default unchangedtest_create_a2a_server_public_url_in_card— integration via TestClient hitting/.well-known/agent-card.jsontest_create_a2a_server_public_url_env_var— env var fallbacktest_create_a2a_server_public_url_kwarg_takes_precedence_over_env— kwarg wins over env varPre-PR review
serve()docstring) fixed, nits (trailing-slash normalisation, redundantor None, dead test code) fixedpublic_urlnaming correct (distinct from existingbase_urlfor MCP discovery manifest), env-var fallback is the right zero-config pattern for PaaS deployments,ServeConfigcoverage is completeNits surfaced (not fixed — noted here per convention):
PUBLIC_URLenv var not surfaced inServeConfigfield-level comment; only documented increate_a2a_serverdocstring. Low impact — caller usingServeConfigcan passpublic_url=explicitly.Session: https://claude.ai/code/session_01NXgGie4ARyxg3hWBYo5tG6
Generated by Claude Code