adcp 2.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. adcp/__init__.py +364 -0
  2. adcp/__main__.py +440 -0
  3. adcp/adagents.py +642 -0
  4. adcp/client.py +1057 -0
  5. adcp/config.py +82 -0
  6. adcp/exceptions.py +185 -0
  7. adcp/protocols/__init__.py +9 -0
  8. adcp/protocols/a2a.py +484 -0
  9. adcp/protocols/base.py +190 -0
  10. adcp/protocols/mcp.py +440 -0
  11. adcp/py.typed +0 -0
  12. adcp/simple.py +451 -0
  13. adcp/testing/__init__.py +53 -0
  14. adcp/testing/test_helpers.py +311 -0
  15. adcp/types/__init__.py +561 -0
  16. adcp/types/_generated.py +237 -0
  17. adcp/types/aliases.py +748 -0
  18. adcp/types/base.py +26 -0
  19. adcp/types/core.py +174 -0
  20. adcp/types/generated_poc/__init__.py +3 -0
  21. adcp/types/generated_poc/adagents.py +411 -0
  22. adcp/types/generated_poc/core/__init__.py +3 -0
  23. adcp/types/generated_poc/core/activation_key.py +30 -0
  24. adcp/types/generated_poc/core/assets/__init__.py +3 -0
  25. adcp/types/generated_poc/core/assets/audio_asset.py +26 -0
  26. adcp/types/generated_poc/core/assets/css_asset.py +20 -0
  27. adcp/types/generated_poc/core/assets/daast_asset.py +61 -0
  28. adcp/types/generated_poc/core/assets/html_asset.py +18 -0
  29. adcp/types/generated_poc/core/assets/image_asset.py +19 -0
  30. adcp/types/generated_poc/core/assets/javascript_asset.py +23 -0
  31. adcp/types/generated_poc/core/assets/text_asset.py +20 -0
  32. adcp/types/generated_poc/core/assets/url_asset.py +28 -0
  33. adcp/types/generated_poc/core/assets/vast_asset.py +63 -0
  34. adcp/types/generated_poc/core/assets/video_asset.py +24 -0
  35. adcp/types/generated_poc/core/assets/webhook_asset.py +53 -0
  36. adcp/types/generated_poc/core/brand_manifest.py +201 -0
  37. adcp/types/generated_poc/core/context.py +15 -0
  38. adcp/types/generated_poc/core/creative_asset.py +102 -0
  39. adcp/types/generated_poc/core/creative_assignment.py +27 -0
  40. adcp/types/generated_poc/core/creative_filters.py +86 -0
  41. adcp/types/generated_poc/core/creative_manifest.py +68 -0
  42. adcp/types/generated_poc/core/creative_policy.py +28 -0
  43. adcp/types/generated_poc/core/delivery_metrics.py +111 -0
  44. adcp/types/generated_poc/core/deployment.py +78 -0
  45. adcp/types/generated_poc/core/destination.py +43 -0
  46. adcp/types/generated_poc/core/dimensions.py +18 -0
  47. adcp/types/generated_poc/core/error.py +29 -0
  48. adcp/types/generated_poc/core/ext.py +15 -0
  49. adcp/types/generated_poc/core/format.py +260 -0
  50. adcp/types/generated_poc/core/format_id.py +50 -0
  51. adcp/types/generated_poc/core/frequency_cap.py +19 -0
  52. adcp/types/generated_poc/core/measurement.py +40 -0
  53. adcp/types/generated_poc/core/media_buy.py +40 -0
  54. adcp/types/generated_poc/core/package.py +68 -0
  55. adcp/types/generated_poc/core/performance_feedback.py +78 -0
  56. adcp/types/generated_poc/core/placement.py +37 -0
  57. adcp/types/generated_poc/core/product.py +164 -0
  58. adcp/types/generated_poc/core/product_filters.py +97 -0
  59. adcp/types/generated_poc/core/promoted_offerings.py +102 -0
  60. adcp/types/generated_poc/core/promoted_products.py +38 -0
  61. adcp/types/generated_poc/core/property.py +64 -0
  62. adcp/types/generated_poc/core/property_id.py +21 -0
  63. adcp/types/generated_poc/core/property_tag.py +21 -0
  64. adcp/types/generated_poc/core/protocol_envelope.py +61 -0
  65. adcp/types/generated_poc/core/publisher_property_selector.py +75 -0
  66. adcp/types/generated_poc/core/push_notification_config.py +51 -0
  67. adcp/types/generated_poc/core/reporting_capabilities.py +51 -0
  68. adcp/types/generated_poc/core/response.py +24 -0
  69. adcp/types/generated_poc/core/signal_filters.py +29 -0
  70. adcp/types/generated_poc/core/sub_asset.py +55 -0
  71. adcp/types/generated_poc/core/targeting.py +53 -0
  72. adcp/types/generated_poc/core/webhook_payload.py +96 -0
  73. adcp/types/generated_poc/creative/__init__.py +3 -0
  74. adcp/types/generated_poc/creative/list_creative_formats_request.py +88 -0
  75. adcp/types/generated_poc/creative/list_creative_formats_response.py +55 -0
  76. adcp/types/generated_poc/creative/preview_creative_request.py +153 -0
  77. adcp/types/generated_poc/creative/preview_creative_response.py +169 -0
  78. adcp/types/generated_poc/creative/preview_render.py +152 -0
  79. adcp/types/generated_poc/enums/__init__.py +3 -0
  80. adcp/types/generated_poc/enums/adcp_domain.py +12 -0
  81. adcp/types/generated_poc/enums/asset_content_type.py +23 -0
  82. adcp/types/generated_poc/enums/auth_scheme.py +12 -0
  83. adcp/types/generated_poc/enums/available_metric.py +19 -0
  84. adcp/types/generated_poc/enums/channels.py +19 -0
  85. adcp/types/generated_poc/enums/co_branding_requirement.py +13 -0
  86. adcp/types/generated_poc/enums/creative_action.py +15 -0
  87. adcp/types/generated_poc/enums/creative_agent_capability.py +14 -0
  88. adcp/types/generated_poc/enums/creative_sort_field.py +16 -0
  89. adcp/types/generated_poc/enums/creative_status.py +14 -0
  90. adcp/types/generated_poc/enums/daast_tracking_event.py +21 -0
  91. adcp/types/generated_poc/enums/daast_version.py +12 -0
  92. adcp/types/generated_poc/enums/delivery_type.py +12 -0
  93. adcp/types/generated_poc/enums/dimension_unit.py +14 -0
  94. adcp/types/generated_poc/enums/feed_format.py +13 -0
  95. adcp/types/generated_poc/enums/feedback_source.py +14 -0
  96. adcp/types/generated_poc/enums/format_category.py +17 -0
  97. adcp/types/generated_poc/enums/format_id_parameter.py +12 -0
  98. adcp/types/generated_poc/enums/frequency_cap_scope.py +16 -0
  99. adcp/types/generated_poc/enums/history_entry_type.py +12 -0
  100. adcp/types/generated_poc/enums/http_method.py +12 -0
  101. adcp/types/generated_poc/enums/identifier_types.py +29 -0
  102. adcp/types/generated_poc/enums/javascript_module_type.py +13 -0
  103. adcp/types/generated_poc/enums/landing_page_requirement.py +13 -0
  104. adcp/types/generated_poc/enums/markdown_flavor.py +12 -0
  105. adcp/types/generated_poc/enums/media_buy_status.py +14 -0
  106. adcp/types/generated_poc/enums/metric_type.py +18 -0
  107. adcp/types/generated_poc/enums/notification_type.py +14 -0
  108. adcp/types/generated_poc/enums/pacing.py +13 -0
  109. adcp/types/generated_poc/enums/preview_output_format.py +12 -0
  110. adcp/types/generated_poc/enums/pricing_model.py +17 -0
  111. adcp/types/generated_poc/enums/property_type.py +17 -0
  112. adcp/types/generated_poc/enums/publisher_identifier_types.py +15 -0
  113. adcp/types/generated_poc/enums/reporting_frequency.py +13 -0
  114. adcp/types/generated_poc/enums/signal_catalog_type.py +13 -0
  115. adcp/types/generated_poc/enums/sort_direction.py +12 -0
  116. adcp/types/generated_poc/enums/standard_format_ids.py +45 -0
  117. adcp/types/generated_poc/enums/task_status.py +19 -0
  118. adcp/types/generated_poc/enums/task_type.py +15 -0
  119. adcp/types/generated_poc/enums/update_frequency.py +14 -0
  120. adcp/types/generated_poc/enums/url_asset_type.py +13 -0
  121. adcp/types/generated_poc/enums/validation_mode.py +12 -0
  122. adcp/types/generated_poc/enums/vast_tracking_event.py +26 -0
  123. adcp/types/generated_poc/enums/vast_version.py +15 -0
  124. adcp/types/generated_poc/enums/webhook_response_type.py +14 -0
  125. adcp/types/generated_poc/enums/webhook_security_method.py +13 -0
  126. adcp/types/generated_poc/media_buy/__init__.py +3 -0
  127. adcp/types/generated_poc/media_buy/build_creative_request.py +41 -0
  128. adcp/types/generated_poc/media_buy/build_creative_response.py +51 -0
  129. adcp/types/generated_poc/media_buy/create_media_buy_request.py +94 -0
  130. adcp/types/generated_poc/media_buy/create_media_buy_response.py +56 -0
  131. adcp/types/generated_poc/media_buy/get_media_buy_delivery_request.py +47 -0
  132. adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py +235 -0
  133. adcp/types/generated_poc/media_buy/get_products_request.py +48 -0
  134. adcp/types/generated_poc/media_buy/get_products_response.py +28 -0
  135. adcp/types/generated_poc/media_buy/list_authorized_properties_request.py +38 -0
  136. adcp/types/generated_poc/media_buy/list_authorized_properties_response.py +84 -0
  137. adcp/types/generated_poc/media_buy/list_creative_formats_request.py +74 -0
  138. adcp/types/generated_poc/media_buy/list_creative_formats_response.py +56 -0
  139. adcp/types/generated_poc/media_buy/list_creatives_request.py +76 -0
  140. adcp/types/generated_poc/media_buy/list_creatives_response.py +214 -0
  141. adcp/types/generated_poc/media_buy/package_request.py +63 -0
  142. adcp/types/generated_poc/media_buy/provide_performance_feedback_request.py +125 -0
  143. adcp/types/generated_poc/media_buy/provide_performance_feedback_response.py +53 -0
  144. adcp/types/generated_poc/media_buy/sync_creatives_request.py +63 -0
  145. adcp/types/generated_poc/media_buy/sync_creatives_response.py +105 -0
  146. adcp/types/generated_poc/media_buy/update_media_buy_request.py +195 -0
  147. adcp/types/generated_poc/media_buy/update_media_buy_response.py +55 -0
  148. adcp/types/generated_poc/pricing_options/__init__.py +3 -0
  149. adcp/types/generated_poc/pricing_options/cpc_option.py +43 -0
  150. adcp/types/generated_poc/pricing_options/cpcv_option.py +45 -0
  151. adcp/types/generated_poc/pricing_options/cpm_auction_option.py +58 -0
  152. adcp/types/generated_poc/pricing_options/cpm_fixed_option.py +43 -0
  153. adcp/types/generated_poc/pricing_options/cpp_option.py +64 -0
  154. adcp/types/generated_poc/pricing_options/cpv_option.py +77 -0
  155. adcp/types/generated_poc/pricing_options/flat_rate_option.py +93 -0
  156. adcp/types/generated_poc/pricing_options/vcpm_auction_option.py +61 -0
  157. adcp/types/generated_poc/pricing_options/vcpm_fixed_option.py +47 -0
  158. adcp/types/generated_poc/protocols/__init__.py +3 -0
  159. adcp/types/generated_poc/protocols/adcp_extension.py +37 -0
  160. adcp/types/generated_poc/signals/__init__.py +3 -0
  161. adcp/types/generated_poc/signals/activate_signal_request.py +32 -0
  162. adcp/types/generated_poc/signals/activate_signal_response.py +51 -0
  163. adcp/types/generated_poc/signals/get_signals_request.py +53 -0
  164. adcp/types/generated_poc/signals/get_signals_response.py +59 -0
  165. adcp/utils/__init__.py +7 -0
  166. adcp/utils/operation_id.py +15 -0
  167. adcp/utils/preview_cache.py +491 -0
  168. adcp/utils/response_parser.py +171 -0
  169. adcp/validation.py +172 -0
  170. adcp-2.12.0.data/data/ADCP_VERSION +1 -0
  171. adcp-2.12.0.dist-info/METADATA +992 -0
  172. adcp-2.12.0.dist-info/RECORD +176 -0
  173. adcp-2.12.0.dist-info/WHEEL +5 -0
  174. adcp-2.12.0.dist-info/entry_points.txt +2 -0
  175. adcp-2.12.0.dist-info/licenses/LICENSE +17 -0
  176. adcp-2.12.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,992 @@
