Skip to content

feat: helper to distinguish schema-invalid adagents.json from "agent not authorized" #707

@bokelley

Description

@bokelley

Problem

get_properties_by_agent(adagents, agent_url) returns [] in two semantically very different cases, and callers can't tell them apart:

  1. Valid file, agent not authorized. The file conforms to the JSON Schema and lists other agents, just not this one (or restricts this agent to properties it isn't matched to).
  2. Schema-invalid file. Entries are missing authorization_type plus a required selector. Per the published schema (https://adcontextprotocol.org/schemas/v1/adagents.json), every entry in authorized_agents.items must satisfy one of:
    oneOf:
      - { required: [url, authorized_for, authorization_type, property_ids] }
      - { required: [url, authorized_for, authorization_type, property_tags] }
      - { required: [url, authorized_for, authorization_type, properties] }
      - { required: [url, authorized_for, authorization_type, publisher_properties] }
      - { required: [url, authorized_for, authorization_type, signal_ids] }
      - { required: [url, authorized_for, authorization_type, signal_tags] }
    Bare entries like {url, authorized_for} match no variant. The SDK currently treats them as "authorizes nothing" — correct by the spec, but the caller has no way to know whether the file is structurally broken or just doesn't list us yet.

Real-world impact

Multiple publishers in production are shipping bare-entry adagents.json. Example (wonderstruck.org):

{
  "$schema": "https://adcontextprotocol.org/schemas/v1/adagents.json",
  "authorized_agents": [
    {"url": "https://wonderstruck.sales-agent.scope3.com", "authorized_for": "Authorized for display banners"},
    {"url": "https://interchange.io", "authorized_for": "Authorized for display banners"}
  ],
  "properties": [
    {"property_id": "main_site", "property_type": "website", "name": "Main site",
     "identifiers": [{"type": "domain", "value": "wonderstruck.org"}], "tags": ["sites"]}
  ]
}

get_properties_by_agent(data, "https://wonderstruck.sales-agent.scope3.com") returns []. Our publisher-partners UI shows "Pending 0/0," which reads as "publisher hasn't authorized us yet" — but actually the file is invalid. We end up chasing publishers without knowing what to tell them. The online validator at agenticadvertising.org reports these files as valid: true (filed separately against adcontextprotocol/adcp), reinforcing the confusion.

Proposed API

A sibling validator that introspects without changing existing behavior:

from adcp.adagents import validate_adagents

result = validate_adagents(adagents_data)
# AdagentsValidationResult(
#     schema_valid: bool,
#     errors: list[AdagentsEntryError],          # per-entry problems
#     authorized_agents_count: int,
#     properties_count: int,
# )

AdagentsEntryError would carry:

  • entry index and url
  • error kind: missing_authorization_type, missing_selector_for_type, unknown_authorization_type, selector_present_for_wrong_type, etc.
  • human-readable message

Then callers can do:

result = validate_adagents(data)
if not result.schema_valid:
    show_publisher_error(result.errors)
elif not get_properties_by_agent(data, agent_url):
    show_publisher_error("File is valid but doesn't list your agent")
else:
    show_authorized(...)

Alternative considered

Adding a second return value or raising on get_properties_by_agent — both break the API. Sibling validator avoids that.

Cross-SDK note

The TS SDK (adcp-client) doesn't implement per-agent resolution at all today (filed separately). Once it does, the same validator helper should land there for consistency.

Willing to PR

Happy to put up a PR if the API shape is agreed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions