Skip to content

decisioning: sync_accounts/list_accounts ship as _not_supported stubs (no wire dispatch) #1631

@bokelley

Description

@bokelley

Problem

PlatformHandler in adcp 4.5.0 ships sync_accounts and list_accounts as _not_supported stubs:

async def sync_accounts(
    self, params: SyncAccountsRequest | dict[str, Any], context: TContext | None = None
) -> Any:
    """Sync accounts. Override this to provide functionality."""
    return self._not_supported("sync_accounts")

Compared to e.g. provide_performance_feedback in the same file:

async def provide_performance_feedback(
    self, params, context=None,
) -> ProvidePerformanceFeedbackResponse:
    tool_ctx = context or ToolContext()
    account = await self._resolve_account(None, tool_ctx)
    ctx = self._build_ctx(tool_ctx, account)
    return cast(..., await _invoke_platform_method(
        self._platform, "provide_performance_feedback", params, ctx,
        executor=self._executor, registry=self._registry,
    ))

The framework declares the spec request/response models (adcp.server.mcp_tools.ADCP_TOOL_DEFINITIONS includes sync_accounts + list_accounts), but never wires _invoke_platform_method for them. Result: an adopter that implements AccountStoreUpsert / AccountStoreList on their platform's accounts store is invisible on the wire — every call returns OPERATION_NOT_SUPPORTED.

Also affected: sync_governance (same _not_supported stub).

Repro

Implement AccountStoreUpsert.upsert on a platform's account store, run tools/list, observe sync_accounts is missing. Try to call it directly, get back the not-supported envelope.

Expected

PlatformHandler.sync_accounts should call self._platform.accounts.upsert(params, ctx) (forwarding to the AccountStore Protocol surface), and PlatformHandler.list_accounts should call self._platform.accounts.list(params, ctx). Same pattern as provide_performance_feedback, just routing through the account store instead of the platform method (because LazyPlatformRouter._ACCOUNT_STORE_METHODS correctly excludes account ops from per-tenant delegation).

get_advertised_tools should also include them — currently _SALES_ADVERTISED_TOOLS doesn't have either, so even with register_handler_tools they get stripped by the per-instance specialism filter (advertised_tools_for_instance).

Workaround

I've shipped a runtime polyfill in salesagent that:

  1. Rebinds PlatformHandler.sync_accounts / list_accounts to call through to self._platform.accounts.upsert / .list (with _resolve_account(None, ctx) for auth-only resolution).
  2. Mutates _HANDLER_TOOLS["PlatformHandler"] to add both tools.
  3. Mutates every sales-* entry of SPECIALISM_TO_ADVERTISED_TOOLS to include both tools, so the per-instance filter doesn't strip them back out.

Code: https://github.com/bokelley/salesagent/blob/main/core/platforms/account_polyfill.py — happy to upstream as a PR if direction is agreed.

Why it matters

Sellers MUST expose either list_accounts or sync_accounts per the spec. Adopters implementing the protocol correctly find their compliance score blocked by what looks like a one-line framework oversight — _not_supported instead of _invoke_platform_method. Storyboard validators silently skip those scenarios under missing_tool, masking the gap (see #1623, #1624 for the related skip-vs-fail framing).

Tracked downstream: bokelley/salesagent commit 4e62024c.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions