Skip to content

feat(a2a): agent-card public URL injection (X-Forwarded-Host aware) #616

@bokelley

Description

@bokelley

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) Bothpublic_url= for static config, X-Forwarded-Host for dynamic deployments behind a reverse proxy.

Files

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions