Skip to content

Commit dd8a220

Browse files
bokelleyclaude
andcommitted
fix(server): correct UserWarning stacklevel on A2A transport path (#318)
The A2A path calls _log_advertised_tools directly from create_a2a_server (no _register_handler_tools frame), so the unregistered-subclass UserWarning attributed to a frame deeper than the operator's call site. Thread a transport-aware stacklevel: 4 for MCP, 3 for A2A. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 595bedf commit dd8a220

1 file changed

Lines changed: 15 additions & 8 deletions

File tree

src/adcp/server/serve.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,14 @@ class introduces a new specialism (a custom subclass that's not in
232232
if unadvertised and not advertise_all:
233233
logger.debug("%s server unadvertised tools: %s", transport, ", ".join(unadvertised))
234234

235-
_warn_if_unregistered_subclass(handler, advertise_all=advertise_all)
235+
# Stacklevel walks: warnings.warn → _warn_if_unregistered_subclass →
236+
# _log_advertised_tools → operator's call site. The MCP path adds one
237+
# extra frame (_register_handler_tools); A2A calls _log_advertised_tools
238+
# directly from create_a2a_server.
239+
caller_stacklevel = 4 if transport == "mcp" else 3
240+
_warn_if_unregistered_subclass(
241+
handler, advertise_all=advertise_all, stacklevel=caller_stacklevel
242+
)
236243

237244

238245
#: Bases whose tool set is broad-by-design — when an adopter subclass
@@ -246,7 +253,9 @@ class introduces a new specialism (a custom subclass that's not in
246253
_BROAD_SURFACE_BASES: frozenset[str] = frozenset({"ADCPHandler"})
247254

248255

249-
def _warn_if_unregistered_subclass(handler: ADCPHandler[Any], *, advertise_all: bool) -> None:
256+
def _warn_if_unregistered_subclass(
257+
handler: ADCPHandler[Any], *, advertise_all: bool, stacklevel: int = 4
258+
) -> None:
250259
"""Emit a one-time ``UserWarning`` when a custom handler base bypasses
251260
the tool-discovery registry.
252261
@@ -281,11 +290,9 @@ def _warn_if_unregistered_subclass(handler: ADCPHandler[Any], *, advertise_all:
281290
)
282291
if has_specialized_parent:
283292
return
284-
# stacklevel=4 walks: warnings.warn → _warn_if_unregistered_subclass
285-
# → _log_advertised_tools → _register_handler_tools → operator's
286-
# serve()/create_mcp_server()/create_a2a_server() call site. If any
287-
# of those frames are added or removed, recompute or refactor to
288-
# use stacklevel=2 from a thin wrapper.
293+
# Default stacklevel=4 covers the MCP path (warn → this fn →
294+
# _log_advertised_tools → _register_handler_tools → caller). The A2A
295+
# path lacks _register_handler_tools and passes stacklevel=3.
289296
warnings.warn(
290297
f"Handler class {cls.__name__!r} subclasses ADCPHandler directly "
291298
f"but isn't registered in the framework's tool-discovery "
@@ -303,7 +310,7 @@ def _warn_if_unregistered_subclass(handler: ADCPHandler[Any], *, advertise_all:
303310
f"`uv run python scripts/generate_decisioning_handler.py` "
304311
f"emits the declaration for you.",
305312
UserWarning,
306-
stacklevel=4,
313+
stacklevel=stacklevel,
307314
)
308315

309316

0 commit comments

Comments
 (0)