Skip to content

feat: implement identity.brand_json_url resolver + verifier (resolve_agent / verify_request_signature) + CLI #344

@bokelley

Description

@bokelley

Summary

AdCP 3.x adds identity.brand_json_url to get_adcp_capabilities (PR adcontextprotocol/adcp#3690). Verifiers can now bootstrap from an agent URL to that agent's signing keys without out-of-band knowledge of the operator domain. This issue tracks the Python SDK port.

Spec references

API to implement

from adcp import resolve_agent, get_agent_jwks, verify_request_signature

result = resolve_agent("https://buyer.example.com/mcp")
# result: AgentResolution { agent_url, brand_json_url, agent_entry, jwks_uri,
#                          jwks, identity_posture, consistency, freshness, trace }

verified = verify_request_signature(
    request,                                  # httpx.Request
    agent_url="https://buyer.example.com/mcp",
    allowed_algs=("EdDSA",),
)
# Raises typed exceptions matching the spec's request_signature_* error codes.

CLI

python -m adcp resolve <agent-url> — same output format as the TypeScript CLI (npx @adcp/client resolve). Flags: --json, --fresh, --quiet. Output portable across SDKs so shell pipelines work either way.

Implementation notes

  • SSRF hardening: HTTPS only, address-family filter before DNS-pin, IPv4 RFC 1918 + IPv6 ULA + cloud-metadata IP blocks, body cap 256 KiB on brand.json / 16 KiB JWKS / 64 KiB capabilities, no redirects, connect 5s / total 10s. httpx provides hooks for connect-time IP filtering.
  • PSL: use publicsuffixlist or tldextract with a pinned snapshot. Don't fetch the PSL at runtime.
  • Cache: brand.json TTL bounded above by JWKS revocation polling interval. Negative-cache ≤ 60s. No SWR on JWKS.
  • Error class: AgentResolverError(Exception) with code and detail matching the spec's taxonomy. Subclasses keyed by error code for except clarity.
  • Pydantic models for AgentResolution, Trace, IdentityPosture, etc.
  • Reference design: see adcontextprotocol/adcp@bd89c62ff4 for the dropped Node.js reference implementation — the algorithm/structure ports cleanly.

Tests

  • Unit: pure-function consistency tests covering every storyboard variant
  • Integration: spin up a fixture HTTP server with brand.json + JWKS, drive the resolver and assert wire shape, error codes, JOSE round-trip via python-jose
  • E2E gated by env var against a known-good agent

Acceptance

  • resolve_agent, get_agent_jwks, verify_request_signature exported from adcp
  • CLI python -m adcp resolve <url> printing the chain + trace
  • All 8 request_signature_* codes surface as typed exceptions
  • SSRF hardening covers the full list from security.mdx §"Quickstart"

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