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
adcp/types/base.py ADDED
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ """Base model for AdCP types with spec-compliant serialization."""
4
+
5
+ from typing import Any
6
+
7
+ from pydantic import BaseModel
8
+
9
+
10
+ class AdCPBaseModel(BaseModel):
11
+ """Base model for AdCP types with spec-compliant serialization.
12
+
13
+ AdCP JSON schemas use additionalProperties: false and do not allow null
14
+ for optional fields. Therefore, optional fields must be omitted entirely
15
+ when not present (not sent as null).
16
+ """
17
+
18
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
19
+ if "exclude_none" not in kwargs:
20
+ kwargs["exclude_none"] = True
21
+ return super().model_dump(**kwargs)
22
+
23
+ def model_dump_json(self, **kwargs: Any) -> str:
24
+ if "exclude_none" not in kwargs:
25
+ kwargs["exclude_none"] = True
26
+ return super().model_dump_json(**kwargs)
adcp/types/core.py ADDED
@@ -0,0 +1,174 @@
1
+ from __future__ import annotations
2
+
3
+ """Core type definitions."""
4
+
5
+ from enum import Enum
6
+ from typing import Any, Generic, Literal, TypeVar
7
+
8
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
9
+
10
+
11
+ class Protocol(str, Enum):
12
+ """Supported protocols."""
13
+
14
+ A2A = "a2a"
15
+ MCP = "mcp"
16
+
17
+
18
+ class AgentConfig(BaseModel):
19
+ """Agent configuration."""
20
+
21
+ id: str
22
+ agent_uri: str
23
+ protocol: Protocol
24
+ auth_token: str | None = None
25
+ requires_auth: bool = False
26
+ auth_header: str = "x-adcp-auth" # Header name for authentication
27
+ auth_type: str = "token" # "token" for direct value, "bearer" for "Bearer {token}"
28
+ timeout: float = 30.0 # Request timeout in seconds
29
+ mcp_transport: str = (
30
+ "streamable_http" # "streamable_http" (default, modern) or "sse" (legacy fallback)
31
+ )
32
+ debug: bool = False # Enable debug mode to capture request/response details
33
+
34
+ @field_validator("agent_uri")
35
+ @classmethod
36
+ def validate_agent_uri(cls, v: str) -> str:
37
+ """Validate agent URI format."""
38
+ if not v:
39
+ raise ValueError("agent_uri cannot be empty")
40
+
41
+ if not v.startswith(("http://", "https://")):
42
+ raise ValueError(
43
+ f"agent_uri must start with http:// or https://, got: {v}\n"
44
+ "Example: https://agent.example.com"
45
+ )
46
+
47
+ # Remove trailing slash for consistency
48
+ return v.rstrip("/")
49
+
50
+ @field_validator("timeout")
51
+ @classmethod
52
+ def validate_timeout(cls, v: float) -> float:
53
+ """Validate timeout is reasonable."""
54
+ if v <= 0:
55
+ raise ValueError(f"timeout must be positive, got: {v}")
56
+
57
+ if v > 300: # 5 minutes
58
+ raise ValueError(
59
+ f"timeout is very large ({v}s). Consider a value under 300 seconds.\n"
60
+ "Large timeouts can cause long hangs if agent is unresponsive."
61
+ )
62
+
63
+ return v
64
+
65
+ @field_validator("mcp_transport")
66
+ @classmethod
67
+ def validate_mcp_transport(cls, v: str) -> str:
68
+ """Validate MCP transport type."""
69
+ valid_transports = ["streamable_http", "sse"]
70
+ if v not in valid_transports:
71
+ raise ValueError(
72
+ f"mcp_transport must be one of {valid_transports}, got: {v}\n"
73
+ "Use 'streamable_http' for modern agents (recommended)"
74
+ )
75
+ return v
76
+
77
+ @field_validator("auth_type")
78
+ @classmethod
79
+ def validate_auth_type(cls, v: str) -> str:
80
+ """Validate auth type."""
81
+ valid_types = ["token", "bearer"]
82
+ if v not in valid_types:
83
+ raise ValueError(
84
+ f"auth_type must be one of {valid_types}, got: {v}\n"
85
+ "Use 'bearer' for OAuth2/standard Authorization header"
86
+ )
87
+ return v
88
+
89
+
90
+ class TaskStatus(str, Enum):
91
+ """Task execution status."""
92
+
93
+ COMPLETED = "completed"
94
+ SUBMITTED = "submitted"
95
+ NEEDS_INPUT = "needs_input"
96
+ FAILED = "failed"
97
+ WORKING = "working"
98
+
99
+
100
+ T = TypeVar("T")
101
+
102
+
103
+ class SubmittedInfo(BaseModel):
104
+ """Information about submitted async task."""
105
+
106
+ webhook_url: str
107
+ operation_id: str
108
+
109
+
110
+ class NeedsInputInfo(BaseModel):
111
+ """Information when agent needs clarification."""
112
+
113
+ message: str
114
+ field: str | None = None
115
+
116
+
117
+ class DebugInfo(BaseModel):
118
+ """Debug information for troubleshooting."""
119
+
120
+ request: dict[str, Any]
121
+ response: dict[str, Any]
122
+ duration_ms: float | None = None
123
+
124
+
125
+ class TaskResult(BaseModel, Generic[T]):
126
+ """Result from task execution."""
127
+
128
+ model_config = ConfigDict(arbitrary_types_allowed=True)
129
+
130
+ status: TaskStatus
131
+ data: T | None = None
132
+ message: str | None = None # Human-readable message from agent (e.g., MCP content text)
133
+ submitted: SubmittedInfo | None = None
134
+ needs_input: NeedsInputInfo | None = None
135
+ error: str | None = None
136
+ success: bool = Field(default=True)
137
+ metadata: dict[str, Any] | None = None
138
+ debug_info: DebugInfo | None = None
139
+
140
+
141
+ class ActivityType(str, Enum):
142
+ """Types of activity events."""
143
+
144
+ PROTOCOL_REQUEST = "protocol_request"
145
+ PROTOCOL_RESPONSE = "protocol_response"
146
+ WEBHOOK_RECEIVED = "webhook_received"
147
+ HANDLER_CALLED = "handler_called"
148
+ STATUS_CHANGE = "status_change"
149
+
150
+
151
+ class Activity(BaseModel):
152
+ """Activity event for observability."""
153
+
154
+ model_config = {"frozen": True}
155
+
156
+ type: ActivityType
157
+ operation_id: str
158
+ agent_id: str
159
+ task_type: str
160
+ status: TaskStatus | None = None
161
+ timestamp: str
162
+ metadata: dict[str, Any] | None = None
163
+
164
+
165
+ class WebhookMetadata(BaseModel):
166
+ """Metadata passed to webhook handlers."""
167
+
168
+ operation_id: str
169
+ agent_id: str
170
+ task_type: str
171
+ status: TaskStatus
172
+ sequence_number: int | None = None
173
+ notification_type: Literal["scheduled", "final", "delayed"] | None = None
174
+ timestamp: str
@@ -0,0 +1,3 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: .schema_temp
3
+ # timestamp: 2025-11-18T04:34:42+00:00
@@ -0,0 +1,411 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: adagents.json
3
+ # timestamp: 2025-11-22T15:23:24+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Annotated, Literal
8
+
9
+ from adcp.types.base import AdCPBaseModel
10
+ from pydantic import AnyUrl, AwareDatetime, ConfigDict, EmailStr, Field, RootModel
11
+
12
+ from .core import property, property_id, property_tag, publisher_property_selector
13
+
14
+
15
+ class AuthorizedSalesAgents1(AdCPBaseModel):
16
+ model_config = ConfigDict(
17
+ extra='forbid',
18
+ )
19
+ field_schema: Annotated[
20
+ str | None,
21
+ Field(alias='$schema', description='JSON Schema identifier for this adagents.json file'),
22
+ ] = 'https://adcontextprotocol.org/schemas/adagents.json'
23
+ authoritative_location: Annotated[
24
+ AnyUrl,
25
+ Field(
26
+ description='HTTPS URL of the authoritative adagents.json file. When present, this file is a reference and the authoritative location contains the actual agent authorization data.'
27
+ ),
28
+ ]
29
+ last_updated: Annotated[
30
+ AwareDatetime | None,
31
+ Field(description='ISO 8601 timestamp indicating when this reference was last updated'),
32
+ ] = None
33
+
34
+
35
+ class Contact(AdCPBaseModel):
36
+ model_config = ConfigDict(
37
+ extra='forbid',
38
+ )
39
+ domain: Annotated[
40
+ str | None,
41
+ Field(
42
+ description='Primary domain of the entity managing this file',
43
+ pattern='^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$',
44
+ ),
45
+ ] = None
46
+ email: Annotated[
47
+ EmailStr | None,
48
+ Field(
49
+ description='Contact email for questions or issues with this authorization file',
50
+ max_length=255,
51
+ min_length=1,
52
+ ),
53
+ ] = None
54
+ name: Annotated[
55
+ str,
56
+ Field(
57
+ description="Name of the entity managing this file (e.g., 'Meta Advertising Operations', 'Clear Channel Digital')",
58
+ max_length=255,
59
+ min_length=1,
60
+ ),
61
+ ]
62
+ seller_id: Annotated[
63
+ str | None,
64
+ Field(
65
+ description='Seller ID from IAB Tech Lab sellers.json (if applicable)',
66
+ max_length=255,
67
+ min_length=1,
68
+ ),
69
+ ] = None
70
+ tag_id: Annotated[
71
+ str | None,
72
+ Field(
73
+ description='TAG Certified Against Fraud ID for verification (if applicable)',
74
+ max_length=100,
75
+ min_length=1,
76
+ ),
77
+ ] = None
78
+
79
+
80
+ class Tags(AdCPBaseModel):
81
+ model_config = ConfigDict(
82
+ extra='forbid',
83
+ )
84
+ description: Annotated[str, Field(description='Description of what this tag represents')]
85
+ name: Annotated[str, Field(description='Human-readable name for this tag')]
86
+
87
+
88
+ class AuthorizedAgents(AdCPBaseModel):
89
+ model_config = ConfigDict(
90
+ extra='forbid',
91
+ )
92
+ authorization_type: Annotated[
93
+ Literal['property_ids'],
94
+ Field(description='Discriminator indicating authorization by specific property IDs'),
95
+ ]
96
+ authorized_for: Annotated[
97
+ str,
98
+ Field(
99
+ description='Human-readable description of what this agent is authorized to sell',
100
+ max_length=500,
101
+ min_length=1,
102
+ ),
103
+ ]
104
+ property_ids: Annotated[
105
+ list[property_id.PropertyId],
106
+ Field(
107
+ description='Property IDs this agent is authorized for. Resolved against the top-level properties array in this file',
108
+ min_length=1,
109
+ ),
110
+ ]
111
+ url: Annotated[AnyUrl, Field(description="The authorized agent's API endpoint URL")]
112
+
113
+
114
+ class AuthorizedAgents1(AdCPBaseModel):
115
+ model_config = ConfigDict(
116
+ extra='forbid',
117
+ )
118
+ authorization_type: Annotated[
119
+ Literal['property_tags'],
120
+ Field(description='Discriminator indicating authorization by property tags'),
121
+ ]
122
+ authorized_for: Annotated[
123
+ str,
124
+ Field(
125
+ description='Human-readable description of what this agent is authorized to sell',
126
+ max_length=500,
127
+ min_length=1,
128
+ ),
129
+ ]
130
+ property_tags: Annotated[
131
+ list[property_tag.PropertyTag],
132
+ Field(
133
+ description='Tags identifying which properties this agent is authorized for. Resolved against the top-level properties array in this file using tag matching',
134
+ min_length=1,
135
+ ),
136
+ ]
137
+ url: Annotated[AnyUrl, Field(description="The authorized agent's API endpoint URL")]
138
+
139
+
140
+ class AuthorizedAgents3(AdCPBaseModel):
141
+ model_config = ConfigDict(
142
+ extra='forbid',
143
+ )
144
+ authorization_type: Annotated[
145
+ Literal['publisher_properties'],
146
+ Field(
147
+ description='Discriminator indicating authorization for properties from other publisher domains'
148
+ ),
149
+ ]
150
+ authorized_for: Annotated[
151
+ str,
152
+ Field(
153
+ description='Human-readable description of what this agent is authorized to sell',
154
+ max_length=500,
155
+ min_length=1,
156
+ ),
157
+ ]
158
+ publisher_properties: Annotated[
159
+ list[
160
+ publisher_property_selector.PublisherPropertySelector1
161
+ | publisher_property_selector.PublisherPropertySelector2
162
+ | publisher_property_selector.PublisherPropertySelector3
163
+ ],
164
+ Field(
165
+ description='Properties from other publisher domains this agent is authorized for. Each entry specifies a publisher domain and which of their properties this agent can sell',
166
+ min_length=1,
167
+ ),
168
+ ]
169
+ url: Annotated[AnyUrl, Field(description="The authorized agent's API endpoint URL")]
170
+
171
+
172
+ class AuthorizedAgents2(AdCPBaseModel):
173
+ model_config = ConfigDict(
174
+ extra='forbid',
175
+ )
176
+ authorization_type: Annotated[
177
+ Literal['inline_properties'],
178
+ Field(description='Discriminator indicating authorization by inline property definitions'),
179
+ ]
180
+ authorized_for: Annotated[
181
+ str,
182
+ Field(
183
+ description='Human-readable description of what this agent is authorized to sell',
184
+ max_length=500,
185
+ min_length=1,
186
+ ),
187
+ ]
188
+ properties: Annotated[
189
+ list[property.Property],
190
+ Field(
191
+ description='Specific properties this agent is authorized for (alternative to property_ids/property_tags)',
192
+ min_length=1,
193
+ ),
194
+ ]
195
+ url: Annotated[AnyUrl, Field(description="The authorized agent's API endpoint URL")]
196
+
197
+
198
+ class AuthorizedSalesAgents2(AdCPBaseModel):
199
+ model_config = ConfigDict(
200
+ extra='forbid',
201
+ )
202
+ field_schema: Annotated[
203
+ str | None,
204
+ Field(alias='$schema', description='JSON Schema identifier for this adagents.json file'),
205
+ ] = 'https://adcontextprotocol.org/schemas/adagents.json'
206
+ authorized_agents: Annotated[
207
+ list[AuthorizedAgents | AuthorizedAgents1 | AuthorizedAgents2 | AuthorizedAgents3],
208
+ Field(
209
+ description='Array of sales agents authorized to sell inventory for properties in this file',
210
+ min_length=1,
211
+ ),
212
+ ]
213
+ contact: Annotated[
214
+ Contact | None,
215
+ Field(
216
+ description='Contact information for the entity managing this adagents.json file (may be publisher or third-party operator)'
217
+ ),
218
+ ] = None
219
+ last_updated: Annotated[
220
+ AwareDatetime | None,
221
+ Field(description='ISO 8601 timestamp indicating when this file was last updated'),
222
+ ] = None
223
+ properties: Annotated[
224
+ list[property.Property] | None,
225
+ Field(
226
+ description='Array of all properties covered by this adagents.json file. Defines the canonical property list that authorized agents reference.',
227
+ min_length=1,
228
+ ),
229
+ ] = None
230
+ tags: Annotated[
231
+ dict[str, Tags] | None,
232
+ Field(
233
+ description='Metadata for each tag referenced by properties. Provides human-readable context for property tag values.'
234
+ ),
235
+ ] = None
236
+
237
+
238
+ class AuthorizedSalesAgents(RootModel[AuthorizedSalesAgents1 | AuthorizedSalesAgents2]):
239
+ root: Annotated[
240
+ AuthorizedSalesAgents1 | AuthorizedSalesAgents2,
241
+ Field(
242
+ description='Declaration of authorized sales agents for advertising inventory. Hosted at /.well-known/adagents.json on publisher domains. Can either contain the full structure inline or reference an authoritative URL.',
243
+ examples=[
244
+ {
245
+ '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
246
+ 'authoritative_location': 'https://cdn.example.com/adagents/v2/adagents.json',
247
+ 'last_updated': '2025-01-15T10:00:00Z',
248
+ },
249
+ {
250
+ '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
251
+ 'authorized_agents': [
252
+ {
253
+ 'authorization_type': 'property_tags',
254
+ 'authorized_for': 'Official sales agent',
255
+ 'property_tags': ['all'],
256
+ 'url': 'https://agent.example.com',
257
+ }
258
+ ],
259
+ 'last_updated': '2025-01-10T12:00:00Z',
260
+ 'properties': [
261
+ {
262
+ 'identifiers': [{'type': 'domain', 'value': 'example.com'}],
263
+ 'name': 'Example Site',
264
+ 'property_type': 'website',
265
+ 'publisher_domain': 'example.com',
266
+ }
267
+ ],
268
+ 'tags': {
269
+ 'all': {
270
+ 'description': 'All properties in this file',
271
+ 'name': 'All Properties',
272
+ }
273
+ },
274
+ },
275
+ {
276
+ '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
277
+ 'authorized_agents': [
278
+ {
279
+ 'authorization_type': 'property_tags',
280
+ 'authorized_for': 'All Meta properties',
281
+ 'property_tags': ['meta_network'],
282
+ 'url': 'https://meta-ads.com',
283
+ }
284
+ ],
285
+ 'contact': {
286
+ 'domain': 'meta.com',
287
+ 'email': 'adops@meta.com',
288
+ 'name': 'Meta Advertising Operations',
289
+ 'seller_id': 'pub-meta-12345',
290
+ 'tag_id': '12345',
291
+ },
292
+ 'last_updated': '2025-01-10T15:30:00Z',
293
+ 'properties': [
294
+ {
295
+ 'identifiers': [
296
+ {'type': 'ios_bundle', 'value': 'com.burbn.instagram'},
297
+ {'type': 'android_package', 'value': 'com.instagram.android'},
298
+ ],
299
+ 'name': 'Instagram',
300
+ 'property_type': 'mobile_app',
301
+ 'publisher_domain': 'instagram.com',
302
+ 'tags': ['meta_network', 'social_media'],
303
+ },
304
+ {
305
+ 'identifiers': [
306
+ {'type': 'ios_bundle', 'value': 'com.facebook.Facebook'},
307
+ {'type': 'android_package', 'value': 'com.facebook.katana'},
308
+ ],
309
+ 'name': 'Facebook',
310
+ 'property_type': 'mobile_app',
311
+ 'publisher_domain': 'facebook.com',
312
+ 'tags': ['meta_network', 'social_media'],
313
+ },
314
+ {
315
+ 'identifiers': [
316
+ {'type': 'ios_bundle', 'value': 'net.whatsapp.WhatsApp'},
317
+ {'type': 'android_package', 'value': 'com.whatsapp'},
318
+ ],
319
+ 'name': 'WhatsApp',
320
+ 'property_type': 'mobile_app',
321
+ 'publisher_domain': 'whatsapp.com',
322
+ 'tags': ['meta_network', 'messaging'],
323
+ },
324
+ ],
325
+ 'tags': {
326
+ 'messaging': {
327
+ 'description': 'Messaging and communication apps',
328
+ 'name': 'Messaging Apps',
329
+ },
330
+ 'meta_network': {
331
+ 'description': 'All Meta-owned properties',
332
+ 'name': 'Meta Network',
333
+ },
334
+ 'social_media': {
335
+ 'description': 'Social networking applications',
336
+ 'name': 'Social Media Apps',
337
+ },
338
+ },
339
+ },
340
+ {
341
+ '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
342
+ 'authorized_agents': [
343
+ {
344
+ 'authorization_type': 'property_tags',
345
+ 'authorized_for': 'Tumblr corporate properties only',
346
+ 'property_tags': ['corporate'],
347
+ 'url': 'https://tumblr-sales.com',
348
+ }
349
+ ],
350
+ 'contact': {'name': 'Tumblr Advertising'},
351
+ 'last_updated': '2025-01-10T16:00:00Z',
352
+ 'properties': [
353
+ {
354
+ 'identifiers': [{'type': 'domain', 'value': 'tumblr.com'}],
355
+ 'name': 'Tumblr Corporate',
356
+ 'property_type': 'website',
357
+ 'publisher_domain': 'tumblr.com',
358
+ 'tags': ['corporate'],
359
+ }
360
+ ],
361
+ 'tags': {
362
+ 'corporate': {
363
+ 'description': 'Tumblr-owned corporate properties (not user blogs)',
364
+ 'name': 'Corporate Properties',
365
+ }
366
+ },
367
+ },
368
+ {
369
+ '$schema': 'https://adcontextprotocol.org/schemas/adagents.json',
370
+ 'authorized_agents': [
371
+ {
372
+ 'authorization_type': 'publisher_properties',
373
+ 'authorized_for': 'CNN CTV properties via publisher authorization',
374
+ 'publisher_properties': [
375
+ {
376
+ 'property_ids': ['cnn_ctv_app'],
377
+ 'publisher_domain': 'cnn.com',
378
+ 'selection_type': 'by_id',
379
+ }
380
+ ],
381
+ 'url': 'https://agent.example/api',
382
+ },
383
+ {
384
+ 'authorization_type': 'publisher_properties',
385
+ 'authorized_for': 'All CTV properties from multiple publishers',
386
+ 'publisher_properties': [
387
+ {
388
+ 'property_tags': ['ctv'],
389
+ 'publisher_domain': 'cnn.com',
390
+ 'selection_type': 'by_tag',
391
+ },
392
+ {
393
+ 'property_tags': ['ctv'],
394
+ 'publisher_domain': 'espn.com',
395
+ 'selection_type': 'by_tag',
396
+ },
397
+ ],
398
+ 'url': 'https://agent.example/api',
399
+ },
400
+ ],
401
+ 'contact': {
402
+ 'domain': 'agent.example',
403
+ 'email': 'sales@agent.example',
404
+ 'name': 'Example Third-Party Sales Agent',
405
+ },
406
+ 'last_updated': '2025-01-10T17:00:00Z',
407
+ },
408
+ ],
409
+ title='Authorized Sales Agents',
410
+ ),
411
+ ]
@@ -0,0 +1,3 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: .schema_temp
3
+ # timestamp: 2025-11-22T15:23:24+00:00
@@ -0,0 +1,30 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: core/activation_key.json
3
+ # timestamp: 2025-11-22T15:23:24+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Annotated, Literal
8
+
9
+ from adcp.types.base import AdCPBaseModel
10
+ from pydantic import ConfigDict, Field
11
+
12
+
13
+ class ActivationKey1(AdCPBaseModel):
14
+ model_config = ConfigDict(
15
+ extra='forbid',
16
+ )
17
+ segment_id: Annotated[
18
+ str,
19
+ Field(description='The platform-specific segment identifier to use in campaign targeting'),
20
+ ]
21
+ type: Annotated[Literal['segment_id'], Field(description='Segment ID based targeting')]
22
+
23
+
24
+ class ActivationKey2(AdCPBaseModel):
25
+ model_config = ConfigDict(
26
+ extra='forbid',
27
+ )
28
+ key: Annotated[str, Field(description='The targeting parameter key')]
29
+ type: Annotated[Literal['key_value'], Field(description='Key-value pair based targeting')]
30
+ value: Annotated[str, Field(description='The targeting parameter value')]
@@ -0,0 +1,3 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: .schema_temp
3
+ # timestamp: 2025-11-22T15:23:24+00:00
@@ -0,0 +1,26 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: core/assets/audio_asset.json
3
+ # timestamp: 2025-11-22T15:23:24+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Annotated
8
+
9
+ from adcp.types.base import AdCPBaseModel
10
+ from pydantic import AnyUrl, ConfigDict, Field
11
+
12
+
13
+ class AudioAsset(AdCPBaseModel):
14
+ model_config = ConfigDict(
15
+ extra='forbid',
16
+ )
17
+ bitrate_kbps: Annotated[
18
+ int | None, Field(description='Audio bitrate in kilobits per second', ge=1)
19
+ ] = None
20
+ duration_ms: Annotated[
21
+ int | None, Field(description='Audio duration in milliseconds', ge=0)
22
+ ] = None
23
+ format: Annotated[str | None, Field(description='Audio file format (mp3, wav, aac, etc.)')] = (
24
+ None
25
+ )
26
+ url: Annotated[AnyUrl, Field(description='URL to the audio asset')]