Problem
adcp.server.a2a_server._build_agent_card hardcodes http://localhost:{port}/ at server-init time. There's no hook for injecting a public URL.
In production the bound socket is internal — the public URL comes from the load balancer's X-Forwarded-Host (or the request Host). Without intervention, the static localhost URL leaks into /.well-known/agent-card.json, and SDK clients that read the card to discover the JSON-RPC endpoint try to reach http://localhost:8080/ from outside the container, failing every A2A request with fetch failed.
Workaround
We run a 190-LOC ASGI middleware (AgentCardPublicUrlMiddleware) that intercepts the /.well-known/agent-card.json response, parses the JSON body, rewrites the URL fields based on X-Forwarded-Host/Host/X-Forwarded-Proto, and sends the modified payload. Rewrites both top-level url and every supportedInterfaces[].url. Only rewrites loopback URLs so explicit configurations pass through.
This is brittle — buffer the response, parse JSON, mutate, re-encode, recompute Content-Length, forward. One bug in the buffering and discovery silently breaks.
Proposed SDK shape
Either:
A) serve(public_url=...) kwarg — explicit override, simplest. Adopters set it once at boot; framework uses it instead of the bound-socket URL when building the card.
B) Honour X-Forwarded-Host natively — when an inbound request to /.well-known/agent-card.json carries X-Forwarded-Host (or X-Forwarded-Proto), the framework derives the public URL from those headers and uses them in the response. Matches HTTP-server convention; no new public API needed beyond a trust_forwarded_headers=True opt-in.
C) Both — public_url= for static config, X-Forwarded-Host for dynamic deployments behind a reverse proxy.
Files
Related
Problem
adcp.server.a2a_server._build_agent_cardhardcodeshttp://localhost:{port}/at server-init time. There's no hook for injecting a public URL.In production the bound socket is internal — the public URL comes from the load balancer's
X-Forwarded-Host(or the requestHost). Without intervention, the static localhost URL leaks into/.well-known/agent-card.json, and SDK clients that read the card to discover the JSON-RPC endpoint try to reachhttp://localhost:8080/from outside the container, failing every A2A request withfetch failed.Workaround
We run a 190-LOC ASGI middleware (
AgentCardPublicUrlMiddleware) that intercepts the/.well-known/agent-card.jsonresponse, parses the JSON body, rewrites the URL fields based onX-Forwarded-Host/Host/X-Forwarded-Proto, and sends the modified payload. Rewrites both top-levelurland everysupportedInterfaces[].url. Only rewrites loopback URLs so explicit configurations pass through.This is brittle — buffer the response, parse JSON, mutate, re-encode, recompute
Content-Length, forward. One bug in the buffering and discovery silently breaks.Proposed SDK shape
Either:
A)
serve(public_url=...)kwarg — explicit override, simplest. Adopters set it once at boot; framework uses it instead of the bound-socket URL when building the card.B) Honour
X-Forwarded-Hostnatively — when an inbound request to/.well-known/agent-card.jsoncarriesX-Forwarded-Host(orX-Forwarded-Proto), the framework derives the public URL from those headers and uses them in the response. Matches HTTP-server convention; no new public API needed beyond atrust_forwarded_headers=Trueopt-in.C) Both —
public_url=for static config,X-Forwarded-Hostfor dynamic deployments behind a reverse proxy.Files
Related
/.well-known/agent.json0.3 alias 404. Solving that doesn't help with the public-URL leak; both need fixing.