adcp 2.1.0__tar.gz → 2.3.0__tar.gz

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 (150) hide show
  1. {adcp-2.1.0/src/adcp.egg-info → adcp-2.3.0}/PKG-INFO +38 -2
  2. {adcp-2.1.0 → adcp-2.3.0}/README.md +36 -0
  3. {adcp-2.1.0 → adcp-2.3.0}/pyproject.toml +2 -2
  4. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/__init__.py +29 -13
  5. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/adagents.py +122 -0
  6. adcp-2.3.0/src/adcp/types/__init__.py +48 -0
  7. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/aliases.py +51 -11
  8. {adcp-2.1.0 → adcp-2.3.0/src/adcp.egg-info}/PKG-INFO +38 -2
  9. {adcp-2.1.0 → adcp-2.3.0}/tests/test_adagents.py +362 -0
  10. {adcp-2.1.0 → adcp-2.3.0}/tests/test_cli.py +22 -0
  11. {adcp-2.1.0 → adcp-2.3.0}/tests/test_discriminated_unions.py +260 -0
  12. {adcp-2.1.0 → adcp-2.3.0}/tests/test_type_aliases.py +108 -3
  13. adcp-2.1.0/src/adcp/types/__init__.py +0 -27
  14. {adcp-2.1.0 → adcp-2.3.0}/LICENSE +0 -0
  15. {adcp-2.1.0 → adcp-2.3.0}/setup.cfg +0 -0
  16. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/__main__.py +0 -0
  17. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/client.py +0 -0
  18. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/config.py +0 -0
  19. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/exceptions.py +0 -0
  20. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/protocols/__init__.py +0 -0
  21. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/protocols/a2a.py +0 -0
  22. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/protocols/base.py +0 -0
  23. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/protocols/mcp.py +0 -0
  24. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/py.typed +0 -0
  25. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/simple.py +0 -0
  26. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/testing/__init__.py +0 -0
  27. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/testing/test_helpers.py +0 -0
  28. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/base.py +0 -0
  29. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/core.py +0 -0
  30. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated.py +0 -0
  31. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/__init__.py +0 -0
  32. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/activate_signal_request.py +0 -0
  33. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/activate_signal_response.py +0 -0
  34. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/activation_key.py +0 -0
  35. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/adagents.py +0 -0
  36. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/asset_type.py +0 -0
  37. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/audio_asset.py +0 -0
  38. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/brand_manifest.py +0 -0
  39. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/brand_manifest_ref.py +0 -0
  40. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/build_creative_request.py +0 -0
  41. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/build_creative_response.py +0 -0
  42. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/channels.py +0 -0
  43. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/cpc_option.py +0 -0
  44. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/cpcv_option.py +0 -0
  45. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/cpm_auction_option.py +0 -0
  46. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/cpm_fixed_option.py +0 -0
  47. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/cpp_option.py +0 -0
  48. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/cpv_option.py +0 -0
  49. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/create_media_buy_request.py +0 -0
  50. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/create_media_buy_response.py +0 -0
  51. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/creative_asset.py +0 -0
  52. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/creative_assignment.py +0 -0
  53. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/creative_manifest.py +0 -0
  54. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/creative_policy.py +0 -0
  55. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/creative_status.py +0 -0
  56. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/css_asset.py +0 -0
  57. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/daast_asset.py +0 -0
  58. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/delivery_metrics.py +0 -0
  59. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/delivery_type.py +0 -0
  60. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/deployment.py +0 -0
  61. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/destination.py +0 -0
  62. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/error.py +0 -0
  63. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/flat_rate_option.py +0 -0
  64. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/format.py +0 -0
  65. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/format_id.py +0 -0
  66. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/frequency_cap.py +0 -0
  67. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/frequency_cap_scope.py +0 -0
  68. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/get_media_buy_delivery_request.py +0 -0
  69. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/get_media_buy_delivery_response.py +0 -0
  70. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/get_products_request.py +0 -0
  71. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/get_products_response.py +0 -0
  72. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/get_signals_request.py +0 -0
  73. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/get_signals_response.py +0 -0
  74. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/html_asset.py +0 -0
  75. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/identifier_types.py +0 -0
  76. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/image_asset.py +0 -0
  77. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/index.py +0 -0
  78. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/javascript_asset.py +0 -0
  79. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/list_authorized_properties_request.py +0 -0
  80. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/list_authorized_properties_response.py +0 -0
  81. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/list_creative_formats_request.py +0 -0
  82. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/list_creative_formats_response.py +0 -0
  83. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/list_creatives_request.py +0 -0
  84. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/list_creatives_response.py +0 -0
  85. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/markdown_asset.py +0 -0
  86. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/measurement.py +0 -0
  87. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/media_buy.py +0 -0
  88. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/media_buy_status.py +0 -0
  89. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/pacing.py +0 -0
  90. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/package.py +0 -0
  91. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/package_request.py +0 -0
  92. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/package_status.py +0 -0
  93. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/performance_feedback.py +0 -0
  94. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/placement.py +0 -0
  95. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/preview_creative_request.py +0 -0
  96. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/preview_creative_response.py +0 -0
  97. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/preview_render.py +0 -0
  98. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/pricing_model.py +0 -0
  99. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/pricing_option.py +0 -0
  100. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/product.py +0 -0
  101. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/promoted_offerings.py +0 -0
  102. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/promoted_products.py +0 -0
  103. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/property.py +0 -0
  104. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/protocol_envelope.py +0 -0
  105. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/provide_performance_feedback_request.py +0 -0
  106. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/provide_performance_feedback_response.py +0 -0
  107. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/publisher_identifier_types.py +0 -0
  108. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/push_notification_config.py +0 -0
  109. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/reporting_capabilities.py +0 -0
  110. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/response.py +0 -0
  111. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/standard_format_ids.py +0 -0
  112. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/start_timing.py +0 -0
  113. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/sub_asset.py +0 -0
  114. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/sync_creatives_request.py +0 -0
  115. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/sync_creatives_response.py +0 -0
  116. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/targeting.py +0 -0
  117. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/task_status.py +0 -0
  118. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/task_type.py +0 -0
  119. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/tasks_get_request.py +0 -0
  120. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/tasks_get_response.py +0 -0
  121. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/tasks_list_request.py +0 -0
  122. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/tasks_list_response.py +0 -0
  123. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/text_asset.py +0 -0
  124. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/update_media_buy_request.py +0 -0
  125. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/update_media_buy_response.py +0 -0
  126. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/url_asset.py +0 -0
  127. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/vast_asset.py +0 -0
  128. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/vcpm_auction_option.py +0 -0
  129. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/vcpm_fixed_option.py +0 -0
  130. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/video_asset.py +0 -0
  131. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/webhook_asset.py +0 -0
  132. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/types/generated_poc/webhook_payload.py +0 -0
  133. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/utils/__init__.py +0 -0
  134. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/utils/operation_id.py +0 -0
  135. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/utils/preview_cache.py +0 -0
  136. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/utils/response_parser.py +0 -0
  137. {adcp-2.1.0 → adcp-2.3.0}/src/adcp/validation.py +0 -0
  138. {adcp-2.1.0 → adcp-2.3.0}/src/adcp.egg-info/SOURCES.txt +0 -0
  139. {adcp-2.1.0 → adcp-2.3.0}/src/adcp.egg-info/dependency_links.txt +0 -0
  140. {adcp-2.1.0 → adcp-2.3.0}/src/adcp.egg-info/entry_points.txt +0 -0
  141. {adcp-2.1.0 → adcp-2.3.0}/src/adcp.egg-info/requires.txt +1 -1
  142. {adcp-2.1.0 → adcp-2.3.0}/src/adcp.egg-info/top_level.txt +0 -0
  143. {adcp-2.1.0 → adcp-2.3.0}/tests/test_client.py +0 -0
  144. {adcp-2.1.0 → adcp-2.3.0}/tests/test_code_generation.py +0 -0
  145. {adcp-2.1.0 → adcp-2.3.0}/tests/test_format_id_validation.py +0 -0
  146. {adcp-2.1.0 → adcp-2.3.0}/tests/test_helpers.py +0 -0
  147. {adcp-2.1.0 → adcp-2.3.0}/tests/test_preview_html.py +0 -0
  148. {adcp-2.1.0 → adcp-2.3.0}/tests/test_protocols.py +0 -0
  149. {adcp-2.1.0 → adcp-2.3.0}/tests/test_response_parser.py +0 -0
  150. {adcp-2.1.0 → adcp-2.3.0}/tests/test_simple_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: adcp
