webGCP discovery interop checklist (pre-L0)

What this is. The runnable discovery subset of webGCP interop for implementers that publish discovery surfaces but do not yet serve /webgcp/v0/query. The full L0 suite (/conformance/v0.1/ — WGCP-L0-001/002/003, 27 assertions, runner.py) requires a query endpoint; this checklist is what an implementation can prove before that endpoint exists. Passing it is not an L0 claim — it is the verified precondition that a restricted-domain implementer established in practice (2026-05-21), made repeatable.

Scope note (carrier precedence).CORRECTED 2026-07-28. An earlier revision of this note called the A2A agent card's extensions.webgcp block "the canonical carrier" and described /.well-known/webgcp as a back-compat mirror. That was a drafting error and is withdrawn. It inverted the normative order. The precedence is:

  1. The frozen v0.1 spec is normative and settles this. §5.1 MUSTs the /.well-known/webgcp descriptor at L0+, and §5.1.1 MUSTs the _webgcp.<host> SVCB record. Frozen text is byte- and URL-stable; no later document demotes a frozen MUST.
  2. A2A is MAY at every conformance level. It is not mentioned in v0.1 or in the v0.2 candidate at all. The external-protocols composition reference states this identically: publishing an agent card is operator-discretionary discoverability and does not affect conformance level.
  3. The 2026-06-09 profile redline does not say otherwise. It is a design target with open gates, it promotes the webMCP manifest — not the A2A agent card — and it independently records A2A as MAY at all levels. Its own default for the descriptor is a permanent mirror, and the sunset question is open, not decided. The error was reading a note that one implementer "is A2A-canonical" — a description of that implementer's own stack, which is Google-ADK/A2A-native — as a statement about webGCP's carrier hierarchy. It is not one.

What an A2A-native implementer actually does is unchanged, and this checklist already tested it correctly: the agent card carries an extensions.webgcp block whose descriptor member points at /.well-known/webgcp, and the descriptor holds the payload — spec_version, server_id, conformance_level, endpoints (D-2.A03, and the worked run below). The agent card is a discovery breadcrumb; the descriptor is the carrier. The checklist covers both surfaces; D-3 asserts their agreement.


D-1 — Descriptor surface (GET /.well-known/webgcp)

# Assertion How
D-1.A01 HTTP 200 with Content-Type: application/json curl -si http://<host>/.well-known/webgcp
D-1.A02 spec_version present — "0.1" (plain v0.1 server) or "0.2-profile" with profile_of: "webmcp" (profile carrier) jq '.spec_version, .profile_of'
D-1.A03 server_id is a typed URN (urn:webgcp:<id>) jq '.server_id'
D-1.A04 conformance_level declared honestly — "L0-restricted-domain" MUST carry a restricted_domain_reason jq '.conformance_level, .restricted_domain_reason'
D-1.A05 endpoints object present; absent capabilities are explicit null, never omitted or faked (e.g., endpoints.query: null until a query endpoint ships) jq '.endpoints'
D-1.A06 Cacheable: Cache-Control max-age between 3600 and 86400 (v0.1 §5.1) response headers
D-1.N01 No claimed-but-dead URLs: every non-null URL in endpoints answers non-404 curl each
D-1.N02 No conformance_level above what the suite can verify (an L0 claim without a query endpoint is an over-claim — declare L0-restricted-domain or lower) review

D-2 — A2A agent card (GET /a2a/.well-known/agent.json)

# Assertion How
D-2.A01 HTTP 200, valid JSON, name present jq '.name'
D-2.A02 extensions.webgcp block present (the profile carrier) jq '.extensions.webgcp'
D-2.A03 extensions.webgcp.descriptor points at the D-1 surface (/.well-known/webgcp) jq '.extensions.webgcp.descriptor'

D-3 — Cross-surface agreement

# Assertion How
D-3.A01 Descriptor endpoints.agent_card and the agent card's extensions.webgcp.descriptor reference each other (the mirror closes) compare D-1/D-2
D-3.A02 Capability lists agree: descriptor supported_features ⊆ agent-card declared capabilities (or vice versa). Tie-break: the descriptor wins — it is the surface v0.1 §5.1 MUSTs, and the agent-card block is MAY at every level (corrected 2026-07-28; the prior "canonical carrier wins" left this undefined) compare

D-4 — DNS (restricted-domain: explicitly N/A, not silently skipped)

# Assertion How
D-4.A01 If the host is DNS-controllable: _webgcp.<host> SVCB answers (then run full L0-003 via runner.py). If restricted-domain (*.run.app, *.web.app): the descriptor's restricted_domain_reason says so — that is the pass DoH: https://cloudflare-dns.com/dns-query?name=_webgcp.<host>&type=64 (Cloudflare primary; Google DoH unreliable for SVCB per the 2026-05-16 observation)

Worked run — a restricted-domain implementer (local)

A real pass, recorded 2026-05-21, with the implementer's name generalized. After the two-line application wiring described in the implementer's onboarding guide:

uv run uvicorn main:app --reload --port 8000

curl -s http://localhost:8000/.well-known/webgcp | jq '.spec_version, .profile_of, .conformance_level, .endpoints.query'
# → "0.2-profile" / "webmcp" / "L0-restricted-domain" / null     (D-1.A02/A04/A05 pass)

curl -s http://localhost:8000/a2a/.well-known/agent.json | jq '.name, .extensions.webgcp.descriptor'
# → "example-agent" / "/.well-known/webgcp"                      (D-2.A01/A03 pass)

Note the direction in the second call: the agent card's extensions.webgcp.descriptor points at /.well-known/webgcp. The descriptor is the carrier; the card is the breadcrumb.

D-4 = N/A-by-declaration (a managed serverless host with no controllable DNS zone; restricted_domain_reason present).

The gap to full L0 (named)

Full L0 needs /webgcp/v0/query + a server descriptor advertising manifests_available and bundle_contracts_available so runner.py can exercise WGCP-L0-001/002. For a restricted-domain implementer this is a named ask in its own request-for-development doc (the implementing team owns it). Until it ships, this checklist is the interop gate of record for the discovery subset.

Cross-references

the published conformance suite at /conformance/v0.1/runner.py (the full suite) · the published v0.1 spec §5.1 + §5.1.1 · the second-implementer's onboarding README (the P1 surfaces + P2–P4 roadmap) · the DNSAID-composition deferral rationale (why webgcp._agents.<host> NXDOMAIN is conformant today).