Skip to content

Commit b23a03d

Browse files
bokelleyclaude
andcommitted
feat!: Release v2.0.0 with breaking changes
BREAKING CHANGES: 1. Dictionary access no longer supported - all generated types are now proper Pydantic models - OLD: format.assets_required[0]["asset_id"] - NEW: format.assets_required[0].asset_id 2. Removed 23 backward compatibility type aliases - Use numbered discriminated union variants (e.g., Destination1, Destination2) - Or import union types (e.g., Destination) 3. Simplified main module exports - Import types from adcp.types.generated directly Features: - Add runtime validation for mutual exclusivity constraints not enforced by upstream schemas - Add SCHEMA_VALIDATION_GAPS.md documenting upstream schema issues - Add validation utilities: validate_adagents(), validate_product(), validate_agent_authorization() - Consolidate exports script now generates 288 unique exports (down from 313) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent a528682 commit b23a03d

20 files changed

+487
-278
lines changed

CHANGELOG.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,112 @@
11
# Changelog
22

3+
## [2.0.0] - 2025-11-15
4+
5+
### ⚠️ BREAKING CHANGES
6+
7+
#### Migration to datamodel-code-generator
8+
9+
This release completes the migration from custom type generation to the industry-standard `datamodel-code-generator` tool. This brings improved type safety, better Pydantic v2 support, and alignment with JSON Schema specifications.
10+
11+
**Breaking Changes:**
12+
13+
1. **Dictionary access no longer supported** - All generated types are now proper Pydantic models, not TypedDicts
14+
-**OLD**: `format.assets_required[0]["asset_id"]`
15+
-**NEW**: `format.assets_required[0].asset_id`
16+
- This affects ALL nested objects throughout the SDK (assets, deployments, destinations, etc.)
17+
18+
2. **Backward compatibility aliases removed** - The 23 type aliases introduced in v1.6.x have been removed
19+
- Use numbered discriminated union variants directly (e.g., `Destination1`, `Destination2` instead of `PlatformDestination`, `AgentDestination`)
20+
- Or import from discriminated union types (e.g., `Destination`)
21+
- See migration guide below for full list
22+
23+
3. **Simplified main module exports** - The main `adcp` module no longer re-exports all generated types
24+
-**NEW**: Import from `adcp.types.generated` directly: `from adcp.types.generated import Product, Format, etc.`
25+
- Or access via module: `from adcp.types import generated; generated.Product`
26+
27+
**Removed Type Aliases:**
28+
29+
- `ActivateSignalSuccess` / `ActivateSignalError` → Use `ActivateSignalResponse1` / `ActivateSignalResponse2`
30+
- `CreateMediaBuySuccess` / `CreateMediaBuyError` → Use `CreateMediaBuyResponse1` / `CreateMediaBuyResponse2`
31+
- `UpdateMediaBuySuccess` / `UpdateMediaBuyError` → Use `UpdateMediaBuyResponse1` / `UpdateMediaBuyResponse2`
32+
- `SyncCreativesSuccess` / `SyncCreativesError` → Use `SyncCreativesResponse1` / `SyncCreativesResponse2`
33+
- `PlatformDestination` / `AgentDestination` → Use `Destination1` / `Destination2`
34+
- `PlatformDeployment` / `AgentDeployment` → Use `Deployment1` / `Deployment2`
35+
- `Segment_idActivationKey` / `Key_valueActivationKey` → Use `ActivationKey1` / `ActivationKey2`
36+
- `UrlVastAsset` / `InlineVastAsset` → Use `VastAsset1` / `VastAsset2`
37+
- `UrlDaastAsset` / `InlineDaastAsset` → Use `DaastAsset1` / `DaastAsset2`
38+
- `MediaSubAsset` / `TextSubAsset` → Use `SubAsset1` / `SubAsset2`
39+
- `UrlPreviewRender` / `HtmlPreviewRender` / `BothPreviewRender` → Use `PreviewRender1` / `PreviewRender2` / `PreviewRender3`
40+
- `ListCreativeFormatsRequest` / `ListCreativeFormatsResponse` → Use `ListCreativeFormatsRequestCreativeAgent` / `ListCreativeFormatsResponseCreativeAgent`
41+
42+
### Features
43+
44+
* **Runtime Validation** - Added validation utilities for constraints not enforced by upstream JSON schemas:
45+
- `validate_adagents()` - Validates mutual exclusivity in adagents.json authorization fields
46+
- `validate_product()` - Validates publisher_properties mutual exclusivity
47+
- `validate_agent_authorization()` - Validates agent authorization field constraints
48+
- `validate_publisher_properties_item()` - Validates property_ids/property_tags mutual exclusivity
49+
- These validators are automatically applied by `fetch_adagents()` but can also be used standalone
50+
51+
* **Schema Validation Gap Documentation** - Added `SCHEMA_VALIDATION_GAPS.md` documenting upstream schema issues where mutual exclusivity is documented but not enforced
52+
53+
### Migration Guide
54+
55+
**Update dictionary access to attribute access:**
56+
57+
```python
58+
# Before (v1.x)
59+
asset_id = format.assets_required[0]["asset_id"]
60+
deployment = signal["deployments"][0]["platform"]
61+
62+
# After (v2.0)
63+
asset_id = format.assets_required[0].asset_id
64+
deployment = signal.deployments[0].platform
65+
```
66+
67+
**Update type imports:**
68+
69+
```python
70+
# Before (v1.x)
71+
from adcp import (
72+
ActivateSignalSuccess,
73+
ActivateSignalError,
74+
PlatformDestination,
75+
AgentDestination,
76+
)
77+
78+
# After (v2.0) - Option 1: Use numbered variants
79+
from adcp.types.generated import (
80+
ActivateSignalResponse1, # Success
81+
ActivateSignalResponse2, # Error
82+
Destination1, # Platform
83+
Destination2, # Agent
84+
)
85+
86+
# After (v2.0) - Option 2: Use union types
87+
from adcp.types.generated import (
88+
ActivateSignalResponse, # Union of Response1 | Response2
89+
Destination, # Union of Destination1 | Destination2
90+
)
91+
```
92+
93+
**Check type discriminators in conditional logic:**
94+
95+
```python
96+
# Before (v1.x) - used isinstance with aliases
97+
if isinstance(destination, PlatformDestination):
98+
print(f"Platform: {destination.platform}")
99+
100+
# After (v2.0) - check discriminator field
101+
if destination.root.type == "platform":
102+
print(f"Platform: {destination.root.platform}")
103+
104+
# Or unwrap the RootModel
105+
dest = destination.root
106+
if dest.type == "platform":
107+
print(f"Platform: {dest.platform}")
108+
```
109+
3110
## [1.6.1](https://github.com/adcontextprotocol/adcp-client-python/compare/v1.6.0...v1.6.1) (2025-11-13)
4111

5112

SCHEMA_VALIDATION_GAPS.md

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# Schema Validation Gaps Report
2+
3+
## Summary
4+
5+
Multiple AdCP JSON schemas document "mutually exclusive" field constraints in descriptions but **do not enforce them** through JSON Schema validation. This allows invalid data to pass schema validation.
6+
7+
## Impact
8+
9+
- **Validation fails silently** - invalid data passes schema checks
10+
- **Runtime errors downstream** - applications must handle ambiguous data
11+
- **Inconsistent implementations** - different clients may interpret differently
12+
- **Poor DX** - validation errors caught late in development
13+
14+
## Issues Found
15+
16+
### 1. Product Schema: `publisher_properties` Items
17+
18+
**File**: `/schemas/v1/core/product.json` (lines 126-162)
19+
20+
**Problem**: The `property_ids` and `property_tags` fields are documented as mutually exclusive but both can be provided (or neither).
21+
22+
**Current Schema** (lines 130-158):
23+
```json
24+
{
25+
"publisher_properties": {
26+
"items": {
27+
"properties": {
28+
"property_ids": {
29+
"description": "Specific property IDs from the publisher's adagents.json. Mutually exclusive with property_tags.",
30+
...
31+
},
32+
"property_tags": {
33+
"description": "Property tags from the publisher's adagents.json. Product covers all properties with these tags. Mutually exclusive with property_ids.",
34+
...
35+
},
36+
"publisher_domain": { ... }
37+
},
38+
"required": ["publisher_domain"],
39+
"type": "object"
40+
}
41+
}
42+
}
43+
```
44+
45+
**Required Fix**:
46+
```json
47+
{
48+
"publisher_properties": {
49+
"items": {
50+
"properties": { ... },
51+
"required": ["publisher_domain"],
52+
"oneOf": [
53+
{
54+
"required": ["property_ids"],
55+
"not": { "required": ["property_tags"] }
56+
},
57+
{
58+
"required": ["property_tags"],
59+
"not": { "required": ["property_ids"] }
60+
}
61+
],
62+
"additionalProperties": false,
63+
"type": "object"
64+
}
65+
}
66+
}
67+
```
68+
69+
### 2. AdAgents Schema: Agent Authorization Fields
70+
71+
**File**: `/schemas/v1/adagents.json` (lines 200-279)
72+
73+
**Problem**: The agent authorization has **four** mutually exclusive fields (`properties`, `property_ids`, `property_tags`, `publisher_properties`) but no validation enforcing this.
74+
75+
**Current Schema** (lines 207-269):
76+
```json
77+
{
78+
"properties": {
79+
"properties": {
80+
"description": "Specific properties this agent is authorized for (alternative to property_ids/property_tags). Mutually exclusive with property_ids and property_tags fields.",
81+
...
82+
},
83+
"property_ids": {
84+
"description": "Property IDs this agent is authorized for. Resolved against the top-level properties array in this file. Mutually exclusive with property_tags and properties fields.",
85+
...
86+
},
87+
"property_tags": {
88+
"description": "Tags identifying which properties this agent is authorized for. Resolved against the top-level properties array in this file using tag matching. Mutually exclusive with property_ids and properties fields.",
89+
...
90+
},
91+
"publisher_properties": {
92+
"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 (by property_id or property_tags). Mutually exclusive with property_ids, property_tags, and properties fields.",
93+
...
94+
}
95+
},
96+
"required": ["url", "authorized_for"]
97+
}
98+
```
99+
100+
**Required Fix**:
101+
```json
102+
{
103+
"properties": { ... },
104+
"required": ["url", "authorized_for"],
105+
"oneOf": [
106+
{
107+
"required": ["properties"],
108+
"not": {
109+
"anyOf": [
110+
{ "required": ["property_ids"] },
111+
{ "required": ["property_tags"] },
112+
{ "required": ["publisher_properties"] }
113+
]
114+
}
115+
},
116+
{
117+
"required": ["property_ids"],
118+
"not": {
119+
"anyOf": [
120+
{ "required": ["properties"] },
121+
{ "required": ["property_tags"] },
122+
{ "required": ["publisher_properties"] }
123+
]
124+
}
125+
},
126+
{
127+
"required": ["property_tags"],
128+
"not": {
129+
"anyOf": [
130+
{ "required": ["properties"] },
131+
{ "required": ["property_ids"] },
132+
{ "required": ["publisher_properties"] }
133+
]
134+
}
135+
},
136+
{
137+
"required": ["publisher_properties"],
138+
"not": {
139+
"anyOf": [
140+
{ "required": ["properties"] },
141+
{ "required": ["property_ids"] },
142+
{ "required": ["property_tags"] }
143+
]
144+
}
145+
}
146+
]
147+
}
148+
```
149+
150+
### 3. AdAgents Schema: `publisher_properties` Items
151+
152+
**File**: `/schemas/v1/adagents.json` (lines 233-269)
153+
154+
**Problem**: Within `publisher_properties` array items, `property_ids` and `property_tags` are mutually exclusive but not validated (same as Product schema issue #1).
155+
156+
**Current Schema** (lines 237-265):
157+
```json
158+
{
159+
"publisher_properties": {
160+
"items": {
161+
"properties": {
162+
"property_ids": {
163+
"description": "Specific property IDs from the publisher's adagents.json properties array. Mutually exclusive with property_tags.",
164+
...
165+
},
166+
"property_tags": {
167+
"description": "Property tags from the publisher's adagents.json tags. Agent is authorized for all properties with these tags. Mutually exclusive with property_ids.",
168+
...
169+
},
170+
"publisher_domain": { ... }
171+
},
172+
"required": ["publisher_domain"],
173+
"additionalProperties": false,
174+
"type": "object"
175+
}
176+
}
177+
}
178+
```
179+
180+
**Required Fix**: Same as Product schema fix (add `oneOf` constraint).
181+
182+
## Recommended Actions
183+
184+
1. **Report to upstream** - File issue with adcontextprotocol/adcp repository
185+
2. **Add runtime validation** - Python SDK should validate these constraints even if schemas don't
186+
3. **Document workaround** - Add validation in SDK with clear error messages
187+
4. **Track schema version** - When upstream fixes schemas, update SDK to rely on schema validation
188+
189+
## Example Invalid Data That Currently Passes Validation
190+
191+
### Product with both `property_ids` and `property_tags`:
192+
```json
193+
{
194+
"product_id": "test",
195+
"publisher_properties": [
196+
{
197+
"publisher_domain": "example.com",
198+
"property_ids": ["site1", "site2"],
199+
"property_tags": ["news", "sports"] // Should be rejected!
200+
}
201+
]
202+
}
203+
```
204+
205+
### Product with neither `property_ids` nor `property_tags`:
206+
```json
207+
{
208+
"product_id": "test",
209+
"publisher_properties": [
210+
{
211+
"publisher_domain": "example.com" // Should require one!
212+
}
213+
]
214+
}
215+
```
216+
217+
### Agent with multiple authorization methods:
218+
```json
219+
{
220+
"url": "https://agent.example.com",
221+
"authorized_for": "All properties",
222+
"property_ids": ["site1"],
223+
"property_tags": ["news"], // Should be rejected!
224+
"properties": [{ ... }] // Should be rejected!
225+
}
226+
```
227+
228+
## Testing
229+
230+
Run schema validation against these invalid examples to confirm they incorrectly pass.
231+
232+
## References
233+
234+
- Product schema: `https://adcontextprotocol.org/schemas/v1/core/product.json`
235+
- AdAgents schema: `https://adcontextprotocol.org/schemas/v1/adagents.json`
236+
- JSON Schema `oneOf`: https://json-schema.org/understanding-json-schema/reference/combining#oneOf

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "adcp"
7-
version = "1.6.1"
7+
version = "2.0.0"
88
description = "Official Python client for the Ad Context Protocol (AdCP)"
99
authors = [
1010
{name = "AdCP Community", email = "maintainers@adcontextprotocol.org"}

0 commit comments

Comments
 (0)