3
- Version: 2.1.0
3
+ Version: 2.3.0
4
4
  Summary: Official Python client for the Ad Context Protocol (AdCP)
5
5
  Author-email: AdCP Community <maintainers@adcontextprotocol.org>
6
6
  License: Apache-2.0
@@ -26,6 +26,7 @@ Requires-Dist: pydantic>=2.0.0
26
26
  Requires-Dist: typing-extensions>=4.5.0
27
27
  Requires-Dist: a2a-sdk>=0.3.0
28
28
  Requires-Dist: mcp>=0.9.0
29
+ Requires-Dist: email-validator>=2.0.0
29
30
  Provides-Extra: dev
30
31
  Requires-Dist: pytest>=7.0.0; extra == "dev"
31
32
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -34,7 +35,6 @@ Requires-Dist: mypy>=1.0.0; extra == "dev"
34
35
  Requires-Dist: black>=23.0.0; extra == "dev"
35
36
  Requires-Dist: ruff>=0.1.0; extra == "dev"
36
37
  Requires-Dist: datamodel-code-generator[http]>=0.35.0; extra == "dev"
37
- Requires-Dist: email-validator>=2.0.0; extra == "dev"
38
38
  Dynamic: license-file
39
39
 
40
40
  # adcp - Python Client for Ad Context Protocol
