The Prebid Sales Agent exposes identical functionality through two protocols: MCP (Model Context Protocol) and A2A (Agent-to-Agent). Both call the same underlying implementation functions and share authentication infrastructure.
| Aspect | MCP | A2A |
|---|---|---|
| Protocol | HTTP + tool-call model | JSON-RPC 2.0 |
| Library | fastmcp (EnhancedMCPServer) |
a2a-sdk (official SDK) |
| Internal Port | 8080 | 8091 |
| Route | /mcp/ |
/a2a |
| Auth Header | x-adcp-auth |
x-adcp-auth |
| Discovery | tools/list method |
/.well-known/agent.json |
| Response Format | Pydantic models (JSON) | A2A Message/Task types |
| Streaming | StreamableHttpTransport (SSE) | A2A spec streaming |
| Best For | Direct AI assistant integration | Multi-agent workflows |
The MCP server is the primary interface for AI assistants. It uses the fastmcp library and exposes tools through an EnhancedMCPServer instance with StreamableHttpTransport for server-sent events.
/mcp/ (behind nginx proxy on port 8000)StreamableHttpTransport (SSE-capable)Tools are registered with the @mcp.tool() decorator. Each tool function receives a ToolContext through FastAPI-style dependency injection (Depends(get_tool_context)), which provides access to the tenant, principal, database session, adapter instance, and server configuration.
@mcp.tool()
async def get_products(
brief: str,
brand_manifest: BrandManifest | None = None,
ctx: ToolContext = Depends(get_tool_context),
) -> GetProductsResponse:
return await core_get_products_tool(brief, brand_manifest, ctx)
x-adcp-auth — Access token for authentication (required for most tools, optional for discovery)x-context-id — Optional request tracking identifier for correlating related operationsThe A2A server handles multi-agent workflows using the official a2a-sdk. It communicates via JSON-RPC 2.0 over Starlette and supports the full A2A task lifecycle.
/a2a (behind nginx proxy on port 8000)A2A clients discover the agent by fetching /.well-known/agent.json, which returns an agent card describing the agent’s capabilities, supported tasks, and endpoint URL.
A2A operations follow a status progression:
submitted — Task received and queuedworking — Task is being processedcompleted — Task finished successfully (result in response)failed — Task encountered an errorinput-required — Task needs additional information from the callerBoth protocols call the same core implementation functions. For example, create_media_buy in MCP and the equivalent A2A task both invoke core_create_media_buy_tool(). This guarantees identical behavior regardless of which protocol a client uses.
Other shared core functions include core_get_products_tool(), core_get_adcp_capabilities_tool(), core_sync_creatives_tool(), and core_get_media_buy_delivery_tool().
The shared core layer means that any bug fix or feature added to a tool is immediately available through both MCP and A2A without duplicate implementation work.
Both protocols use the same authentication mechanism:
x-adcp-auth header.ToolContext is populated with the authenticated state and passed to the core function.Token scopes determine which tools the principal can access. Discovery tools (get_adcp_capabilities, get_products, list_creative_formats) work without authentication but return richer data when a token is provided.
uvx adcp CLI/.well-known/agent.json is part of the integration patternUse the uvx adcp CLI to interact with the MCP server directly:
# List available tools
uvx adcp http://localhost:8000/mcp/ --auth test-token list_tools
# Discover capabilities
uvx adcp http://localhost:8000/mcp/ --auth test-token get_adcp_capabilities
# Search products
uvx adcp http://localhost:8000/mcp/ --auth test-token get_products '{"brief": "video ads"}'
Use curl to send JSON-RPC 2.0 requests to the A2A endpoint:
# Send a task
curl -X POST http://localhost:8000/a2a \
-H "Content-Type: application/json" \
-H "x-adcp-auth: test-token" \
-d '{
"jsonrpc": "2.0",
"method": "tasks/send",
"id": "1",
"params": {
"message": {
"role": "user",
"parts": [{"text": "Find video advertising products"}]
}
}
}'
# Fetch agent card
curl http://localhost:8000/.well-known/agent.json
Both protocols surface the same error codes (unauthorized, not_found, validation_error, etc.), but the transport format differs:
| Aspect | MCP | A2A |
|---|---|---|
| Error envelope | Tool result with is_error: true |
JSON-RPC error object or failed task |
| Error fields | error_code + message in response body |
code + message in JSON-RPC error, or task status failed |
| HTTP status | Always 200 (errors in body) | Always 200 (errors in JSON-RPC response) |
| Partial results | Not supported | Possible via input-required status |