Skip to content

Commit d5ca420

Browse files
committed
feat(decisioning): surface advertise_all=False on create_adcp_server_from_platform
Adopters using the standalone build-the-handler path saw ~40+ entries in handler.advertised_tools after create_adcp_server_from_platform() because the ClassVar holds the full union across all PlatformHandler shims. This adds advertise_all=False (matching serve()'s default) and filters the instance-level advertised_tools to the platform's claimed specialisms, consistent with the existing advertised_tools_for_instance() hook already used by get_tools_for_handler. Existing serve() callers get the same filtering applied to handler.advertised_tools for consistency. https://claude.ai/code/session_01VYgrLhaR7HWfBf3VxbMn5K
1 parent 863bb93 commit d5ca420

2 files changed

Lines changed: 73 additions & 0 deletions

File tree

src/adcp/decisioning/serve.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def create_adcp_server_from_platform(
8787
buyer_agent_registry: BuyerAgentRegistry | None = None,
8888
config_store: ProductConfigStore | None = None,
8989
property_list_fetcher: PropertyListFetcher | None = None,
90+
advertise_all: bool = False,
9091
) -> tuple[PlatformHandler, ThreadPoolExecutor, TaskRegistry]:
9192
"""Build the :class:`PlatformHandler` + supporting wiring from a
9293
:class:`DecisioningPlatform`.
@@ -176,6 +177,14 @@ def create_adcp_server_from_platform(
176177
(avoid duplicate delivery; idempotency-key dedup at the
177178
receiver would handle it but explicit suppression matches the
178179
v5 manual-emit posture for adopters mid-migration).
180+
:param advertise_all: When ``False`` (default), ``handler.advertised_tools``
181+
is filtered to the tools covered by the platform's claimed
182+
specialisms — matching :func:`serve`'s default. When ``True``,
183+
the full spec surface is retained on ``handler.advertised_tools``
184+
(useful for spec-compliance storyboards). Either way, the
185+
per-instance specialism filter and the override-detection filter
186+
still apply inside :func:`~adcp.server.mcp_tools.get_tools_for_handler`
187+
when the handler is wired into an MCP/A2A server.
179188
180189
To wire a :class:`ProposalManager` (v1 two-platform composition),
181190
pass it on a :class:`PlatformRouter` via
@@ -331,6 +340,20 @@ def create_adcp_server_from_platform(
331340

332341
validate_capabilities_response_shape(handler)
333342

343+
# Filter handler.advertised_tools to the platform's claimed specialisms
344+
# when advertise_all=False (the default). The ClassVar holds the full
345+
# union of all shim-covered tools (~40+); adopters using the standalone
346+
# build-the-handler path saw all of them when accessing
347+
# handler.advertised_tools directly, instead of the per-specialism
348+
# subset that serve() already projects through
349+
# get_tools_for_handler → advertised_tools_for_instance(). Override at
350+
# the instance level so the ClassVar (and the _HANDLER_TOOLS registry
351+
# populated at class-definition time) are unaffected.
352+
if not advertise_all:
353+
per_instance = set(handler.advertised_tools_for_instance())
354+
if per_instance:
355+
handler.advertised_tools = per_instance # type: ignore[misc]
356+
334357
return handler, executor, registry
335358

336359

@@ -428,6 +451,7 @@ def serve(
428451
buyer_agent_registry=buyer_agent_registry,
429452
config_store=config_store,
430453
property_list_fetcher=property_list_fetcher,
454+
advertise_all=advertise_all,
431455
)
432456

433457
# Phase 1 sandbox-authority — wire the comply controller's account

tests/test_decisioning_serve.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,52 @@ def test_serve_does_not_fire_gate_for_platform_without_webhook_eligible_tools()
504504
handler, executor, _ = create_adcp_server_from_platform(platform)
505505
assert handler._webhook_sender is None
506506
executor.shutdown(wait=True)
507+
508+
509+
# ---- Issue #519: advertise_all=False filters handler.advertised_tools ----
510+
511+
512+
def test_create_default_filters_advertised_tools_to_specialism() -> None:
513+
"""handler.advertised_tools with default advertise_all=False returns
514+
only the tools for the platform's claimed specialisms, not the full
515+
~40-tool union across all PlatformHandler shims."""
516+
from adcp.decisioning.handler import _SALES_ADVERTISED_TOOLS
517+
518+
platform = _SalesPlatformWithRequiredMethods()
519+
# Disable auto-emit so the webhook-sender gate doesn't fire — this
520+
# test focuses on advertised_tools filtering, not F12 validation.
521+
handler, executor, _ = create_adcp_server_from_platform(
522+
platform, auto_emit_completion_webhooks=False
523+
)
524+
executor.shutdown(wait=True)
525+
assert handler.advertised_tools == set(_SALES_ADVERTISED_TOOLS)
526+
assert "build_creative" not in handler.advertised_tools
527+
assert "activate_signal" not in handler.advertised_tools
528+
assert "sync_audiences" not in handler.advertised_tools
529+
530+
531+
def test_create_advertise_all_true_retains_full_tool_surface() -> None:
532+
"""advertise_all=True keeps handler.advertised_tools as the full
533+
class-level union — spec-compliance storyboards use this escape hatch."""
534+
from adcp.decisioning.handler import PlatformHandler
535+
536+
platform = _SalesPlatformWithRequiredMethods()
537+
handler, executor, _ = create_adcp_server_from_platform(
538+
platform, advertise_all=True, auto_emit_completion_webhooks=False
539+
)
540+
executor.shutdown(wait=True)
541+
assert handler.advertised_tools == PlatformHandler.advertised_tools
542+
assert "build_creative" in handler.advertised_tools
543+
assert "activate_signal" in handler.advertised_tools
544+
545+
546+
def test_create_bare_platform_no_specialism_leaves_classlevel_intact() -> None:
547+
"""When advertised_tools_for_instance() returns empty (no recognized
548+
specialism), the fallback leaves the ClassVar intact so the handler
549+
is not accidentally muted."""
550+
from adcp.decisioning.handler import PlatformHandler
551+
552+
platform = _BarePlatform()
553+
handler, executor, _ = create_adcp_server_from_platform(platform)
554+
executor.shutdown(wait=True)
555+
assert handler.advertised_tools == PlatformHandler.advertised_tools

0 commit comments

Comments
 (0)