Problem
In-process test harnesses (httpx.ASGITransport, Starlette TestClient) need the unified MCP+A2A+admin ASGI app without binding a uvicorn socket. serve() binds a socket as part of its contract; it doesn't return the app handle.
We currently import private symbols to assemble the app ourselves:
from adcp.decisioning.serve import create_adcp_server_from_platform
from adcp.server.serve import _apply_asgi_middleware, _build_mcp_and_a2a_app
handler, _executor, _registry = create_adcp_server_from_platform(router, ...)
app = _build_mcp_and_a2a_app(handler, **build_kwargs)
return _apply_asgi_middleware(app, asgi_middleware)
This is fragile: any rename / signature change to _build_mcp_and_a2a_app or _apply_asgi_middleware (both leading-underscore private) silently breaks every test that drives the app via httpx.ASGITransport.
Proposed SDK shape
from adcp.testing import build_asgi_app
app = build_asgi_app(
router,
name=\"my-test-server\",
auth=BearerTokenAuth(validate_token=...),
asgi_middleware=[(MyMiddleware, {})],
context_factory=auth_context_factory,
enable_dns_rebinding_protection=False,
streaming_responses=False,
)
# app is an ASGI handler ready for httpx.ASGITransport(app=app)
Same kwargs serve() accepts (auth, middleware, context_factory, etc.) minus the uvicorn-binding ones (port, host, debug endpoints). Returns the unified app handle.
Why this matters for adopters beyond us
- Any adopter writing high-fidelity transport tests (assert exact wire shapes, headers, error envelopes) needs this — mocking the dispatcher doesn't catch transport-layer bugs.
- The closed #549 / PR #554 shipped
build_test_client(platform) for HTTP-level testing. This is the same intent at a lower layer: hand back the ASGI app, let me drive it however I want (httpx, Starlette TestClient, hypercorn for load testing, etc.).
Files
Problem
In-process test harnesses (httpx.ASGITransport, Starlette TestClient) need the unified MCP+A2A+admin ASGI app without binding a uvicorn socket.
serve()binds a socket as part of its contract; it doesn't return the app handle.We currently import private symbols to assemble the app ourselves:
This is fragile: any rename / signature change to
_build_mcp_and_a2a_appor_apply_asgi_middleware(both leading-underscore private) silently breaks every test that drives the app viahttpx.ASGITransport.Proposed SDK shape
Same kwargs
serve()accepts (auth, middleware, context_factory, etc.) minus the uvicorn-binding ones (port, host, debug endpoints). Returns the unified app handle.Why this matters for adopters beyond us
build_test_client(platform)for HTTP-level testing. This is the same intent at a lower layer: hand back the ASGI app, let me drive it however I want (httpx, Starlette TestClient, hypercorn for load testing, etc.).Files
build_app).