Symptom
fetch_agent_authorizations_from_directory (adcp/adagents.py) sends pagination cursors via ?since=<cursor>. The spec at docs/aao/directory-api.mdx defines two distinct query parameters:
| param |
type |
purpose |
since |
ISO 8601 |
Return only publishers whose last_verified_at ≥ since. Incremental sync. |
cursor |
opaque string |
Pagination cursor returned by a prior response. |
The SDK's docstring conflates them:
since: Optional opaque cursor or RFC 3339 timestamp from a prior
``directory_indexed_at`` — passed through as ``?since=...``
Production server enforces the spec — ?since=<cursor> returns 400:
$ curl -s -o /dev/null -w "%{http_code}\n" \
"https://agenticadvertising.org/api/v1/agents/https%3A%2F%2Finterchange.io/publishers?since=YWxsbnVyc2VzLmNvbQ"
400
$ curl -s -o /dev/null -w "%{http_code}\n" \
"https://agenticadvertising.org/api/v1/agents/https%3A%2F%2Finterchange.io/publishers?cursor=YWxsbnVyc2VzLmNvbQ"
200
Impact
Pagination is broken end-to-end for the Python SDK. Caught against Raptive's ~6,800-publisher set:
result = await fetch_agent_authorizations_from_directory(
"https://interchange.io",
directory_url="https://agenticadvertising.org/api",
since=cursor_from_previous_page,
)
# AdagentsValidationError: Agent-publishers directory returned HTTP 400
First page works (no cursor passed). Second page onward 400s. So today the SDK can fetch the first 200 publishers of a 6,800-publisher network and then fails.
Reproduction
import asyncio, urllib.parse, httpx
async def main():
base = "https://agenticadvertising.org/api/v1/agents"
agent = urllib.parse.quote("https://interchange.io", safe="")
url = f"{base}/{agent}/publishers"
async with httpx.AsyncClient(timeout=30) as c:
page1 = (await c.get(url)).json()
cursor = page1["next_cursor"]
# SDK behavior — fails
r = await c.get(url, params={"since": cursor})
print(f" since=cursor → HTTP {r.status_code}") # 400
# Spec behavior — works
r = await c.get(url, params={"cursor": cursor})
print(f" cursor=cursor → HTTP {r.status_code}") # 200
asyncio.run(main())
Fix
Add a distinct cursor: str | None = None keyword to fetch_agent_authorizations_from_directory, separate from since:
async def fetch_agent_authorizations_from_directory(
agent_url: str,
*,
directory_url: str,
since: str | None = None, # ISO 8601 timestamp — incremental sync
cursor: str | None = None, # opaque pagination cursor
include: list[str] | None = None,
timeout: float = 10.0,
client: httpx.AsyncClient | None = None,
) -> AgentAuthorizationsDirectoryResult:
...
Send since and cursor as separate query params. Update docstring to stop conflating them. Treat as a v5.7.0 follow-on bugfix.
Companion server bug
The same investigation found that production mounts the endpoint at /api/v1/... instead of the spec's /v1/.... Filed at adcontextprotocol/adcp#4924. Independent — fixing either does not fix the other.
References
Symptom
fetch_agent_authorizations_from_directory(adcp/adagents.py) sends pagination cursors via?since=<cursor>. The spec atdocs/aao/directory-api.mdxdefines two distinct query parameters:sincelast_verified_at≥since. Incremental sync.cursorThe SDK's docstring conflates them:
Production server enforces the spec —
?since=<cursor>returns 400:Impact
Pagination is broken end-to-end for the Python SDK. Caught against Raptive's ~6,800-publisher set:
First page works (no cursor passed). Second page onward 400s. So today the SDK can fetch the first 200 publishers of a 6,800-publisher network and then fails.
Reproduction
Fix
Add a distinct
cursor: str | None = Nonekeyword tofetch_agent_authorizations_from_directory, separate fromsince:Send
sinceandcursoras separate query params. Update docstring to stop conflating them. Treat as a v5.7.0 follow-on bugfix.Companion server bug
The same investigation found that production mounts the endpoint at
/api/v1/...instead of the spec's/v1/.... Filed at adcontextprotocol/adcp#4924. Independent — fixing either does not fix the other.References
docs/aao/directory-api.mdxlines 29-30, 124, 153, 194, 207-209adcp/adagents.py:2094