Repository navigation
10.0.0b1: an SDK seller's default tools/list exceeds the SDK client's 100,000-member bound, so call_tool fails #1692
Description
On 10.0.0b1, a plain sales seller built with create_mcp_server and the default schema_mode="compact" publishes a tools/list larger than the SDK client's own default JSON bound (MAX_JSON_MEMBERS = 100_000, adcp/_json_bounds.py). The MCP client calls list_tools before call_tool, so every tool call from an SDK 10 client to that seller fails with ADCPResponseTooComplexError, even though the payload (about 2.5 MB) is under the 4 MiB byte budget.
The bound is unchanged from 10.0.0b0, but the listing grew: our seller's MCP client test (ADCPClient calling get_products) passed on 10.0.0b0 and fails on 10.0.0b1.
Repro
import asyncio
import json
from typing import Any
from adcp.server import ADCPHandler, create_mcp_server
def members(value: Any) -> int:
if isinstance(value, dict):
return len(value) + sum(members(v) for v in value.values())
if isinstance(value, list):
return len(value) + sum(members(v) for v in value)
return 0
class Seller(ADCPHandler[Any]):
_adcp_version = "3.2"
async def get_products(self, params, context=None): ...
async def create_media_buy(self, params, context=None): ...
async def update_media_buy(self, params, context=None): ...
async def get_media_buys(self, params, context=None): ...
async def get_media_buy_delivery(self, params, context=None): ...
async def sync_creatives(self, params, context=None): ...
async def list_creatives(self, params, context=None): ...
async def provide_performance_feedback(self, params, context=None): ...
async def list_creative_formats(self, params, context=None): ...
async def list_accounts(self, params, context=None): ...
async def sync_accounts(self, params, context=None): ...
async def main() -> None:
mcp = create_mcp_server(Seller(), supported_versions=["3.1", "3.2"])
tools = [t.model_dump(mode="json", by_alias=True, exclude_none=True) for t in await mcp.list_tools()]
listing = {"jsonrpc": "2.0", "id": 1, "result": {"tools": tools}}
print(f"{len(tools)} tools, {len(json.dumps(listing))} bytes, {members(listing)} JSON members")
asyncio.run(main())Output on 10.0.0b1: 11 tools, 2519482 bytes, 106250 JSON members. Without supported_versions the same handler lists about 24,600 members.
A real seller (the Scope3 embedded sales agent: 12 tools on the decisioning serve() path, supported_versions 3.1/3.2) lists 101,604 members, 73,697 of them in outputSchemas (get_products 14,865, get_media_buys 12,731, create_media_buy 12,075). Calling it with ADCPClient:
mcp/client/session.py in call_tool -> await self.list_tools()
adcp._response_limit.ADCPResponseTooComplexError: Agent response exceeds the bound of 100000 JSON object members and array elements
Expected
A seller using the SDK's defaults can be called by a client using the SDK's defaults. Some options:
- keep the compact
outputSchemas (or the per-version alternates) small enough that a full sales seller fits; - exempt or separately bound
tools/list, which comes from the server's own schemas; - have
serve()refuse or warn when its listing exceeds the client bound, the way it already warns at half the byte budget.
Activity
fix(server): compact a pinned seller's tools/list and size the client…
fix(server): compact a pinned seller's tools/list and size the client…