-
Notifications
You must be signed in to change notification settings - Fork 22
Open
Labels
Description
Summary
Implement the sync_accounts AdCP v3 tool that allows buyer agents to provision and manage advertiser accounts. This is the write counterpart to list_accounts (#1011).
Depends on: #1012 (Agent/Account data model refactor)
AdCP version: v3.0.0-beta.3 (adcp library 3.3.0)
What sync_accounts Does
Buyer agents call sync_accounts to onboard advertiser accounts. The sales agent provisions them, maps to the ad server, and returns status.
Request
class SyncAccountsRequest(AdCPBaseModel):
accounts: list[Account] # Accounts to provision
delete_missing: bool = False # Deactivate accounts not in list
dry_run: bool = False # Preview without applying
push_notification_config: PushNotificationConfig | None # Async webhooksEach account has:
house: Brand house domain (e.g., "unilever.com")brand_id: Brand within house portfolio (e.g., "dove")operator: Agency/operator domain (e.g., "groupm.com")billing: Who gets invoiced - "brand", "operator", or "agent"
Response
Returns each account with:
account_id: Seller-assigned IDaction: "created" | "updated" | "unchanged" | "failed"status: "active" | "pending_approval" | "payment_required" | "suspended" | "closed"credit_limit: Amount and currencypayment_terms: e.g., "net_30", "prepay"setup: If further setup needed (URL + message)
Implementation Plan
1. Add _impl() function
# src/core/tools/accounts.py (new file)
from adcp.types import SyncAccountsRequest, SyncAccountsResponse
def _sync_accounts_impl(request: SyncAccountsRequest, context) -> SyncAccountsResponse:
# 1. Validate agent has permission to manage accounts
# 2. For each account in request:
# a. Check if account exists (by house + brand_id)
# b. Create or update Account record
# c. Map to ad server advertiser via adapter
# d. Set initial status
# 3. Handle delete_missing (deactivate unlisted)
# 4. Handle dry_run (validate without persisting)
# 5. Return response with per-account status2. Add MCP tool wrapper
# src/core/main.py
@mcp.tool()
def sync_accounts(...) -> SyncAccountsResponse:
return _sync_accounts_impl(...)3. Add A2A raw function
# src/core/tools/accounts.py
def sync_accounts_raw(...) -> SyncAccountsResponse:
return _sync_accounts_impl(...)4. Adapter interface
# src/adapters/base.py
def sync_advertiser(self, account: Account) -> AdvertiserMapping:
"""Create/update advertiser in ad server."""GAM adapter: Use AdvertiserService to create/update Companies.
Key Design Decisions
- Account matching: Match by
house+brand_idcombo (unique per tenant) - Approval flow: May need workflow step for
pending_approvalstatus - Credit limits: Store locally; ad server may have its own limits
- delete_missing: Only affects accounts previously synced by this agent
Files to Modify
| File | Change |
|---|---|
src/core/tools/accounts.py |
New file: _sync_accounts_impl() + raw function |
src/core/main.py |
MCP tool wrapper |
src/core/tools/__init__.py |
Export raw function |
src/adapters/base.py |
sync_advertiser() interface |
src/adapters/gam/adapter.py |
GAM advertiser creation |
src/adapters/mock/adapter.py |
Mock implementation |
tests/unit/test_sync_accounts.py |
Unit tests |
Testing
uv run pytest tests/unit/test_sync_accounts.py -v
uv run pytest tests/unit/test_adcp_contract.py -vTest cases:
- Create new account → status "active", action "created"
- Update existing account → action "updated"
- dry_run=True → no database changes
- delete_missing=True → deactivates unlisted accounts
- Account creation fails → action "failed" with errors
- Agent permission validation
Reactions are currently unavailable