@@ -544,6 +544,42 @@ is_authorized = await verify_agent_for_property(
544
544
 
545
545
  See `examples/adagents_validation.py` for complete examples.
546
546
 
547
+ ### Authorization Discovery
548
+
549
+ Discover which publishers have authorized your agent using two approaches:
550
+
551
+ **1. "Push" Approach** - Ask the agent (recommended, fastest):
552
+ ```python
553
+ from adcp import ADCPClient
554
+
555
+ async with ADCPClient(agent_config) as client:
556
+ # Single API call to agent
557
+ response = await client.simple.list_authorized_properties()
558
+ print(f"Authorized for: {response.publisher_domains}")
559
+ ```
560
+
561
+ **2. "Pull" Approach** - Check publisher adagents.json files (when you need property details):
562
+ ```python
563
+ from adcp import fetch_agent_authorizations
564
+
565
+ # Check specific publishers (fetches in parallel)
566
+ contexts = await fetch_agent_authorizations(
567
+ "https://our-sales-agent.com",
568
+ ["nytimes.com", "wsj.com", "cnn.com"]
569
+ )
570
+
571
+ for domain, ctx in contexts.items():
572
+ print(f"{domain}:")
573
+ print(f" Property IDs: {ctx.property_ids}")
574
+ print(f" Tags: {ctx.property_tags}")
575
+ ```
576
+
577
+ **When to use which:**
578
+ - **Push**: Quick discovery, portfolio overview, high-level authorization check
579
+ - **Pull**: Property-level details, specific publisher list, works offline
580
+
581
+ See `examples/fetch_agent_authorizations.py` for complete examples.
582
+
547
583
  ## CLI Tool
548
584
 
549
585
  The `adcp` command-line tool provides easy interaction with AdCP agents without writing code.
@@ -505,6 +505,42 @@ is_authorized = await verify_agent_for_property(
505
505
 
506
506
  See `examples/adagents_validation.py` for complete examples.
507
507
 
508
+ ### Authorization Discovery
509
+
510
+ Discover which publishers have authorized your agent using two approaches:
511
+
512
+ **1. "Push" Approach** - Ask the agent (recommended, fastest):
513
+ ```python
514
+ from adcp import ADCPClient
515
+
516
+ async with ADCPClient(agent_config) as client:
517
+ # Single API call to agent
518
+ response = await client.simple.list_authorized_properties()
519
+ print(f"Authorized for: {response.publisher_domains}")
520
+ ```
521
+
522
+ **2. "Pull" Approach** - Check publisher adagents.json files (when you need property details):
523
+ ```python
524
+ from adcp import fetch_agent_authorizations
525
+
526
+ # Check specific publishers (fetches in parallel)
527
+ contexts = await fetch_agent_authorizations(
528
+ "https://our-sales-agent.com",
529
+ ["nytimes.com", "wsj.com", "cnn.com"]
530
+ )
531
+
532
+ for domain, ctx in contexts.items():
533
+ print(f"{domain}:")
534
+ print(f" Property IDs: {ctx.property_ids}")
535
+ print(f" Tags: {ctx.property_tags}")
536
+ ```
537
+
538
+ **When to use which:**
539
+ - **Push**: Quick discovery, portfolio overview, high-level authorization check
540
+ - **Pull**: Property-level details, specific publisher list, works offline
541
+
542
+ See `examples/fetch_agent_authorizations.py` for complete examples.
543
+
508
544
  ## CLI Tool
509
545
 
510
546
  The `adcp` command-line tool provides easy interaction with AdCP agents without writing code.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "adcp"
7
- version = "2.1.0"
7
+ version = "2.3.0"
8
8
  description = "Official Python client for the Ad Context Protocol (AdCP)"
9
9
  authors = [
10
10
  {name = "AdCP Community", email = "maintainers@adcontextprotocol.org"}
@@ -31,6 +31,7 @@ dependencies = [
31
31
  "typing-extensions>=4.5.0",
32
32
  "a2a-sdk>=0.3.0",
33
33
  "mcp>=0.9.0",
34
+ "email-validator>=2.0.0",
34
35
  ]
35
36
 
36
37
  [project.scripts]
@@ -45,7 +46,6 @@ dev = [
45
46
  "black>=23.0.0",
46
47
  "ruff>=0.1.0",
47
48
  "datamodel-code-generator[http]>=0.35.0",
48
- "email-validator>=2.0.0",
49
49
  ]
50
50
 
51
51
  [project.urls]
@@ -8,8 +8,10 @@ Supports both A2A and MCP protocols with full type safety.
8
8
  """
9
9
 
10
10
  from adcp.adagents import (
11
+ AuthorizationContext,
11
12
  domain_matches,
12
13
  fetch_adagents,
14
+ fetch_agent_authorizations,
13
15
  get_all_properties,
14
16
  get_all_tags,
15
17
  get_properties_by_agent,
@@ -56,27 +58,33 @@ from adcp.types import aliases, generated
56
58
  from adcp.types.aliases import (
57
59
  ActivateSignalErrorResponse,
58
60
  ActivateSignalSuccessResponse,
61
+ BothPreviewRender,
59
62
  BuildCreativeErrorResponse,
60
63
  BuildCreativeSuccessResponse,
61
64
  CreateMediaBuyErrorResponse,
62
65
  CreateMediaBuySuccessResponse,
66
+ HtmlPreviewRender,
67
+ InlineDaastAsset,
68
+ InlineVastAsset,
69
+ MediaSubAsset,
63
70
  PreviewCreativeFormatRequest,
64
71
  PreviewCreativeInteractiveResponse,
65
72
  PreviewCreativeManifestRequest,
66
73
  PreviewCreativeStaticResponse,
67
- PreviewRenderHtml,
68
- PreviewRenderIframe,
69
- PreviewRenderImage,
70
74
  PropertyIdActivationKey,
71
75
  PropertyTagActivationKey,
72
76
  ProvidePerformanceFeedbackErrorResponse,
73
77
  ProvidePerformanceFeedbackSuccessResponse,
74
78
  SyncCreativesErrorResponse,
75
79
  SyncCreativesSuccessResponse,
80
+ TextSubAsset,
76
81
  UpdateMediaBuyErrorResponse,
77
82
  UpdateMediaBuyPackagesRequest,
78
83
  UpdateMediaBuyPropertiesRequest,
79
84
  UpdateMediaBuySuccessResponse,
85
+ UrlDaastAsset,
86
+ UrlPreviewRender,
87
+ UrlVastAsset,
80
88
  )
81
89
  from adcp.types.core import AgentConfig, Protocol, TaskResult, TaskStatus, WebhookMetadata
82
90
 
@@ -128,7 +136,7 @@ from adcp.validation import (
128
136
  validate_publisher_properties_item,
129
137
  )
130
138
 
131
- __version__ = "2.1.0"
139
+ __version__ = "2.3.0"
132
140
 
133
141
  __all__ = [
134
142
  # Client classes
@@ -172,7 +180,9 @@ __all__ = [
172
180
  "Product",
173
181
  "Property",
174
182
  # Adagents validation
183
+ "AuthorizationContext",
175
184
  "fetch_adagents",
185
+ "fetch_agent_authorizations",
176
186
  "verify_agent_authorization",
177
187
  "verify_agent_for_property",
178
188
  "domain_matches",
@@ -219,25 +229,31 @@ __all__ = [
219
229
  # Semantic type aliases (for better API ergonomics)
220
230
  "ActivateSignalSuccessResponse",
221
231
  "ActivateSignalErrorResponse",
232
+ "BothPreviewRender",
222
233
  "BuildCreativeSuccessResponse",
223
234
  "BuildCreativeErrorResponse",
224
235
  "CreateMediaBuySuccessResponse",
225
236
  "CreateMediaBuyErrorResponse",
226
- "ProvidePerformanceFeedbackSuccessResponse",
227
- "ProvidePerformanceFeedbackErrorResponse",
228
- "SyncCreativesSuccessResponse",
229
- "SyncCreativesErrorResponse",
230
- "UpdateMediaBuySuccessResponse",
231
- "UpdateMediaBuyErrorResponse",
237
+ "HtmlPreviewRender",
238
+ "InlineDaastAsset",
239
+ "InlineVastAsset",
240
+ "MediaSubAsset",
232
241
  "PreviewCreativeFormatRequest",
233
242
  "PreviewCreativeManifestRequest",
234
243
  "PreviewCreativeStaticResponse",
235
244
  "PreviewCreativeInteractiveResponse",
236
- "PreviewRenderImage",
237
- "PreviewRenderHtml",
238
- "PreviewRenderIframe",
239
245
  "PropertyIdActivationKey",
240
246
  "PropertyTagActivationKey",
247
+ "ProvidePerformanceFeedbackSuccessResponse",
248
+ "ProvidePerformanceFeedbackErrorResponse",
249
+ "SyncCreativesSuccessResponse",
250
+ "SyncCreativesErrorResponse",
251
+ "TextSubAsset",
252
+ "UpdateMediaBuySuccessResponse",
253
+ "UpdateMediaBuyErrorResponse",
241
254
  "UpdateMediaBuyPackagesRequest",
242
255
  "UpdateMediaBuyPropertiesRequest",
256
+ "UrlDaastAsset",
257
+ "UrlPreviewRender",
258
+ "UrlVastAsset",
243
259
  ]
@@ -518,3 +518,125 @@ def get_properties_by_agent(adagents_data: dict[str, Any], agent_url: str) -> li
518
518
  return [p for p in properties if isinstance(p, dict)]
519
519
 
520
520
  return []
521
+
522
+
523
+ class AuthorizationContext:
524
+ """Authorization context for a publisher domain.
525
+
526
+ Attributes:
527
+ property_ids: List of property IDs the agent is authorized for
528
+ property_tags: List of property tags the agent is authorized for
529
+ raw_properties: Raw property data from adagents.json
530
+ """
531
+
532
+ def __init__(self, properties: list[dict[str, Any]]):
533
+ """Initialize from list of properties.
534
+
535
+ Args:
536
+ properties: List of property dictionaries from adagents.json
537
+ """
538
+ self.property_ids: list[str] = []
539
+ self.property_tags: list[str] = []
540
+ self.raw_properties = properties
541
+
542
+ # Extract property IDs and tags
543
+ for prop in properties:
544
+ if not isinstance(prop, dict):
545
+ continue
546
+
547
+ # Extract property ID
548
+ prop_id = prop.get("id")
549
+ if prop_id and isinstance(prop_id, str):
550
+ self.property_ids.append(prop_id)
551
+
552
+ # Extract tags
553
+ tags = prop.get("tags", [])
554
+ if isinstance(tags, list):
555
+ for tag in tags:
556
+ if isinstance(tag, str) and tag not in self.property_tags:
557
+ self.property_tags.append(tag)
558
+
559
+ def __repr__(self) -> str:
560
+ return (
561
+ f"AuthorizationContext("
562
+ f"property_ids={self.property_ids}, "
563
+ f"property_tags={self.property_tags})"
564
+ )
565
+
566
+
567
+ async def fetch_agent_authorizations(
568
+ agent_url: str,
569
+ publisher_domains: list[str],
570
+ timeout: float = 10.0,
571
+ client: httpx.AsyncClient | None = None,
572
+ ) -> dict[str, AuthorizationContext]:
573
+ """Fetch authorization contexts by checking publisher adagents.json files.
574
+
575
+ This function discovers what publishers have authorized your agent by fetching
576
+ their adagents.json files from the .well-known directory and extracting the
577
+ properties your agent can access.
578
+
579
+ This is the "pull" approach - you query publishers to see if they've authorized you.
580
+ For the "push" approach where the agent tells you what it's authorized for,
581
+ use the agent's list_authorized_properties endpoint via ADCPClient.
582
+
583
+ Args:
584
+ agent_url: URL of your sales agent
585
+ publisher_domains: List of publisher domains to check (e.g., ["nytimes.com", "wsj.com"])
586
+ timeout: Request timeout in seconds for each fetch
587
+ client: Optional httpx.AsyncClient for connection pooling
588
+
589
+ Returns:
590
+ Dictionary mapping publisher domain to AuthorizationContext.
591
+ Only includes domains where the agent is authorized.
592
+
593
+ Example:
594
+ >>> # "Pull" approach - check what publishers have authorized you
595
+ >>> contexts = await fetch_agent_authorizations(
596
+ ... "https://our-sales-agent.com",
597
+ ... ["nytimes.com", "wsj.com", "cnn.com"]
598
+ ... )
599
+ >>> for domain, ctx in contexts.items():
600
+ ... print(f"{domain}:")
601
+ ... print(f" Property IDs: {ctx.property_ids}")
602
+ ... print(f" Tags: {ctx.property_tags}")
603
+
604
+ See Also:
605
+ ADCPClient.list_authorized_properties: "Push" approach using the agent's API
606
+
607
+ Notes:
608
+ - Silently skips domains where adagents.json is not found or invalid
609
+ - Only returns domains where the agent is explicitly authorized
610
+ - For production use with many domains, pass a shared httpx.AsyncClient
611
+ to enable connection pooling
612
+ """
613
+ import asyncio
614
+
615
+ # Create tasks to fetch all adagents.json files in parallel
616
+ async def fetch_authorization_for_domain(
617
+ domain: str,
618
+ ) -> tuple[str, AuthorizationContext | None]:
619
+ """Fetch authorization context for a single domain."""
620
+ try:
621
+ adagents_data = await fetch_adagents(domain, timeout=timeout, client=client)
622
+
623
+ # Check if agent is authorized
624
+ if not verify_agent_authorization(adagents_data, agent_url):
625
+ return (domain, None)
626
+
627
+ # Get properties for this agent
628
+ properties = get_properties_by_agent(adagents_data, agent_url)
629
+
630
+ # Create authorization context
631
+ return (domain, AuthorizationContext(properties))
632
+
633
+ except (AdagentsNotFoundError, AdagentsValidationError, AdagentsTimeoutError):
634
+ # Silently skip domains with missing or invalid adagents.json
635
+ return (domain, None)
636
+
637
+ # Fetch all domains in parallel
638
+ tasks = [fetch_authorization_for_domain(domain) for domain in publisher_domains]
639
+ results = await asyncio.gather(*tasks)
640
+
641
+ # Build result dictionary, filtering out None values
642
+ return {domain: ctx for domain, ctx in results if ctx is not None}
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ """Type definitions for AdCP client."""
4
+
5
+ from adcp.types.aliases import (
6
+ BothPreviewRender,
7
+ HtmlPreviewRender,
8
+ InlineDaastAsset,
9
+ InlineVastAsset,
10
+ MediaSubAsset,
11
+ TextSubAsset,
12
+ UrlDaastAsset,
13
+ UrlPreviewRender,
14
+ UrlVastAsset,
15
+ )
16
+ from adcp.types.base import AdCPBaseModel
17
+ from adcp.types.core import (
18
+ Activity,
19
+ ActivityType,
20
+ AgentConfig,
21
+ DebugInfo,
22
+ Protocol,
23
+ TaskResult,
24
+ TaskStatus,
25
+ WebhookMetadata,
26
+ )
27
+
28
+ __all__ = [
29
+ "AdCPBaseModel",
30
+ "AgentConfig",
31
+ "Protocol",
32
+ "TaskResult",
33
+ "TaskStatus",
34
+ "WebhookMetadata",
35
+ "Activity",
36
+ "ActivityType",
37
+ "DebugInfo",
38
+ # Semantic aliases for discriminated unions
39
+ "BothPreviewRender",
40
+ "HtmlPreviewRender",
41
+ "InlineDaastAsset",
42
+ "InlineVastAsset",
43
+ "MediaSubAsset",
44
+ "TextSubAsset",
45
+ "UrlDaastAsset",
46
+ "UrlPreviewRender",
47
+ "UrlVastAsset",
48
+ ]
@@ -45,6 +45,9 @@ from adcp.types.generated import (
45
45
  # Create media buy responses
46
46
  CreateMediaBuyResponse1,
47
47
  CreateMediaBuyResponse2,
48
+ # DAAST assets
49
+ DaastAsset1,
50
+ DaastAsset2,
48
51
  # Preview creative requests
49
52
  PreviewCreativeRequest1,
50
53
  PreviewCreativeRequest2,
@@ -58,6 +61,9 @@ from adcp.types.generated import (
58
61
  # Performance feedback responses
59
62
  ProvidePerformanceFeedbackResponse1,
60
63
  ProvidePerformanceFeedbackResponse2,
64
+ # SubAssets
65
+ SubAsset1,
66
+ SubAsset2,
61
67
  # Sync creatives responses
62
68
  SyncCreativesResponse1,
63
69
  SyncCreativesResponse2,
@@ -67,6 +73,9 @@ from adcp.types.generated import (
67
73
  # Update media buy responses
68
74
  UpdateMediaBuyResponse1,
69
75
  UpdateMediaBuyResponse2,
76
+ # VAST assets
77
+ VastAsset1,
78
+ VastAsset2,
70
79
  )
71
80
 
72
81
  # ============================================================================
@@ -157,15 +166,40 @@ PreviewCreativeStaticResponse = PreviewCreativeResponse1
157
166
  PreviewCreativeInteractiveResponse = PreviewCreativeResponse2
158
167
  """Preview response with interactive renders (iframe embedding)."""
159
168
 
160
- # Preview Render Variants
161
- PreviewRenderImage = PreviewRender1
162
- """Image-based preview render (PNG/JPEG)."""
169
+ # Preview Render Variants (discriminated by output_format)
170
+ UrlPreviewRender = PreviewRender1
171
+ """Preview render with output_format='url' - provides preview_url for iframe embedding."""
163
172
 
164
- PreviewRenderHtml = PreviewRender2
165
- """HTML-based preview render (static markup)."""
173
+ HtmlPreviewRender = PreviewRender2
174
+ """Preview render with output_format='html' - provides preview_html for direct embedding."""
166
175
 
167
- PreviewRenderIframe = PreviewRender3
168
- """Interactive iframe-based preview render."""
176
+ BothPreviewRender = PreviewRender3
177
+ """Preview render with output_format='both' - provides both preview_url and preview_html."""
178
+
179
+ # ============================================================================
180
+ # ASSET TYPE ALIASES - Delivery & Kind Discriminated Unions
181
+ # ============================================================================
182
+
183
+ # VAST Asset Variants (discriminated by delivery_type)
184
+ UrlVastAsset = VastAsset1
185
+ """VAST asset delivered via URL endpoint - delivery_type='url'."""
186
+
187
+ InlineVastAsset = VastAsset2
188
+ """VAST asset with inline XML content - delivery_type='inline'."""
189
+
190
+ # DAAST Asset Variants (discriminated by delivery_type)
191
+ UrlDaastAsset = DaastAsset1
192
+ """DAAST asset delivered via URL endpoint - delivery_type='url'."""
193
+
194
+ InlineDaastAsset = DaastAsset2
195
+ """DAAST asset with inline XML content - delivery_type='inline'."""
196
+
197
+ # SubAsset Variants (discriminated by asset_kind)
198
+ MediaSubAsset = SubAsset1
199
+ """SubAsset for media content (images, videos) - asset_kind='media', provides content_uri."""
200
+
201
+ TextSubAsset = SubAsset2
202
+ """SubAsset for text content (headlines, body text) - asset_kind='text', provides content."""
169
203
 
170
204
  # ============================================================================
171
205
  # EXPORTS
@@ -178,6 +212,16 @@ __all__ = [
178
212
  # Activation keys
179
213
  "PropertyIdActivationKey",
180
214
  "PropertyTagActivationKey",
215
+ # Asset type aliases
216
+ "BothPreviewRender",
217
+ "HtmlPreviewRender",
218
+ "InlineDaastAsset",
219
+ "InlineVastAsset",
220
+ "MediaSubAsset",
221
+ "TextSubAsset",
222
+ "UrlDaastAsset",
223
+ "UrlPreviewRender",
224
+ "UrlVastAsset",
181
225
  # Build creative responses
182
226
  "BuildCreativeSuccessResponse",
183
227
  "BuildCreativeErrorResponse",
@@ -193,10 +237,6 @@ __all__ = [
193
237
  # Preview creative responses
194
238
  "PreviewCreativeStaticResponse",
195
239
  "PreviewCreativeInteractiveResponse",
196
- # Preview renders
197
- "PreviewRenderImage",
198
- "PreviewRenderHtml",
199
- "PreviewRenderIframe",
200
240
  # Sync creatives responses
201
241
  "SyncCreativesSuccessResponse",
202
242
  "SyncCreativesErrorResponse",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: adcp
3
- Version: 2.1.0
3
+ Version: 2.3.0
4
4
  Summary: Official Python client for the Ad Context Protocol (AdCP)
5
5
  Author-email: AdCP Community <maintainers@adcontextprotocol.org>
6
6
  License: Apache-2.0
@@ -26,6 +26,7 @@ Requires-Dist: pydantic>=2.0.0
26
26
  Requires-Dist: typing-extensions>=4.5.0
27
27
  Requires-Dist: a2a-sdk>=0.3.0
28
28
  Requires-Dist: mcp>=0.9.0
29
+ Requires-Dist: email-validator>=2.0.0
29
30
  Provides-Extra: dev
30
31
  Requires-Dist: pytest>=7.0.0; extra == "dev"
31
32
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -34,7 +35,6 @@ Requires-Dist: mypy>=1.0.0; extra == "dev"
34
35
  Requires-Dist: black>=23.0.0; extra == "dev"
35
36
  Requires-Dist: ruff>=0.1.0; extra == "dev"
36
37
  Requires-Dist: datamodel-code-generator[http]>=0.35.0; extra == "dev"
37
- Requires-Dist: email-validator>=2.0.0; extra == "dev"
38
38
  Dynamic: license-file
39
39
 
40
40
  # adcp - Python Client for Ad Context Protocol
@@ -544,6 +544,42 @@ is_authorized = await verify_agent_for_property(
544
544
 
545
545
  See `examples/adagents_validation.py` for complete examples.
546
546
 
547
+ ### Authorization Discovery
548
+
549
+ Discover which publishers have authorized your agent using two approaches:
550
+
551
+ **1. "Push" Approach** - Ask the agent (recommended, fastest):
552
+ ```python
553
+ from adcp import ADCPClient
554
+
555
+ async with ADCPClient(agent_config) as client:
556
+ # Single API call to agent
557
+ response = await client.simple.list_authorized_properties()
558
+ print(f"Authorized for: {response.publisher_domains}")
559
+ ```
560
+
561
+ **2. "Pull" Approach** - Check publisher adagents.json files (when you need property details):
562
+ ```python
563
+ from adcp import fetch_agent_authorizations
564
+
565
+ # Check specific publishers (fetches in parallel)
566
+ contexts = await fetch_agent_authorizations(
567
+ "https://our-sales-agent.com",
568
+ ["nytimes.com", "wsj.com", "cnn.com"]
569
+ )
570
+
571
+ for domain, ctx in contexts.items():
572
+ print(f"{domain}:")
573
+ print(f" Property IDs: {ctx.property_ids}")
574
+ print(f" Tags: {ctx.property_tags}")
575
+ ```
576
+
577
+ **When to use which:**
578
+ - **Push**: Quick discovery, portfolio overview, high-level authorization check
579
+ - **Pull**: Property-level details, specific publisher list, works offline
580
+
581
+ See `examples/fetch_agent_authorizations.py` for complete examples.
582
+
547
583
  ## CLI Tool
548
584
 
549
585
  The `adcp` command-line tool provides easy interaction with AdCP agents without writing code.