Skip to content

feat(a2a): per-request agent-card URL resolution (follow-up to #616) #647

Description

@bokelley

Problem (follow-up to closed #616)

PR for #616 shipped Option A — the static serve(public_url=...) kwarg — and the issue closed COMPLETED. Option B (trust_forwarded_headers for per-request resolution) was explicitly deferred per the dx-expert review.

This is fine for single-host deployments. For multi-tenant subdomain deployments, where every tenant has its own public host, the static kwarg can only advertise one URL. Adopters still need a per-request rewrite to surface the correct tenant host in /.well-known/agent-card.json.

Concrete adopter scenario

bokelley/salesagent runs multi-tenant on subdomains: acme.scope3.com, beta.scope3.com, etc. Each tenant's buyer agent fetches the agent card from its own host. A static public_url=https://scope3.com/ would advertise the wrong host to every non-apex tenant.

Today we work around this with a 190-LOC ASGI middleware (core/middleware/agent_card_public_url.py) that buffers the /.well-known/agent-card.json response and rewrites localhost URLs from X-Forwarded-Host per request. It composes cleanly with the new static kwarg (only rewrites loopback, so it no-ops when public_url is set) but it's adopter-side code maintaining an SDK-shaped behaviour.

Proposed shape

Two options, both addressing the per-request case the deferral explicitly called out:

A) serve(trust_forwarded_headers=True) — when set, the agent-card route honours X-Forwarded-Host and X-Forwarded-Proto at response-build time. Defaults to False so unconfigured behaviour is unchanged. Concerns from the original deferral:

  • Trust boundary: gate via the kwarg — adopters explicitly opt in only when they know they're behind a trusted proxy. Matches how Flask / FastAPI / starlette handle the same headers.
  • Thread-safety on shared pb.AgentCard: build the response card per-request rather than once at server-init. The card payload is small, the marginal CPU is negligible vs. the network IO.

B) serve(public_url=Callable[[Request], str]) — accept a callable in addition to a static string. Adopters provide the resolution logic themselves. More general, less opinionated about trust boundaries.

Either closes the multi-tenant subdomain gap. A is the cleaner zero-config option for the common case (most multi-tenant adopters have a reverse proxy that sends X-Forwarded-Host); B keeps the SDK out of the trust-policy business at the cost of every adopter writing the resolver.

Why a new issue vs reopening #616

#616 closed COMPLETED — fine, the static kwarg is real and useful. Re-opening it would muddy the changelog. This is a separate ask that depends on the closed work landing first.

Source

Our deferral entry in bokelley/salesagent core/SDK_FEEDBACK.md: "make the agent-card URL X-Forwarded-Host-aware when no static public_url is configured, OR accept a Callable[[Request], str] for per-request resolution."

Local tracker: bokelley/salesagent#103.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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