Skip to content

fix(reporting): make the managed seller pass the SDK's own capability validation - #1779

Merged
bokelley merged 2 commits into
nextfrom
claude/issue-1695-managed-capabilities
Nov 1, 2026
Merged

bokelley merged 2 commits into
nextfrom
claude/issue-1695-managed-capabilities

Conversation

@bokelley

@bokelley bokelley commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Fixes #1771, found by the cross-SDK interop matrix (adcontextprotocol/adcp#8679). This is an early, standalone PR into next, because any seller built from the guide hits it.

Problem

A seller on the managed path (ReliableReportingService(subscriptions=SubscriptionOptions(...))) advertised a get_adcp_capabilities that the Python SDK's own client rejects against the 3.3.0-beta.3 schemas. Serving it over MCP also needed wiring the guide never mentions.

Fix

  • adcp.principal.
    • optimistic_concurrency: true is now always advertised; the principal runtime enforces expected_configuration_version.
    • reporting_destinations is listed only when the seller's block carries the facts the schema then requires: max_reporting_destinations, reporting_destination_offerings and suspension_interval_seconds. This matches TS, which adds the section only with prepareReportingDestination.
  • adcp.supported_versions is advertised on the managed path when the seller doesn't state it (the SDK's served releases, without RCs). Without it, a buyer negotiates down from major_versions and gets "get_principal is unavailable in AdCP 3.1".
  • Plain MCP callers. On the managed path, a reporting task with an authenticated caller but no resolved account (a plain ToolContext from serve()) reads the account the request names.
    • This replaces the "Reporting service configuration failed" error.
    • The subscription grant check still refuses an account the caller holds no grant on, and ledger reads stay scoped to the caller's own consumer_id.
    • Outside the managed path, caller resolution is unchanged.
  • Seller guide. A new Capabilities and serving subsection lists what the host still supplies:
    • adcp.idempotency, a fact only the seller knows;
    • the destination facts, to accept reporting_destinations;
    • context_factory=auth_context_factory.

Tests (the #1771 repros)

  • test_issue_1771_managed_capabilities_pass_the_sdk_response_validation: the managed capabilities, with the seller's idempotency, validate against protocol/get-adcp-capabilities-response.json with zero errors, and include 3.3-beta.3 in supported_versions.
  • test_issue_1771_status_is_served_to_a_plain_mcp_caller: get_reporting_status serves subscription_coverage to a plain ToolContext(caller_identity=...).
  • The managed capability test is updated for the conditional reporting_destinations.

The managed, service and reliable-reporting suites are green. ruff and mypy are clean.

The reference seller in #1776 (PR 6) carries the same principal fix; this PR lands it on next without waiting for the stack. It also cherry-picks the row-lease test fix from #1767, so the Postgres job is green.

🤖 Generated with Claude Code

bokelley and others added 2 commits November 1, 2026 18:56
The producer row-upload test stamps its worker lease with the settling
harness clock, fixed at 2026-11-01T02:10Z, while the row store checks the
lease against the database's clock_timestamp. Once the wall clock passed that
instant plus the lease, every run failed with STATE_UNAVAILABLE: no live
worker lease covers this row upload, on next and on every pull request. The
test now runs its turn at the wall clock, so the lease lies in the real
future.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit ae15bab)
… validation

adcp-client-python#1771: a managed reporting-subscriptions seller advertised
capabilities the SDK's own client rejected, and needed undocumented wiring
over MCP. The principal block now carries optimistic_concurrency true and
lists reporting_destinations only when the seller supplies the destination
facts the schema requires with it. supported_versions is advertised when
the seller does not state it, so a buyer negotiates the 3.3 line. A
reporting task with an authenticated caller but no resolved account, as a
plain ToolContext over MCP has, reads the account the request names, behind
the subscription grant check. The seller guide lists what the host still
supplies: adcp.idempotency, the destination facts, and the context factory.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean, well-scoped fix for #1771.

Checked: the security-sensitive _managed_caller fallback reads account_id from the request body but is properly guarded — consumer_id comes from the authenticated context, the grant check runs, and ledger reads stay consumer_id-scoped, so multi-tenant isolation holds; unauthenticated callers re-raise fail-closed. advertise_principal's conditional reporting_destinations and the supported_versions injection are correct and tested against the response schema. No public-API/semver, generated-code, forward-compat, or import-layering surface touched. No credentials routed through ctx_metadata. CI gates remain armed.

No blocking findings. No high-risk paths, no gated paths, no author team gate. Falls through to row 9.

@bokelley
bokelley merged commit 922fb87 into next Nov 1, 2026
41 checks passed
@bokelley
bokelley deleted the claude/issue-1695-managed-capabilities branch November 1, 2026 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant