Breaking Changes: Simplified API with unified client architecture and atomic response types
The client API has been unified under a single name:
- Primary export:
AdCPClient(formerlyADCPMultiAgentClient) - Deprecated:
ADCPMultiAgentClientstill works but will be removed in v4.0 - Removed: Internal class names are no longer exposed
Task responses now use discriminated unions with atomic semantics (success XOR errors).
What this means: Responses can contain EITHER success data OR errors, never both.
Before (v2.x):
interface CreateMediaBuyResponse {
media_buy_id?: string; // Optional
packages?: Package[]; // Optional
errors?: Error[]; // Optional
}
// Code could check both simultaneously
if (response.media_buy_id) {
// Success
} else if (response.errors) {
// Error
}After (v3.0):
type CreateMediaBuyResponse =
| { media_buy_id: string; packages: Package[] } // Success branch (required fields)
| { errors: [Error, ...Error[]] }; // Error branch (non-empty array)
// Use type guards to determine which branch
if ('media_buy_id' in response) {
// Success branch - media_buy_id is guaranteed to exist
console.log(response.media_buy_id);
} else {
// Error branch - errors is guaranteed to exist
console.error(response.errors);
}Migration: Replace property checks (if (response.property)) with type guards (if ('property' in response)).
Before (v2.x):
import { ADCPMultiAgentClient } from '@adcp/sdk';
const client = new ADCPMultiAgentClient([
{ id: 'agent-1', agent_uri: 'https://agent.com', protocol: 'a2a' }
]);
const agent = client.agent('agent-1');
const result = await agent.getProducts({ brief: '...' });After (v3.0):
import { AdCPClient } from '@adcp/sdk'; // ← Changed import name
const client = new AdCPClient([
{ id: 'agent-1', agent_uri: 'https://agent.com', protocol: 'a2a' }
]);
const agent = client.agent('agent-1');
const result = await agent.getProducts({ brief: '...' });Change: Import and use AdCPClient instead of ADCPMultiAgentClient.
Before (v2.x):
import { createAdCPClient, createAdCPClientFromEnv } from '@adcp/sdk';
// Option 1
const client = createAdCPClient([agentConfig]);
// Option 2
const client = createAdCPClientFromEnv();After (v3.0):
import { AdCPClient } from '@adcp/sdk';
// Option 1: Use constructor
const client = new AdCPClient([agentConfig]);
// Option 2: Use static factory method
const client = AdCPClient.fromEnv();Change: Use constructor or static factory methods instead of standalone functions.
Before (v2.x):
const result = await agent.createMediaBuy(params);
// Check optional properties
if (result.data.media_buy_id) {
console.log('Created:', result.data.media_buy_id);
}
if (result.data.errors && result.data.errors.length > 0) {
console.error('Errors:', result.data.errors);
}After (v3.0):
const result = await agent.createMediaBuy(params);
// Use type guards to check which branch
if ('media_buy_id' in result.data) {
// Success branch - media_buy_id is guaranteed present
console.log('Created:', result.data.media_buy_id);
console.log('Packages:', result.data.packages); // Also guaranteed present
} else {
// Error branch - errors is guaranteed present
console.error('Errors:', result.data.errors);
}Change: Replace property existence checks with 'property' in response type guards.
Before (v2.x):
const agents = client.getAgents(); // Returns AgentConfig[]After (v3.0):
const agents = client.getAgentConfigs(); // Returns AgentConfig[]Change: getAgents() → getAgentConfigs()
- Replace
ADCPMultiAgentClientwithAdCPClientin imports - Replace
createAdCPClient()withnew AdCPClient() - Replace
createAdCPClientFromEnv()withAdCPClient.fromEnv() - Replace
client.getAgents()withclient.getAgentConfigs()
- Replace
if (response.media_buy_id)withif ('media_buy_id' in response) - Replace
if (response.errors)withif ('errors' in response)or use else branch - Remove code that checks for both success and error fields simultaneously
- Update TypeScript types to handle discriminated union branches
Affected methods: All task methods return discriminated union types:
getProducts()→GetProductsResponselistCreativeFormats()→ListCreativeFormatsResponsecreateMediaBuy()→CreateMediaBuyResponseupdateMediaBuy()→UpdateMediaBuyResponsesyncCreatives()→SyncCreativesResponselistCreatives()→ListCreativesResponsegetMediaBuyDelivery()→GetMediaBuyDeliveryResponselistAuthorizedProperties()→ListAuthorizedPropertiesResponseprovidePerformanceFeedback()→ProvidePerformanceFeedbackResponsegetSignals()→GetSignalsResponseactivateSignal()→ActivateSignalResponse
AdCPClient is shorter and clearer than ADCPMultiAgentClient. It handles both single-agent and multi-agent use cases seamlessly.
Discriminated unions enforce that responses contain EITHER success data OR errors, never both. This matches the AdCP v2.2 schema specification and prevents ambiguous states.
TypeScript can now properly narrow types based on which branch you're in, eliminating the need for optional property checks and reducing runtime errors.
You can migrate gradually during the v3.x series:
Step 1: Update response handling to use type guards (required immediately)
// Change from:
if (response.media_buy_id) { ... }
// To:
if ('media_buy_id' in response) { ... }Step 2: Update import names (recommended, but ADCPMultiAgentClient still works)
// Change from:
import { ADCPMultiAgentClient } from '@adcp/sdk';
// To:
import { AdCPClient } from '@adcp/sdk';In v4.0: The ADCPMultiAgentClient alias will be removed. You must use AdCPClient.
If you encounter issues during migration:
- Check the API documentation
- Review the webhook examples for updated patterns
- Open an issue at https://github.com/adcontextprotocol/adcp-client/issues