1
+ Metadata-Version: 2.4
2
+ Name: adcp
3
+ Version: 2.12.0
4
+ Summary: Official Python client for the Ad Context Protocol (AdCP)
5
+ Author-email: AdCP Community <maintainers@adcontextprotocol.org>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/adcontextprotocol/adcp-client-python
8
+ Project-URL: Documentation, https://docs.adcontextprotocol.org
9
+ Project-URL: Repository, https://github.com/adcontextprotocol/adcp-client-python
10
+ Project-URL: Issues, https://github.com/adcontextprotocol/adcp-client-python/issues
11
+ Keywords: adcp,mcp,a2a,protocol,advertising
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: httpx>=0.24.0
25
+ Requires-Dist: pydantic>=2.0.0
26
+ Requires-Dist: typing-extensions>=4.5.0
27
+ Requires-Dist: a2a-sdk>=0.3.0
28
+ Requires-Dist: mcp>=0.9.0
29
+ Requires-Dist: email-validator>=2.0.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
32
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
33
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
34
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
35
+ Requires-Dist: black>=23.0.0; extra == "dev"
36
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
37
+ Requires-Dist: datamodel-code-generator[http]>=0.35.0; extra == "dev"
38
+ Provides-Extra: docs
39
+ Requires-Dist: pdoc3>=0.10.0; extra == "docs"
40
+ Dynamic: license-file
41
+
42
+ # adcp - Python Client for Ad Context Protocol
43
+
44
+ [![PyPI version](https://badge.fury.io/py/adcp.svg)](https://badge.fury.io/py/adcp)
45
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
46
+ [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
47
+
48
+ Official Python client for the **Ad Context Protocol (AdCP)**. Build distributed advertising operations that work synchronously OR asynchronously with the same code.
49
+
50
+ ## The Core Concept
51
+
52
+ AdCP operations are **distributed and asynchronous by default**. An agent might:
53
+ - Complete your request **immediately** (synchronous)
54
+ - Need time to process and **send results via webhook** (asynchronous)
55
+ - Ask for **clarifications** before proceeding
56
+ - Send periodic **status updates** as work progresses
57
+
58
+ **Your code stays the same.** You write handlers once, and they work for both sync completions and webhook deliveries.
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install adcp
64
+ ```
65
+
66
+ > **Note**: This client requires Python 3.10 or later and supports both synchronous and asynchronous workflows.
67
+
68
+ ## Quick Start: Test Helpers
69
+
70
+ The fastest way to get started is using pre-configured test agents with the **`.simple` API**:
71
+
72
+ ```python
73
+ from adcp.testing import test_agent
74
+
75
+ # Zero configuration - just import and call with kwargs!
76
+ products = await test_agent.simple.get_products(
77
+ brief='Coffee subscription service for busy professionals'
78
+ )
79
+
80
+ print(f"Found {len(products.products)} products")
81
+ ```
82
+
83
+ ### Simple vs. Standard API
84
+
85
+ **Every ADCPClient** includes both API styles via the `.simple` accessor:
86
+
87
+ **Simple API** (`client.simple.*`) - Recommended for examples/prototyping:
88
+ ```python
89
+ from adcp.testing import test_agent
90
+
91
+ # Kwargs and direct return - raises on error
92
+ products = await test_agent.simple.get_products(brief='Coffee brands')
93
+ print(products.products[0].name)
94
+ ```
95
+
96
+ **Standard API** (`client.*`) - Recommended for production:
97
+ ```python
98
+ from adcp.testing import test_agent
99
+ from adcp import GetProductsRequest
100
+
101
+ # Explicit request objects and TaskResult wrapper
102
+ request = GetProductsRequest(brief='Coffee brands')
103
+ result = await test_agent.get_products(request)
104
+
105
+ if result.success and result.data:
106
+ print(result.data.products[0].name)
107
+ else:
108
+ print(f"Error: {result.error}")
109
+ ```
110
+
111
+ **When to use which:**
112
+ - **Simple API** (`.simple`): Quick testing, documentation, examples, notebooks
113
+ - **Standard API**: Production code, complex error handling, webhook workflows
114
+
115
+ ### Available Test Helpers
116
+
117
+ Pre-configured agents (all include `.simple` accessor):
118
+ - **`test_agent`**: MCP test agent with authentication
119
+ - **`test_agent_a2a`**: A2A test agent with authentication
120
+ - **`test_agent_no_auth`**: MCP test agent without authentication
121
+ - **`test_agent_a2a_no_auth`**: A2A test agent without authentication
122
+ - **`creative_agent`**: Reference creative agent for preview functionality
123
+ - **`test_agent_client`**: Multi-agent client with both protocols
124
+
125
+ > **Note**: Test agents are rate-limited and for testing/examples only. DO NOT use in production.
126
+
127
+ See [examples/simple_api_demo.py](examples/simple_api_demo.py) for a complete comparison.
128
+
129
+ > **Tip**: Import types from the main `adcp` package (e.g., `from adcp import GetProductsRequest`) rather than `adcp.types.generated` for better API stability.
130
+
131
+ ## Quick Start: Distributed Operations
132
+
133
+ For production use, configure your own agents:
134
+
135
+ ```python
136
+ from adcp import ADCPMultiAgentClient, AgentConfig, GetProductsRequest
137
+
138
+ # Configure agents and handlers (context manager ensures proper cleanup)
139
+ async with ADCPMultiAgentClient(
140
+ agents=[
141
+ AgentConfig(
142
+ id="agent_x",
143
+ agent_uri="https://agent-x.com",
144
+ protocol="a2a"
145
+ ),
146
+ AgentConfig(
147
+ id="agent_y",
148
+ agent_uri="https://agent-y.com/mcp/",
149
+ protocol="mcp"
150
+ )
151
+ ],
152
+ # Webhook URL template (macros: {agent_id}, {task_type}, {operation_id})
153
+ webhook_url_template="https://myapp.com/webhook/{task_type}/{agent_id}/{operation_id}",
154
+
155
+ # Activity callback - fires for ALL events
156
+ on_activity=lambda activity: print(f"[{activity.type}] {activity.task_type}"),
157
+
158
+ # Status change handlers
159
+ handlers={
160
+ "on_get_products_status_change": lambda response, metadata: (
161
+ db.save_products(metadata.operation_id, response.products)
162
+ if metadata.status == "completed" else None
163
+ )
164
+ }
165
+ ) as client:
166
+ # Execute operation - library handles operation IDs, webhook URLs, context management
167
+ agent = client.agent("agent_x")
168
+ request = GetProductsRequest(brief="Coffee brands")
169
+ result = await agent.get_products(request)
170
+
171
+ # Check result
172
+ if result.status == "completed":
173
+ # Agent completed synchronously!
174
+ print(f"✅ Sync completion: {len(result.data.products)} products")
175
+
176
+ if result.status == "submitted":
177
+ # Agent will send webhook when complete
178
+ print(f"⏳ Async - webhook registered at: {result.submitted.webhook_url}")
179
+ # Connections automatically cleaned up here
180
+ ```
181
+
182
+ ## Documentation
183
+
184
+ - **[API Reference](https://adcontextprotocol.github.io/adcp-client-python/)** - Complete API documentation with type signatures and examples
185
+ - **[Protocol Spec](https://github.com/adcontextprotocol/adcp)** - Ad Context Protocol specification
186
+ - **[Examples](examples/)** - Code examples and usage patterns
187
+
188
+ The API reference documentation is automatically generated from the code and includes:
189
+ - Full type signatures for all methods
190
+ - Field descriptions from JSON Schema
191
+ - Method documentation with examples
192
+ - Searchable interface
193
+
194
+ ## Features
195
+
196
+ ### Test Helpers
197
+
198
+ Pre-configured test agents for instant prototyping and testing:
199
+
200
+ ```python
201
+ from adcp.testing import (
202
+ test_agent, test_agent_a2a,
203
+ test_agent_no_auth, test_agent_a2a_no_auth,
204
+ creative_agent, test_agent_client, create_test_agent
205
+ )
206
+ from adcp import GetProductsRequest, PreviewCreativeRequest
207
+
208
+ # 1. Single agent with authentication (MCP)
209
+ result = await test_agent.get_products(
210
+ GetProductsRequest(brief="Coffee brands")
211
+ )
212
+
213
+ # 2. Single agent with authentication (A2A)
214
+ result = await test_agent_a2a.get_products(
215
+ GetProductsRequest(brief="Coffee brands")
216
+ )
217
+
218
+ # 3. Single agent WITHOUT authentication (MCP)
219
+ # Useful for testing unauthenticated behavior
220
+ result = await test_agent_no_auth.get_products(
221
+ GetProductsRequest(brief="Coffee brands")
222
+ )
223
+
224
+ # 4. Single agent WITHOUT authentication (A2A)
225
+ result = await test_agent_a2a_no_auth.get_products(
226
+ GetProductsRequest(brief="Coffee brands")
227
+ )
228
+
229
+ # 5. Creative agent (preview functionality, no auth required)
230
+ result = await creative_agent.preview_creative(
231
+ PreviewCreativeRequest(
232
+ manifest={"format_id": "banner_300x250", "assets": {...}}
233
+ )
234
+ )
235
+
236
+ # 6. Multi-agent (parallel execution with both protocols)
237
+ results = await test_agent_client.get_products(
238
+ GetProductsRequest(brief="Coffee brands")
239
+ )
240
+
241
+ # 7. Custom configuration
242
+ from adcp.client import ADCPClient
243
+ config = create_test_agent(id="my-test", timeout=60.0)
244
+ client = ADCPClient(config)
245
+ ```
246
+
247
+ **Use cases:**
248
+ - Quick prototyping and experimentation
249
+ - Example code and documentation
250
+ - Integration testing without mock servers
251
+ - Testing authentication behavior (comparing auth vs no-auth results)
252
+ - Learning AdCP concepts
253
+
254
+ **Important:** Test agents are public, rate-limited, and for testing only. Never use in production.
255
+
256
+ ### Full Protocol Support
257
+ - **A2A Protocol**: Native support for Agent-to-Agent protocol
258
+ - **MCP Protocol**: Native support for Model Context Protocol
259
+ - **Auto-detection**: Automatically detect which protocol an agent uses
260
+
261
+ ### Type Safety
262
+
263
+ Full type hints with Pydantic validation and auto-generated types from the AdCP spec. All commonly-used types are exported from the main `adcp` package for convenience:
264
+
265
+ ```python
266
+ from adcp import (
267
+ GetProductsRequest,
268
+ BrandManifest,
269
+ Package,
270
+ CpmFixedRatePricingOption,
271
+ MediaBuyStatus,
272
+ )
273
+
274
+ # All methods require typed request objects
275
+ request = GetProductsRequest(brief="Coffee brands", max_results=10)
276
+ result = await agent.get_products(request)
277
+ # result: TaskResult[GetProductsResponse]
278
+
279
+ if result.success:
280
+ for product in result.data.products:
281
+ print(product.name, product.pricing_options) # Full IDE autocomplete!
282
+
283
+ # Type-safe pricing with discriminators
284
+ pricing = CpmFixedRatePricingOption(
285
+ pricing_option_id="cpm_usd",
286
+ pricing_model="cpm",
287
+ is_fixed=True, # Literal[True] - type checked!
288
+ currency="USD",
289
+ rate=5.0
290
+ )
291
+
292
+ # Type-safe status enums
293
+ if media_buy.status == MediaBuyStatus.active:
294
+ print("Media buy is active")
295
+ ```
296
+
297
+ **Exported from main package:**
298
+ - **Core domain types**: `BrandManifest`, `Creative`, `CreativeManifest`, `MediaBuy`, `Package`
299
+ - **Status enums**: `CreativeStatus`, `MediaBuyStatus`, `PackageStatus`, `PricingModel`
300
+ - **All 9 pricing options**: `CpcPricingOption`, `CpmFixedRatePricingOption`, `VcpmAuctionPricingOption`, etc.
301
+ - **Request/Response types**: All 16 operations with full request/response types
302
+
303
+ #### Semantic Type Aliases
304
+
305
+ For discriminated union types (success/error responses), use semantic aliases for clearer code:
306
+
307
+ ```python
308
+ from adcp import (
309
+ CreateMediaBuySuccessResponse, # Clear: this is the success case
310
+ CreateMediaBuyErrorResponse, # Clear: this is the error case
311
+ )
312
+
313
+ def handle_response(
314
+ response: CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse
315
+ ) -> None:
316
+ if isinstance(response, CreateMediaBuySuccessResponse):
317
+ print(f"✅ Media buy created: {response.media_buy_id}")
318
+ else:
319
+ print(f"❌ Errors: {response.errors}")
320
+ ```
321
+
322
+ **Available semantic aliases:**
323
+ - Response types: `*SuccessResponse` / `*ErrorResponse` (e.g., `CreateMediaBuySuccessResponse`)
324
+ - Request variants: `*FormatRequest` / `*ManifestRequest` (e.g., `PreviewCreativeFormatRequest`)
325
+ - Preview renders: `PreviewRenderImage` / `PreviewRenderHtml` / `PreviewRenderIframe`
326
+ - Activation keys: `PropertyIdActivationKey` / `PropertyTagActivationKey`
327
+
328
+ See `examples/type_aliases_demo.py` for more examples.
329
+
330
+ **Import guidelines:**
331
+ - ✅ **DO**: Import from main package: `from adcp import GetProductsRequest`
332
+ - ✅ **DO**: Use semantic aliases: `from adcp import CreateMediaBuySuccessResponse`
333
+ - ⚠️ **AVOID**: Import from internal modules: `from adcp.types._generated import CreateMediaBuyResponse1`
334
+
335
+ The main package exports provide a stable API while internal generated types may change.
336
+
337
+ ### Multi-Agent Operations
338
+ Execute across multiple agents simultaneously:
339
+
340
+ ```python
341
+ from adcp import GetProductsRequest
342
+
343
+ # Parallel execution across all agents
344
+ request = GetProductsRequest(brief="Coffee brands")
345
+ results = await client.get_products(request)
346
+
347
+ for result in results:
348
+ if result.status == "completed":
349
+ print(f"Sync: {len(result.data.products)} products")
350
+ elif result.status == "submitted":
351
+ print(f"Async: webhook to {result.submitted.webhook_url}")
352
+ ```
353
+
354
+ ### Webhook Handling
355
+ Single endpoint handles all webhooks:
356
+
357
+ ```python
358
+ from fastapi import FastAPI, Request
359
+
360
+ app = FastAPI()
361
+
362
+ @app.post("/webhook/{task_type}/{agent_id}/{operation_id}")
363
+ async def webhook(task_type: str, agent_id: str, operation_id: str, request: Request):
364
+ payload = await request.json()
365
+ payload["task_type"] = task_type
366
+ payload["operation_id"] = operation_id
367
+
368
+ # Route to agent client - handlers fire automatically
369
+ agent = client.agent(agent_id)
370
+ await agent.handle_webhook(
371
+ payload,
372
+ request.headers.get("x-adcp-signature")
373
+ )
374
+
375
+ return {"received": True}
376
+ ```
377
+
378
+ ### Security
379
+ Webhook signature verification built-in:
380
+
381
+ ```python
382
+ client = ADCPMultiAgentClient(
383
+ agents=agents,
384
+ webhook_secret=os.getenv("WEBHOOK_SECRET")
385
+ )
386
+ # Signatures verified automatically on handle_webhook()
387
+ ```
388
+
389
+ ### Debug Mode
390
+
391
+ Enable debug mode to see full request/response details:
392
+
393
+ ```python
394
+ agent_config = AgentConfig(
395
+ id="agent_x",
396
+ agent_uri="https://agent-x.com",
397
+ protocol="mcp",
398
+ debug=True # Enable debug mode
399
+ )
400
+
401
+ result = await client.agent("agent_x").get_products(brief="Coffee brands")
402
+
403
+ # Access debug information
404
+ if result.debug_info:
405
+ print(f"Duration: {result.debug_info.duration_ms}ms")
406
+ print(f"Request: {result.debug_info.request}")
407
+ print(f"Response: {result.debug_info.response}")
408
+ ```
409
+
410
+ Or use the CLI:
411
+
412
+ ```bash
413
+ uvx adcp --debug myagent get_products '{"brief":"TV ads"}'
414
+ ```
415
+
416
+ ### Resource Management
417
+
418
+ **Why use async context managers?**
419
+ - Ensures HTTP connections are properly closed, preventing resource leaks
420
+ - Handles cleanup even when exceptions occur
421
+ - Required for production applications with connection pooling
422
+ - Prevents issues with async task group cleanup in MCP protocol
423
+
424
+ The recommended pattern uses async context managers:
425
+
426
+ ```python
427
+ from adcp import ADCPClient, AgentConfig, GetProductsRequest
428
+
429
+ # Recommended: Automatic cleanup with context manager
430
+ config = AgentConfig(id="agent_x", agent_uri="https://...", protocol="a2a")
431
+ async with ADCPClient(config) as client:
432
+ request = GetProductsRequest(brief="Coffee brands")
433
+ result = await client.get_products(request)
434
+ # Connection automatically closed on exit
435
+
436
+ # Multi-agent client also supports context managers
437
+ async with ADCPMultiAgentClient(agents) as client:
438
+ # Execute across all agents in parallel
439
+ results = await client.get_products(request)
440
+ # All agent connections closed automatically (even if some failed)
441
+ ```
442
+
443
+ Manual cleanup is available for special cases (e.g., managing client lifecycle manually):
444
+
445
+ ```python
446
+ # Use manual cleanup when you need fine-grained control over lifecycle
447
+ client = ADCPClient(config)
448
+ try:
449
+ result = await client.get_products(request)
450
+ finally:
451
+ await client.close() # Explicit cleanup
452
+ ```
453
+
454
+ **When to use manual cleanup:**
455
+ - Managing client lifecycle across multiple functions
456
+ - Testing scenarios requiring explicit control
457
+ - Integration with frameworks that manage resources differently
458
+
459
+ In most cases, prefer the context manager pattern.
460
+
461
+ ### Error Handling
462
+
463
+ The library provides a comprehensive exception hierarchy with helpful error messages:
464
+
465
+ ```python
466
+ from adcp.exceptions import (
467
+ ADCPError, # Base exception
468
+ ADCPConnectionError, # Connection failed
469
+ ADCPAuthenticationError, # Auth failed (401, 403)
470
+ ADCPTimeoutError, # Request timed out
471
+ ADCPProtocolError, # Invalid response format
472
+ ADCPToolNotFoundError, # Tool not found
473
+ ADCPWebhookSignatureError # Invalid webhook signature
474
+ )
475
+
476
+ try:
477
+ result = await client.agent("agent_x").get_products(brief="Coffee")
478
+ except ADCPAuthenticationError as e:
479
+ # Exception includes agent context and helpful suggestions
480
+ print(f"Auth failed for {e.agent_id}: {e.message}")
481
+ print(f"Suggestion: {e.suggestion}")
482
+ except ADCPTimeoutError as e:
483
+ print(f"Request timed out after {e.timeout}s")
484
+ except ADCPConnectionError as e:
485
+ print(f"Connection failed: {e.message}")
486
+ print(f"Agent URI: {e.agent_uri}")
487
+ except ADCPError as e:
488
+ # Catch-all for other AdCP errors
489
+ print(f"AdCP error: {e.message}")
490
+ ```
491
+
492
+ All exceptions include:
493
+ - **Contextual information**: agent ID, URI, and operation details
494
+ - **Actionable suggestions**: specific steps to fix common issues
495
+ - **Error classification**: proper HTTP status code handling
496
+
497
+ ## Available Tools
498
+
499
+ All AdCP tools with full type safety:
500
+
501
+ **Media Buy Lifecycle:**
502
+ - `get_products()` - Discover advertising products
503
+ - `list_creative_formats()` - Get supported creative formats
504
+ - `create_media_buy()` - Create new media buy
505
+ - `update_media_buy()` - Update existing media buy
506
+ - `sync_creatives()` - Upload/sync creative assets
507
+ - `list_creatives()` - List creative assets
508
+ - `get_media_buy_delivery()` - Get delivery performance
509
+
510
+ **Creative Management:**
511
+ - `preview_creative()` - Preview creative before building
512
+ - `build_creative()` - Generate production-ready creative assets
513
+
514
+ **Audience & Targeting:**
515
+ - `list_authorized_properties()` - Get authorized properties
516
+ - `get_signals()` - Get audience signals
517
+ - `activate_signal()` - Activate audience signals
518
+ - `provide_performance_feedback()` - Send performance feedback
519
+
520
+ ## Workflow Examples
521
+
522
+ ### Complete Media Buy Workflow
523
+
524
+ A typical media buy workflow involves discovering products, creating the buy, and managing creatives:
525
+
526
+ ```python
527
+ from adcp import ADCPClient, AgentConfig, GetProductsRequest, CreateMediaBuyRequest
528
+ from adcp import BrandManifest, PublisherPropertiesAll
529
+
530
+ # 1. Connect to agent
531
+ config = AgentConfig(id="sales_agent", agent_uri="https://...", protocol="mcp")
532
+ async with ADCPClient(config) as client:
533
+
534
+ # 2. Discover available products
535
+ products_result = await client.get_products(
536
+ GetProductsRequest(brief="Premium video inventory for coffee brand")
537
+ )
538
+
539
+ if products_result.success:
540
+ product = products_result.data.products[0]
541
+ print(f"Found product: {product.name}")
542
+
543
+ # 3. Create media buy reservation
544
+ media_buy_result = await client.create_media_buy(
545
+ CreateMediaBuyRequest(
546
+ brand_manifest=BrandManifest(
547
+ name="Coffee Co",
548
+ brand_url="https://coffeeco.com",
549
+ logo_url="https://coffeeco.com/logo.png",
550
+ # ... additional brand details
551
+ ),
552
+ packages=[{
553
+ "package_id": product.packages[0].package_id,
554
+ "quantity": 1000000 # impressions
555
+ }],
556
+ publisher_properties=PublisherPropertiesAll(
557
+ selection_type="all" # Target all authorized properties
558
+ )
559
+ )
560
+ )
561
+
562
+ if media_buy_result.success:
563
+ media_buy_id = media_buy_result.data.media_buy_id
564
+ print(f"✅ Media buy created: {media_buy_id}")
565
+
566
+ # 4. Update media buy if needed
567
+ from adcp import UpdateMediaBuyPackagesRequest
568
+
569
+ update_result = await client.update_media_buy(
570
+ UpdateMediaBuyPackagesRequest(
571
+ media_buy_id=media_buy_id,
572
+ packages=[{
573
+ "package_id": product.packages[0].package_id,
574
+ "quantity": 1500000 # Increase budget
575
+ }]
576
+ )
577
+ )
578
+
579
+ if update_result.success:
580
+ print("✅ Media buy updated")
581
+ ```
582
+
583
+ ### Complete Creative Workflow
584
+
585
+ Build and deliver production-ready creatives:
586
+
587
+ ```python
588
+ from adcp import ADCPClient, AgentConfig
589
+ from adcp import PreviewCreativeFormatRequest, BuildCreativeRequest
590
+ from adcp import CreativeManifest, PlatformDeployment
591
+
592
+ # 1. Connect to creative agent
593
+ config = AgentConfig(id="creative_agent", agent_uri="https://...", protocol="mcp")
594
+ async with ADCPClient(config) as client:
595
+
596
+ # 2. List available formats
597
+ formats_result = await client.list_creative_formats()
598
+
599
+ if formats_result.success:
600
+ format_id = formats_result.data.formats[0].format_id
601
+ print(f"Using format: {format_id.id}")
602
+
603
+ # 3. Preview creative (test before building)
604
+ preview_result = await client.preview_creative(
605
+ PreviewCreativeFormatRequest(
606
+ target_format_id=format_id.id,
607
+ inputs={
608
+ "headline": "Fresh Coffee Daily",
609
+ "cta": "Order Now"
610
+ },
611
+ output_format="url" # Get preview URL
612
+ )
613
+ )
614
+
615
+ if preview_result.success:
616
+ preview_url = preview_result.data.renders[0].url
617
+ print(f"Preview at: {preview_url}")
618
+
619
+ # 4. Build production creative
620
+ build_result = await client.build_creative(
621
+ BuildCreativeRequest(
622
+ manifest=CreativeManifest(
623
+ format_id=format_id,
624
+ brand_url="https://coffeeco.com",
625
+ # ... creative content
626
+ ),
627
+ target_format_id=format_id.id,
628
+ deployment=PlatformDeployment(
629
+ type="platform",
630
+ platform_id="google_admanager"
631
+ )
632
+ )
633
+ )
634
+
635
+ if build_result.success:
636
+ vast_url = build_result.data.assets[0].url
637
+ print(f"✅ Creative ready: {vast_url}")
638
+ ```
639
+
640
+ ### Integrated Workflow: Media Buy + Creatives
641
+
642
+ Combine both workflows for a complete campaign setup:
643
+
644
+ ```python
645
+ from adcp import ADCPMultiAgentClient, AgentConfig
646
+ from adcp import GetProductsRequest, CreateMediaBuyRequest, BuildCreativeRequest
647
+
648
+ # Connect to both sales and creative agents
649
+ async with ADCPMultiAgentClient(
650
+ agents=[
651
+ AgentConfig(id="sales", agent_uri="https://sales-agent.com", protocol="mcp"),
652
+ AgentConfig(id="creative", agent_uri="https://creative-agent.com", protocol="mcp"),
653
+ ]
654
+ ) as client:
655
+
656
+ # 1. Get products from sales agent
657
+ sales_agent = client.agent("sales")
658
+ products = await sales_agent.simple.get_products(
659
+ brief="Premium video inventory"
660
+ )
661
+
662
+ # 2. Get creative formats from creative agent
663
+ creative_agent = client.agent("creative")
664
+ formats = await creative_agent.simple.list_creative_formats()
665
+
666
+ # 3. Build creative asset
667
+ creative_result = await creative_agent.build_creative(
668
+ BuildCreativeRequest(
669
+ manifest=creative_manifest,
670
+ target_format_id=formats.formats[0].format_id.id
671
+ )
672
+ )
673
+
674
+ # 4. Create media buy with creative
675
+ media_buy_result = await sales_agent.create_media_buy(
676
+ CreateMediaBuyRequest(
677
+ brand_manifest=brand_manifest,
678
+ packages=[{"package_id": products.products[0].packages[0].package_id}],
679
+ publisher_properties=publisher_properties,
680
+ creative_urls=[creative_result.data.assets[0].url]
681
+ )
682
+ )
683
+
684
+ print(f"✅ Campaign live: {media_buy_result.data.media_buy_id}")
685
+ ```
686
+
687
+ ## Property Discovery (AdCP v2.2.0)
688
+
689
+ Build agent registries by discovering properties agents can sell:
690
+
691
+ ```python
692
+ from adcp.discovery import PropertyCrawler, get_property_index
693
+
694
+ # Crawl agents to discover properties
695
+ crawler = PropertyCrawler()
696
+ await crawler.crawl_agents([
697
+ {"agent_url": "https://agent-x.com", "protocol": "a2a"},
698
+ {"agent_url": "https://agent-y.com/mcp/", "protocol": "mcp"}
699
+ ])
700
+
701
+ index = get_property_index()
702
+
703
+ # Query 1: Who can sell this property?
704
+ matches = index.find_agents_for_property("domain", "cnn.com")
705
+
706
+ # Query 2: What can this agent sell?
707
+ auth = index.get_agent_authorizations("https://agent-x.com")
708
+
709
+ # Query 3: Find by tags
710
+ premium = index.find_agents_by_property_tags(["premium", "ctv"])
711
+ ```
712
+
713
+ ## Publisher Authorization Validation
714
+
715
+ Verify sales agents are authorized to sell publisher properties via adagents.json:
716
+
717
+ ```python
718
+ from adcp import (
719
+ fetch_adagents,
720
+ verify_agent_authorization,
721
+ verify_agent_for_property,
722
+ )
723
+
724
+ # Fetch and parse adagents.json from publisher
725
+ adagents_data = await fetch_adagents("publisher.com")
726
+
727
+ # Verify agent authorization for a property
728
+ is_authorized = verify_agent_authorization(
729
+ adagents_data=adagents_data,
730
+ agent_url="https://sales-agent.example.com",
731
+ property_type="website",
732
+ property_identifiers=[{"type": "domain", "value": "publisher.com"}]
733
+ )
734
+
735
+ # Or use convenience wrapper (fetch + verify in one call)
736
+ is_authorized = await verify_agent_for_property(
737
+ publisher_domain="publisher.com",
738
+ agent_url="https://sales-agent.example.com",
739
+ property_identifiers=[{"type": "domain", "value": "publisher.com"}],
740
+ property_type="website"
741
+ )
742
+ ```
743
+
744
+ **Domain Matching Rules:**
745
+ - Exact match: `example.com` matches `example.com`
746
+ - Common subdomains: `www.example.com` matches `example.com`
747
+ - Wildcards: `api.example.com` matches `*.example.com`
748
+ - Protocol-agnostic: `http://agent.com` matches `https://agent.com`
749
+
750
+ **Use Cases:**
751
+ - Sales agents verify authorization before accepting media buys
752
+ - Publishers test their adagents.json files
753
+ - Developer tools build authorization validators
754
+
755
+ See `examples/adagents_validation.py` for complete examples.
756
+
757
+ ### Authorization Discovery
758
+
759
+ Discover which publishers have authorized your agent using two approaches:
760
+
761
+ **1. "Push" Approach** - Ask the agent (recommended, fastest):
762
+ ```python
763
+ from adcp import ADCPClient
764
+
765
+ async with ADCPClient(agent_config) as client:
766
+ # Single API call to agent
767
+ response = await client.simple.list_authorized_properties()
768
+ print(f"Authorized for: {response.publisher_domains}")
769
+ ```
770
+
771
+ **2. "Pull" Approach** - Check publisher adagents.json files (when you need property details):
772
+ ```python
773
+ from adcp import fetch_agent_authorizations
774
+
775
+ # Check specific publishers (fetches in parallel)
776
+ contexts = await fetch_agent_authorizations(
777
+ "https://our-sales-agent.com",
778
+ ["nytimes.com", "wsj.com", "cnn.com"]
779
+ )
780
+
781
+ for domain, ctx in contexts.items():
782
+ print(f"{domain}:")
783
+ print(f" Property IDs: {ctx.property_ids}")
784
+ print(f" Tags: {ctx.property_tags}")
785
+ ```
786
+
787
+ **When to use which:**
788
+ - **Push**: Quick discovery, portfolio overview, high-level authorization check
789
+ - **Pull**: Property-level details, specific publisher list, works offline
790
+
791
+ See `examples/fetch_agent_authorizations.py` for complete examples.
792
+
793
+ ## CLI Tool
794
+
795
+ The `adcp` command-line tool provides easy interaction with AdCP agents without writing code.
796
+
797
+ ### Installation
798
+
799
+ ```bash
800
+ # Install globally
801
+ pip install adcp
802
+
803
+ # Or use uvx to run without installing
804
+ uvx adcp --help
805
+ ```
806
+
807
+ ### Quick Start
808
+
809
+ ```bash
810
+ # Save agent configuration
811
+ uvx adcp --save-auth myagent https://agent.example.com mcp
812
+
813
+ # List tools available on agent
814
+ uvx adcp myagent list_tools
815
+
816
+ # Execute a tool
817
+ uvx adcp myagent get_products '{"brief":"TV ads"}'
818
+
819
+ # Use from stdin
820
+ echo '{"brief":"TV ads"}' | uvx adcp myagent get_products
821
+
822
+ # Use from file
823
+ uvx adcp myagent get_products @request.json
824
+
825
+ # Get JSON output
826
+ uvx adcp --json myagent get_products '{"brief":"TV ads"}'
827
+
828
+ # Enable debug mode
829
+ uvx adcp --debug myagent get_products '{"brief":"TV ads"}'
830
+ ```
831
+
832
+ ### Using Test Agents from CLI
833
+
834
+ The CLI provides easy access to public test agents without configuration:
835
+
836
+ ```bash
837
+ # Use test agent with authentication (MCP)
838
+ uvx adcp https://test-agent.adcontextprotocol.org/mcp/ \
839
+ --auth 1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ \
840
+ get_products '{"brief":"Coffee brands"}'
841
+
842
+ # Use test agent WITHOUT authentication (MCP)
843
+ uvx adcp https://test-agent.adcontextprotocol.org/mcp/ \
844
+ get_products '{"brief":"Coffee brands"}'
845
+
846
+ # Use test agent with authentication (A2A)
847
+ uvx adcp --protocol a2a \
848
+ --auth 1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ \
849
+ https://test-agent.adcontextprotocol.org \
850
+ get_products '{"brief":"Coffee brands"}'
851
+
852
+ # Save test agent for easier access
853
+ uvx adcp --save-auth test-agent https://test-agent.adcontextprotocol.org/mcp/ mcp
854
+ # Enter token when prompted: 1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ
855
+
856
+ # Now use saved config
857
+ uvx adcp test-agent get_products '{"brief":"Coffee brands"}'
858
+
859
+ # Use creative agent (no auth required)
860
+ uvx adcp https://creative.adcontextprotocol.org/mcp \
861
+ preview_creative @creative_manifest.json
862
+ ```
863
+
864
+ **Test Agent Details:**
865
+ - **URL (MCP)**: `https://test-agent.adcontextprotocol.org/mcp/`
866
+ - **URL (A2A)**: `https://test-agent.adcontextprotocol.org`
867
+ - **Auth Token**: `1v8tAhASaUYYp4odoQ1PnMpdqNaMiTrCRqYo9OJp6IQ` (optional, public token)
868
+ - **Rate Limited**: For testing only, not for production
869
+ - **No Auth Mode**: Omit `--auth` flag to test unauthenticated behavior
870
+ ```
871
+
872
+ ### Configuration Management
873
+
874
+ ```bash
875
+ # Save agent with authentication
876
+ uvx adcp --save-auth myagent https://agent.example.com mcp
877
+ # Prompts for optional auth token
878
+
879
+ # List saved agents
880
+ uvx adcp --list-agents
881
+
882
+ # Remove saved agent
883
+ uvx adcp --remove-agent myagent
884
+
885
+ # Show config file location
886
+ uvx adcp --show-config
887
+ ```
888
+
889
+ ### Direct URL Access
890
+
891
+ ```bash
892
+ # Use URL directly without saving
893
+ uvx adcp https://agent.example.com/mcp list_tools
894
+
895
+ # Override protocol
896
+ uvx adcp --protocol a2a https://agent.example.com list_tools
897
+
898
+ # Pass auth token
899
+ uvx adcp --auth YOUR_TOKEN https://agent.example.com list_tools
900
+ ```
901
+
902
+ ### Examples
903
+
904
+ ```bash
905
+ # Get products from saved agent
906
+ uvx adcp myagent get_products '{"brief":"Coffee brands for digital video"}'
907
+
908
+ # Create media buy
909
+ uvx adcp myagent create_media_buy '{
910
+ "name": "Q4 Campaign",
911
+ "budget": 50000,
912
+ "start_date": "2024-01-01",
913
+ "end_date": "2024-03-31"
914
+ }'
915
+
916
+ # List creative formats with JSON output
917
+ uvx adcp --json myagent list_creative_formats | jq '.data'
918
+
919
+ # Debug connection issues
920
+ uvx adcp --debug myagent list_tools
921
+ ```
922
+
923
+ ### Configuration File
924
+
925
+ Agent configurations are stored in `~/.adcp/config.json`:
926
+
927
+ ```json
928
+ {
929
+ "agents": {
930
+ "myagent": {
931
+ "agent_uri": "https://agent.example.com",
932
+ "protocol": "mcp",
933
+ "auth_token": "optional-token"
934
+ }
935
+ }
936
+ }
937
+ ```
938
+
939
+ ## Environment Configuration
940
+
941
+ ```bash
942
+ # .env
943
+ WEBHOOK_URL_TEMPLATE="https://myapp.com/webhook/{task_type}/{agent_id}/{operation_id}"
944
+ WEBHOOK_SECRET="your-webhook-secret"
945
+
946
+ ADCP_AGENTS='[
947
+ {
948
+ "id": "agent_x",
949
+ "agent_uri": "https://agent-x.com",
950
+ "protocol": "a2a",
951
+ "auth_token_env": "AGENT_X_TOKEN"
952
+ }
953
+ ]'
954
+ AGENT_X_TOKEN="actual-token-here"
955
+ ```
956
+
957
+ ```python
958
+ # Auto-discover from environment
959
+ client = ADCPMultiAgentClient.from_env()
960
+ ```
961
+
962
+ ## Development
963
+
964
+ ```bash
965
+ # Install with dev dependencies
966
+ pip install -e ".[dev]"
967
+
968
+ # Run tests
969
+ pytest
970
+
971
+ # Type checking
972
+ mypy src/
973
+
974
+ # Format code
975
+ black src/ tests/
976
+ ruff check src/ tests/
977
+ ```
978
+
979
+ ## Contributing
980
+
981
+ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
982
+
983
+ ## License
984
+
985
+ Apache 2.0 License - see [LICENSE](LICENSE) file for details.
986
+
987
+ ## Support
988
+
989
+ - **API Reference**: [adcontextprotocol.github.io/adcp-client-python](https://adcontextprotocol.github.io/adcp-client-python/)
990
+ - **Protocol Documentation**: [docs.adcontextprotocol.org](https://docs.adcontextprotocol.org)
991
+ - **Issues**: [GitHub Issues](https://github.com/adcontextprotocol/adcp-client-python/issues)
992
+ - **Protocol Spec**: [AdCP Specification](https://github.com/adcontextprotocol/adcp)