diff --git a/docs/media-buy/media-buys/index.md b/docs/media-buy/media-buys/index.md index 6611031cc7..336c9ababe 100644 --- a/docs/media-buy/media-buys/index.md +++ b/docs/media-buy/media-buys/index.md @@ -159,22 +159,11 @@ Media buy operations are asynchronous with varying timing: - **Webhook Integration**: Implement webhooks for real-time updates - **User Communication**: Clearly communicate pending states to end users -## Error Handling Philosophy +## Error Handling -### Pending States vs Errors +For comprehensive error handling guidance including pending vs error states, response patterns, and recovery strategies, see [Protocol Error Handling](../../protocols/error-handling.md). -**Pending States (Normal Flow):** -- `pending_manual`: Operation requires human approval -- `pending_permission`: Operation blocked by permissions -- `pending_approval`: Awaiting ad server approval - -These are NOT errors and should be handled as part of normal operation flow. - -**Error States (Exceptional):** -- `failed`: Operation cannot be completed -- Authentication failures -- Invalid parameters -- Resource not found +Media buy specific error codes are documented in each task specification and the [Error Codes Reference](../../reference/error-codes.md). ## Asynchronous Operations and Human-in-the-Loop diff --git a/docs/protocols/context-management.md b/docs/protocols/context-management.md index 56e796fe22..d1dc1398a0 100644 --- a/docs/protocols/context-management.md +++ b/docs/protocols/context-management.md @@ -7,7 +7,51 @@ title: Context Management How AdCP handles conversation state differs significantly between protocols. -## Key Difference +## Key Identifiers + +AdCP uses two distinct identifiers for different purposes: + +### context_id vs task_id + +**context_id**: +- Comes from the protocol layer (built into A2A) +- Provides conversation history and session continuity +- Lives at the protocol level +- Used for maintaining state across multiple task calls in a conversation +- Expires after conversation timeout (typically 1 hour) + +**task_id**: +- Specific to individual requests that could be asynchronous +- Lives beyond the conversation +- Used for tracking operation progress over time +- Persists until the task completes (may be days for complex media buys) +- Can be referenced across different conversations or sessions + +### Usage Example + +```javascript +// First call - establishes context and creates task +const result = await call('create_media_buy', { + brief: "Launch summer campaign" +}); + +const contextId = result.context_id; // For conversation continuity +const taskId = result.task_id; // For tracking this specific media buy + +// Later in same conversation - uses context_id +const update1 = await call('update_media_buy', { + context_id: contextId, // Maintains conversation state + task_id: taskId, // References the specific media buy + updates: {...} +}); + +// Days later in new conversation - only task_id needed +const status = await call('get_media_buy_status', { + task_id: taskId // No context_id - this is a new conversation +}); +``` + +## Protocol Differences - **A2A**: Context is handled automatically by the protocol - **MCP**: Requires manual context_id management @@ -74,12 +118,14 @@ class MCPSession { ## What Context Maintains -Regardless of protocol: -- Current media buy and products -- Search results and filters -- Conversation history -- User preferences -- Workflow state +The `context_id` maintains conversation state, regardless of protocol: +- Current media buy and products being discussed +- Search results and applied filters +- Conversation history and user intent +- User preferences expressed in the session +- Workflow state and temporary decisions + +Note: Long-term task state (like media buy status, creative assets, performance data) is tracked via `task_id`, not `context_id`. ## Best Practices diff --git a/docs/protocols/error-handling.md b/docs/protocols/error-handling.md new file mode 100644 index 0000000000..26777a81ce --- /dev/null +++ b/docs/protocols/error-handling.md @@ -0,0 +1,291 @@ +--- +sidebar_position: 4 +title: Error Handling +--- + +# Error Handling Across Protocols + +This document outlines AdCP's approach to error handling across MCP and A2A protocols, providing consistent patterns for both fatal errors and non-fatal warnings. + +## Philosophy + +### Pending States vs Errors + +**Pending States (Normal Flow):** +- `pending`: Request received and queued +- `processing`: Operation in progress +- `pending_manual`: Operation requires human approval +- `pending_permission`: Operation blocked by permissions +- `pending_approval`: Awaiting ad server approval + +These are NOT errors and should be handled as part of normal operation flow. + +**Error States (Exceptional):** +- Task-level failures that prevent completion +- Authentication failures +- Invalid parameters +- Resource not found +- Authorization denied + +### Error Response Patterns + +AdCP uses a two-tier error handling approach: + +1. **Task-Level Errors (Non-Fatal)**: Warnings and issues that don't prevent operation completion +2. **Protocol-Level Errors (Fatal)**: Operations that cannot be completed + +## Task-Level Error Handling + +### Non-Fatal Errors vs Warnings + +AdCP distinguishes between two types of task-level issues: + +**Non-Fatal Errors**: Actual failures that prevented part of the request from being fulfilled. These go in the `errors` array: + +```json +{ + "message": "Signal discovery completed with partial results", + "adcp_version": "1.0.0", + "context_id": "ctx-123", + "signals": [/* available signals */], + "errors": [ + { + "code": "NO_DATA_IN_REGION", + "message": "No signal data available for requested region: Australia", + "field": "deliver_to.countries[1]", + "suggestion": "Remove Australia from target countries or contact data provider for coverage expansion", + "details": { + "requested_country": "AU", + "available_countries": ["US", "CA", "GB", "DE"], + "data_provider": "Peer39" + } + } + ] +} +``` + +**Warnings**: Advisory information about configuration, timing, or data quality. These are communicated only in the `message` field: + +```json +{ + "message": "Signal activation successful. Note that your account frequency cap settings are set conservatively and may limit reach. Contact your account manager to review frequency cap settings for optimal performance.", + "adcp_version": "1.0.0", + "context_id": "ctx-456", + "task_id": "activation_789", + "status": "deployed", + "decisioning_platform_segment_id": "pm_brand456_peer39_lux_auto", + "deployed_at": "2025-01-15T14:30:00Z" +} +``` + +### Partial Success Support + +Tasks can succeed while returning non-fatal errors in the `errors` array or warnings in the `message` field: + +**Non-Fatal Errors (partial failures in `errors` array):** +- **Discovery tasks**: Can find some results while reporting regions/platforms with no data +- **Activation tasks**: Can activate on some platforms while failing on others +- **Creation tasks**: Can create some packages while others fail validation + +**Warnings (advisory information in `message` field):** +- **Discovery tasks**: Can find results while noting data freshness or coverage limitations in the message +- **Activation tasks**: Can activate successfully while noting suboptimal configuration in the message +- **Creation tasks**: Can create resources while suggesting optimization opportunities in the message + +### Error Object Structure + +The `errors` array contains error objects for actual failures: + +```json +{ + "code": "ERROR_CODE", // Required: Standardized error code + "message": "Description", // Required: Human-readable message + "field": "field.path", // Optional: Which field has the issue + "suggestion": "Try this", // Optional: Actionable remediation steps + "details": { // Optional: Additional context + "affected_items": ["id1"], + "retry_after": 1800 + } +} +``` + +### Warning Communication + +Warnings and advisory information should be communicated in the human-readable `message` field rather than cluttering the `errors` array. This keeps the `errors` array focused on actionable failures that require programmatic handling. + +## Protocol-Level Error Handling + +### MCP (Model Context Protocol) + +For fatal errors that prevent task completion, MCP uses the `isError: true` pattern: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "content": [ + { + "type": "text", + "text": "Failed to activate signal: Account 'brand-456-pm' not authorized for Peer39 data on PubMatic. Contact your PubMatic account manager to enable access." + } + ], + "isError": true + } +} +``` + +**When to use MCP fatal errors:** +- Authentication failures +- Invalid tool parameters +- Resource not found +- Authorization denied +- System errors + +### A2A (Agent-to-Agent Protocol) + +For fatal errors, A2A uses the `status: "failed"` pattern: + +```json +{ + "taskId": "task_123", + "status": "failed", + "message": { + "parts": [{ + "kind": "text", + "text": "Unable to complete signal activation: Invalid signal agent segment ID 'seg_invalid_123'. Use get_signals to find current segment IDs." + }] + } +} +``` + +**When to use A2A fatal errors:** +- Skill execution failures +- Invalid request parameters +- External service unavailable +- Authentication issues + +## Error Recovery Strategies + +### Retry Logic + +- **Check `retry_after` field** for appropriate retry timing +- **Implement exponential backoff** for rate limiting and service issues +- **Categorize errors** by retry-ability (permanent vs temporary) + +```typescript +const RETRYABLE_ERRORS = [ + 'RATE_LIMIT_EXCEEDED', + 'TIMEOUT', + 'SERVICE_UNAVAILABLE', + 'ACTIVATION_FAILED' // May succeed on retry +]; + +const PERMANENT_ERRORS = [ + 'INVALID_CREDENTIALS', + 'INSUFFICIENT_PERMISSIONS', + 'SIGNAL_AGENT_SEGMENT_NOT_FOUND', + 'PLATFORM_UNAUTHORIZED' +]; +``` + +### Actionable Feedback + +- **Use `suggestion` field** to guide remediation steps +- **Include relevant context** in `details` object for debugging +- **Provide specific field paths** for validation errors +- **Reference external resources** when additional action is required + +### Context Preservation + +- **Use `context_id`** for session management across related operations +- **Include `task_id`** for asynchronous operation tracking +- **Preserve request context** in error details for debugging + +## Implementation Guidelines + +### Error Message Construction + +**Good error messages:** +- Explain what went wrong and why +- Provide specific remediation steps +- Include relevant context (IDs, values, constraints) +- Use clear, non-technical language + +**Example:** +```json +{ + "code": "TARGETING_TOO_NARROW", + "message": "Package targeting yielded 0 available impressions", + "field": "packages[1].targeting_overlay", + "suggestion": "Broaden geographic targeting or remove segment exclusions", + "details": { + "requested_budget": 40000, + "available_impressions": 0, + "affected_package": "nike_audio_drive_package" + } +} +``` + +### Error Code Naming + +- Use `SCREAMING_SNAKE_CASE` format +- Be specific and descriptive +- Group by category (ACTIVATION_, TARGETING_, PRICING_, etc.) +- Avoid generic codes like `ERROR` or `FAILED` + +### Validation Error Patterns + +For field validation issues: + +```json +{ + "code": "INVALID_FIELD_VALUE", + "message": "Budget must be between $1,000 and $1,000,000", + "field": "packages[0].budget.total", + "suggestion": "Adjust budget to be within allowed range", + "details": { + "provided_value": 500, + "min_value": 1000, + "max_value": 1000000, + "currency": "USD" + } +} +``` + +## Common Error Scenarios + +### Authentication & Authorization + +- **Invalid credentials**: Protocol-level fatal error +- **Insufficient permissions**: Protocol-level fatal error +- **Account restrictions**: Task-level warning with limited functionality + +### Resource Management + +- **Resource not found**: Protocol-level fatal error +- **Resource unavailable**: Task-level warning with alternatives +- **Resource limitations**: Task-level warning with impact description + +### Data Quality Issues + +- **Invalid input data**: Protocol-level fatal error +- **Stale data**: Task-level warning communicated in message field +- **Incomplete data**: Task-level warning communicated in message field + +## Testing Error Scenarios + +When implementing AdCP tasks, test these error scenarios: + +1. **Invalid authentication** → Protocol-level fatal +2. **Missing required fields** → Protocol-level fatal +3. **Field validation failures** → Protocol-level fatal +4. **Resource limitations** → Task-level warnings in message field +5. **Partial data issues** → Task-level warnings in message field +6. **Service degradation** → Task-level warnings in message field with retry guidance + +## Reference + +- [Error Codes Reference](../reference/error-codes.md) - Complete list of standardized error codes +- [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#error-handling) - Official MCP error handling +- [A2A Protocol Guide](./a2a-guide.md) - A2A-specific error patterns \ No newline at end of file diff --git a/docs/protocols/getting-started.md b/docs/protocols/getting-started.md index fd5805659d..507ca52eed 100644 --- a/docs/protocols/getting-started.md +++ b/docs/protocols/getting-started.md @@ -131,5 +131,6 @@ The reference documentation is there when you need it, but most users only need - **For MCP questions**: See the [MCP Guide](./mcp-guide.md) - **For A2A questions**: See the [A2A Guide](./a2a-guide.md) +- **For context & task IDs**: See [Context Management](./context-management.md) to understand the difference between `context_id` and `task_id` - **For task details**: See the [Media Buy](../media-buy/index.md) or [Signals](../signals/overview.md) sections - **For deep technical details**: Check the [Reference](../reference/error-codes.md) section (only if needed) \ No newline at end of file diff --git a/docs/reference/error-codes.md b/docs/reference/error-codes.md index d45bf29b36..719ff77518 100644 --- a/docs/reference/error-codes.md +++ b/docs/reference/error-codes.md @@ -7,20 +7,66 @@ title: Error Codes This page documents all standard error codes used across ACP implementations. -## Error Response Format +## Error Response Patterns -All errors follow this consistent structure: +AdCP uses different error handling patterns depending on the severity and context: + +### Task-Level Errors (Non-Fatal Warnings) + +Use the optional `errors` array in successful responses for warnings and non-blocking issues: ```json { - "success": false, - "error": { - "code": "ERROR_CODE", - "message": "Human-readable description", - "details": { - // Additional context (optional) - }, - "retry_after": 30 // Seconds to wait before retry (optional) + "message": "Operation completed with warnings", + "adcp_version": "1.0.0", + "context_id": "ctx-123", + // ... task-specific success data ... + "errors": [ + { + "code": "ERROR_CODE", + "message": "Human-readable description", + "field": "field.path.with.issue", // Optional + "suggestion": "Recommended action", // Optional + "details": { + // Additional context (optional) + }, + "retry_after": 30 // Seconds to wait before retry (optional) + } + ] +} +``` + +### Protocol-Level Errors (Fatal) + +For operations that cannot be completed, use protocol-specific error mechanisms: + +**MCP (Model Context Protocol):** +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "content": [ + { + "type": "text", + "text": "Failed to process request: Invalid credentials provided" + } + ], + "isError": true + } +} +``` + +**A2A (Agent-to-Agent Protocol):** +```json +{ + "taskId": "task_123", + "status": "failed", + "message": { + "parts": [{ + "kind": "text", + "text": "Unable to complete operation: Authentication failed" + }] } } ``` diff --git a/docs/signals/specification.md b/docs/signals/specification.md index 08c09966a7..285c87ca15 100644 --- a/docs/signals/specification.md +++ b/docs/signals/specification.md @@ -36,10 +36,10 @@ The Signals Activation Protocol operates within the broader [AdCP Ecosystem Laye Every signal discovery request involves two key roles: #### Orchestrator -The platform or system making the API request to the audience platform: +The platform or system making the API request to the signal platform: - **Examples**: Scope3, Claude AI assistant, trading desk platform, campaign management tool - **Responsibilities**: Makes API calls, handles authentication, manages the technical interaction -- **Account**: Has technical credentials and API access to the audience platform +- **Account**: Has technical credentials and API access to the signal platform #### Principal The entity on whose behalf the request is being made: @@ -49,7 +49,7 @@ The entity on whose behalf the request is being made: #### How This Works in Practice -1. **Request Flow**: Orchestrator → Audience Platform (on behalf of Principal) → Decisioning Platform +1. **Request Flow**: Orchestrator → Signal Platform (on behalf of Principal) → Decisioning Platform 2. **Authentication**: Orchestrator authenticates with technical credentials 3. **Authorization**: Principal's identity determines available signals and pricing 4. **Activation**: Signals are activated for Principal's account on the decisioning platform @@ -65,14 +65,14 @@ The entity on whose behalf the request is being made: - **Flow**: Claude (on behalf of Omnicom) → LiveRamp (Omnicom's personalized catalog with negotiated rates and private data) → delivers to Omnicom's account on The Trade Desk **Scenario 2: Marketplace Agent with Personalized Catalog** -- **Orchestrator**: Scope3 platform (connecting audiences to agents) +- **Orchestrator**: Scope3 platform (connecting signals to agents) - **Principal**: Nike (advertiser setting up their agent) - **Signal Agent**: Experian (marketplace agent, Nike has account for personalized catalog) - **Decisioning Platform**: Nike Advertising Agent (running on Scope3 platform) - **Flow**: Scope3 (on behalf of Nike) → Experian (Nike's personalized catalog with owned data and custom rates) → delivers to Nike's advertising agent (hosted by Scope3) -**Scenario 3: Private Audience Agent** -- **Orchestrator**: Scope3 platform (connecting audiences to agents) +**Scenario 3: Private Signal Agent** +- **Orchestrator**: Scope3 platform (connecting signals to agents) - **Principal**: Walmart (retailer setting up their agent) - **Signal Agent**: Walmart (private agent, only visible to Walmart) - **Decisioning Platform**: Walmart Advertising Agent (running on Scope3 platform) @@ -116,7 +116,7 @@ External agents that license signal data with catalog-based access: - No account specification needed in requests - All segments already live (`scope: "platform-wide"`) -- **Personalized Catalog**: Requires principal account with the audience agent +- **Personalized Catalog**: Requires principal account with the signal agent - All platform-wide segments (same as public catalog) - PLUS account-specific segments (custom signals, private data) - Mixed pricing: negotiated rates for some, standard rates for others @@ -127,11 +127,11 @@ External agents that license signal data with catalog-based access: - **Private**: Owner-only authentication (e.g., Walmart authenticates to their own agent) - **Marketplace**: Orchestrator authentication determines catalog access level - Public catalog: Orchestrator credentials sufficient - - Personalized catalog: Requires principal account with audience agent + - Personalized catalog: Requires principal account with signal agent ### Segment ID Structure -Audience discovery involves multiple segment identifiers at different stages: +Signal discovery involves multiple segment identifiers at different stages: #### Signal Agent Segment ID The identifier used by the signal agent for their internal segment tracking: @@ -188,7 +188,7 @@ Activate a signal for use on a specific platform/account. This task handles the ### Data Provider Multi-Platform Flow (e.g., Peer39) -1. **Discovery**: Call `get_audiences` with multiple platforms to see all deployments at once +1. **Discovery**: Call `get_signals` with multiple platforms to see all deployments at once 2. **Review**: See which platforms have signals live vs. requiring activation, compare segment IDs across platforms @@ -236,6 +236,33 @@ Activate a signal for use on a specific platform/account. This task handles the - `AGENT_NOT_FOUND`: Private signal agent not visible to this principal - `AGENT_ACCESS_DENIED`: Principal not authorized for this signal agent +## Error Handling + +For comprehensive error handling guidance including pending vs error states, response patterns, and recovery strategies, see [Protocol Error Handling](../protocols/error-handling.md). + +Signal-specific error codes are documented in each task specification and the [Error Codes Reference](../reference/error-codes.md). + +## Response Structure + +All AdCP Signals responses follow a consistent structure across both MCP and A2A protocols: + +### Core Response Fields +- **message**: Human-readable summary of the operation result +- **context_id**: Session continuity identifier for follow-up requests +- **adcp_version**: Schema version used for this response +- **data**: Task-specific payload (varies by task) + +### Protocol Transport +- **MCP**: Returns complete response as flat JSON object +- **A2A**: Returns as structured artifacts with message in text part, data in data part +- **Data Consistency**: Both protocols contain identical AdCP data structures and version information + +### Asynchronous Operations +Some operations (like signal activation) are asynchronous and include: +- **task_id**: Unique identifier for tracking operation progress +- **status**: Current operation status (pending, processing, deployed, failed) +- **estimated_duration**: Time estimates for pending operations + ## Implementation Notes ### Authentication @@ -295,12 +322,14 @@ The principal's identity determines business-level access and pricing: ### Best Practices -1. Check `is_live` status before attempting activation -2. Allow 24-48 hours for signal activation -3. Understand the difference between size units -4. Consider both pricing options when available -5. Use multi-platform queries when discovering signals across SSPs -6. Store platform-specific segment IDs for campaign execution +1. **Status Checking**: Always check `is_live` status before attempting activation +2. **Activation Timing**: Allow appropriate time for signal activation (typically 1-2 hours, up to 24-48 hours for complex deployments) +3. **Size Understanding**: Understand the difference between individuals, devices, and households in coverage metrics +4. **Pricing Flexibility**: Consider both CPM and revenue share pricing options when available +5. **Multi-Platform Efficiency**: Use multi-platform queries when discovering signals across data providers to reduce API calls +6. **Campaign Integration**: Store platform-specific segment IDs for use in campaign targeting +7. **Asynchronous Handling**: Implement proper task monitoring for activation operations using task_id tracking +8. **Context Continuity**: Use context_id for session management across related signal operations ## Next Steps diff --git a/docs/signals/tasks/activate_signal.md b/docs/signals/tasks/activate_signal.md index 3cb569e6d6..82ddf853ed 100644 --- a/docs/signals/tasks/activate_signal.md +++ b/docs/signals/tasks/activate_signal.md @@ -27,19 +27,22 @@ The `activate_signal` task handles the entire activation lifecycle, including: *Required when activating at account level -## Response (Message) +## Response Structure -The response includes a human-readable message that: -- Confirms activation was initiated with estimated timing -- Provides progress updates during processing -- Explains successful deployment with segment ID -- Describes any errors and remediation steps +All AdCP responses include: +- **message**: Human-readable summary of the activation status +- **context_id**: Session continuity identifier for tracking progress +- **data**: Task-specific payload (see Response Data below) -The message is returned differently in each protocol: -- **MCP**: Returned as a `message` field in the JSON response -- **A2A**: Returned as a text part in the artifact +The response structure is identical across protocols, with only the transport wrapper differing: +- **MCP**: Returns complete response as flat JSON +- **A2A**: Returns as artifacts with message in text part, data in data part -## Response (Payload) +For asynchronous operations like activation, both protocols support: +- **Status tracking**: Check completion status via task_id +- **Progress updates**: Real-time updates on activation progress + +## Response Data ```json { @@ -48,23 +51,31 @@ The message is returned differently in each protocol: "decisioning_platform_segment_id": "string", "estimated_activation_duration_minutes": "number", "deployed_at": "string", - "error": { - "code": "string", - "message": "string" - } + "errors": [ + { + "code": "string", + "message": "string", + "field": "string", + "suggestion": "string", + "details": {} + } + ] } ``` ### Field Descriptions -- **task_id**: Unique identifier for tracking the activation -- **status**: Current status (pending, processing, deployed, failed) -- **decisioning_platform_segment_id**: The platform-specific ID to use once activated -- **estimated_activation_duration_minutes**: Estimated time to complete (optional) -- **deployed_at**: Timestamp when activation completed (optional) -- **error**: Error details if activation failed (optional) - - **code**: Error code for programmatic handling - - **message**: Detailed error message +- **task_id**: Unique identifier for tracking the activation task +- **status**: Current activation status (pending, processing, deployed, failed) +- **decisioning_platform_segment_id**: Platform-specific segment ID for campaign targeting +- **estimated_activation_duration_minutes**: Estimated completion time (when status is pending) +- **deployed_at**: ISO 8601 timestamp when activation completed (when status is deployed) +- **errors**: Optional array of errors and warnings encountered during activation + - **code**: Standardized error code for programmatic handling + - **message**: Human-readable error description with context + - **field**: Field path associated with the error (optional) + - **suggestion**: Suggested fix for the error (optional) + - **details**: Additional activation-specific error details (optional) ## Protocol-Specific Examples @@ -89,6 +100,7 @@ Initial response: { "message": "Initiating activation of 'Luxury Auto Intenders' on The Trade Desk", "context_id": "ctx-signals-123", + "adcp_version": "1.0.0", "task_id": "activation_789", "status": "pending", "decisioning_platform_segment_id": "ttd_agency123_lux_auto", @@ -100,11 +112,12 @@ After polling for completion: ```json { "message": "Signal successfully activated on The Trade Desk", - "context_id": "ctx-signals-123", + "context_id": "ctx-signals-123", + "adcp_version": "1.0.0", "task_id": "activation_789", "status": "deployed", "decisioning_platform_segment_id": "ttd_agency123_lux_auto", - "deployed_at": "2024-01-15T14:30:00Z" + "deployed_at": "2025-01-15T14:30:00Z" } ``` @@ -160,27 +173,33 @@ data: {"status": {"state": "completed"}, "artifacts": [{ "parts": [ {"kind": "text", "text": "Signal successfully activated on The Trade Desk"}, {"kind": "data", "data": { + "context_id": "ctx-signals-123", + "adcp_version": "1.0.0", + "task_id": "activation_789", "status": "deployed", "decisioning_platform_segment_id": "ttd_agency123_lux_auto", - "deployed_at": "2024-01-15T14:30:00Z" + "deployed_at": "2025-01-15T14:30:00Z" }} ] }]} ``` -### Key Differences -- **MCP**: Returns task_id for polling asynchronous operations -- **A2A**: Always async with real-time progress updates via SSE -- **Payload**: The `input` field in A2A contains the exact same structure as MCP's `arguments` +### Protocol Transport +- **MCP**: Returns task_id for polling-based asynchronous operation tracking +- **A2A**: Uses Server-Sent Events for real-time progress updates and completion +- **Data Consistency**: Both protocols contain identical AdCP data structures and version information ## Scenarios ### Initial Response (Pending) **Message**: "I've initiated activation of 'Luxury Automotive Context' on PubMatic for account brand-456-pm. This typically takes about 60 minutes. I'll monitor the progress and notify you when it's ready to use." -**Payload**: +**Complete Response**: ```json { + "message": "I've initiated activation of 'Luxury Automotive Context' on PubMatic for account brand-456-pm. This typically takes about 60 minutes. I'll monitor the progress and notify you when it's ready to use.", + "context_id": "ctx-signals-def456", + "adcp_version": "1.0.0", "task_id": "activation_12345", "status": "pending", "decisioning_platform_segment_id": "pm_brand456_peer39_lux_auto", @@ -191,9 +210,12 @@ data: {"status": {"state": "completed"}, "artifacts": [{ ### Status Update (Processing) **Message**: "Good progress on the activation. Access permissions validated successfully. Now configuring the signal deployment on PubMatic's platform. About 45 minutes remaining." -**Payload**: +**Complete Response**: ```json { + "message": "Good progress on the activation. Access permissions validated successfully. Now configuring the signal deployment on PubMatic's platform. About 45 minutes remaining.", + "context_id": "ctx-signals-def456", + "adcp_version": "1.0.0", "task_id": "activation_12345", "status": "processing" } @@ -202,9 +224,12 @@ data: {"status": {"state": "completed"}, "artifacts": [{ ### Final Response (Deployed) **Message**: "Excellent! The 'Luxury Automotive Context' signal is now live on PubMatic. You can start using it immediately in your campaigns with the ID 'pm_brand456_peer39_lux_auto'. The activation completed faster than expected - just 52 minutes." -**Payload**: +**Complete Response**: ```json { + "message": "Excellent! The 'Luxury Automotive Context' signal is now live on PubMatic. You can start using it immediately in your campaigns with the ID 'pm_brand456_peer39_lux_auto'. The activation completed faster than expected - just 52 minutes.", + "context_id": "ctx-signals-def456", + "adcp_version": "1.0.0", "task_id": "activation_12345", "status": "deployed", "decisioning_platform_segment_id": "pm_brand456_peer39_lux_auto", @@ -212,18 +237,55 @@ data: {"status": {"state": "completed"}, "artifacts": [{ } ``` +### Success with Warnings +**Message**: "Successfully activated 'Luxury Automotive Context' on PubMatic, but noted some configuration issues. The signal is live and ready to use, though performance may be sub-optimal until the account settings are updated." + +**Complete Response**: +```json +{ + "message": "Successfully activated 'Luxury Automotive Context' on PubMatic, but noted some configuration issues. The signal is live and ready to use, though performance may be sub-optimal until the account settings are updated.", + "context_id": "ctx-signals-def456", + "adcp_version": "1.0.0", + "task_id": "activation_12345", + "status": "deployed", + "decisioning_platform_segment_id": "pm_brand456_peer39_lux_auto", + "deployed_at": "2025-01-15T14:30:00Z", + "errors": [ + { + "code": "SUBOPTIMAL_CONFIGURATION", + "message": "Account frequency cap settings may limit signal reach", + "field": "account.frequency_settings", + "suggestion": "Contact your PubMatic account manager to review frequency cap settings for optimal performance" + } + ] +} +``` + ### Error Response (Failed) **Message**: "I couldn't activate the signal on PubMatic. Your account 'brand-456-pm' doesn't have permission to use Peer39 data. Please contact your PubMatic account manager to enable Peer39 access, then we can try again." -**Payload**: +**Complete Response**: ```json { + "message": "I couldn't activate the signal on PubMatic. Your account 'brand-456-pm' doesn't have permission to use Peer39 data. Please contact your PubMatic account manager to enable Peer39 access, then we can try again.", + "context_id": "ctx-signals-def456", + "adcp_version": "1.0.0", "task_id": "activation_12345", "status": "failed", - "error": { - "code": "DEPLOYMENT_UNAUTHORIZED", - "message": "Account brand-456-pm not authorized for Peer39 data on PubMatic" - } + "errors": [ + { + "code": "DEPLOYMENT_UNAUTHORIZED", + "message": "Account brand-456-pm not authorized for Peer39 data on PubMatic", + "field": "account", + "suggestion": "Contact your PubMatic account manager to enable Peer39 data access for your account", + "details": { + "account_id": "brand-456-pm", + "platform": "pubmatic", + "data_provider": "peer39", + "required_permission": "third_party_data_access" + } + } + ] } ``` @@ -232,15 +294,33 @@ data: {"status": {"state": "completed"}, "artifacts": [{ - **pending**: Activation request received and queued - **processing**: Activation in progress - **deployed**: Signal successfully activated and ready to use -- **failed**: Activation failed (see error message for details) +- **failed**: Activation could not be completed (check errors array for details) ## Error Codes +### Activation Errors - `SIGNAL_AGENT_SEGMENT_NOT_FOUND`: Signal agent segment ID doesn't exist -- `ACTIVATION_FAILED`: Could not activate signal -- `ALREADY_ACTIVATED`: Signal already active -- `DEPLOYMENT_UNAUTHORIZED`: Can't deploy to platform/account -- `INVALID_PRICING_MODEL`: Pricing model not available +- `ACTIVATION_FAILED`: Could not activate signal for unspecified reasons +- `ALREADY_ACTIVATED`: Signal already active on the specified platform/account +- `DEPLOYMENT_UNAUTHORIZED`: Can't deploy to platform/account due to permissions +- `INVALID_PRICING_MODEL`: Requested pricing model not available for this signal + +### Configuration Warnings +- `SUBOPTIMAL_CONFIGURATION`: Signal activated but account settings may impact performance +- `SLOW_ACTIVATION`: Activation taking longer than expected but still in progress +- `FREQUENCY_CAP_RESTRICTIVE`: Signal activated but account frequency caps may reduce performance + +## Error Handling Philosophy + +### Status vs Errors +- **Task Status**: Indicates overall activation outcome (`deployed`, `failed`, etc.) +- **Errors Array**: Contains specific issues, warnings, and remediation steps +- **Partial Success**: Signal can be `deployed` while still having warnings in `errors` array + +### Error Types +- **Fatal Errors**: Prevent activation (status = `failed`) +- **Warnings**: Signal activates successfully but with caveats (status = `deployed` + errors) +- **Configuration Issues**: Non-blocking problems that affect performance ## Usage Notes diff --git a/docs/signals/tasks/get_signals.md b/docs/signals/tasks/get_signals.md index 0d37739b1e..5a137af1c7 100644 --- a/docs/signals/tasks/get_signals.md +++ b/docs/signals/tasks/get_signals.md @@ -47,19 +47,18 @@ The `get_signals` task returns both signal metadata and real-time deployment sta | `max_cpm` | number | No | Maximum CPM price filter | | `min_coverage_percentage` | number | No | Minimum coverage requirement | -## Response (Message) +## Response Structure -The response includes a human-readable message that: -- Summarizes the signals found matching the criteria -- Explains deployment status across platforms -- Highlights activation requirements or timing -- Provides recommendations for signal selection +All AdCP responses include: +- **message**: Human-readable summary of the operation result +- **context_id**: Session continuity identifier for follow-up requests +- **data**: Task-specific payload (see Response Data below) -The message is returned differently in each protocol: -- **MCP**: Returned as a `message` field in the JSON response -- **A2A**: Returned as a text part in the artifact +The response structure is identical across protocols, with only the transport wrapper differing: +- **MCP**: Returns complete response as flat JSON +- **A2A**: Returns as artifacts with message in text part, data in data part -## Response (Payload) +## Response Data ```json { @@ -139,6 +138,7 @@ The AdCP payload is identical across protocols. Only the request/response wrappe { "message": "Found 1 luxury segment matching your criteria. Available on The Trade Desk, pending activation on Amazon DSP.", "context_id": "ctx-signals-123", + "adcp_version": "1.0.0", "signals": [ { "signal_agent_segment_id": "luxury_auto_intenders", @@ -213,7 +213,7 @@ await a2a.send({ ``` ### A2A Response -A2A returns results as artifacts: +A2A returns results as artifacts with the same data structure: ```json { "artifacts": [{ @@ -226,6 +226,8 @@ A2A returns results as artifacts: { "kind": "data", "data": { + "context_id": "ctx-signals-123", + "adcp_version": "1.0.0", "signals": [ { "signal_agent_segment_id": "luxury_auto_intenders", @@ -263,10 +265,10 @@ A2A returns results as artifacts: } ``` -### Key Differences -- **MCP**: Direct tool call with arguments, returns flat JSON response -- **A2A**: Skill invocation with input, returns artifacts with text and data parts -- **Payload**: The `input` field in A2A contains the exact same structure as MCP's `arguments` +### Protocol Transport +- **MCP**: Direct tool call with arguments, returns complete response as flat JSON +- **A2A**: Skill invocation with input, returns structured artifacts with message and data separated +- **Data Consistency**: Both protocols contain identical AdCP data structures and version information ## Scenarios @@ -364,10 +366,16 @@ Discover all available deployments across platforms: ## Error Codes +### Discovery Errors - `SIGNAL_AGENT_SEGMENT_NOT_FOUND`: Signal agent segment ID doesn't exist - `AGENT_NOT_FOUND`: Private signal agent not visible to this principal - `AGENT_ACCESS_DENIED`: Principal not authorized for this signal agent +### Discovery Warnings +- `PRICING_UNAVAILABLE`: Pricing data temporarily unavailable for one or more platforms +- `PARTIAL_COVERAGE`: Some requested platforms don't support this signal type +- `STALE_DATA`: Some signal metadata may be outdated due to provider refresh delays + ## Usage Notes 1. **Multi-Platform Discovery**: Use `platforms: "all"` to see all available deployments @@ -382,6 +390,7 @@ Discover all available deployments across platforms: { "message": "I found 3 signals matching your luxury goods criteria. The best option is 'Affluent Shoppers' with 22% coverage, already live across all requested platforms. 'High Income Households' offers broader reach (35%) but requires activation on OpenX. All signals are priced between $2-4 CPM.", "context_id": "ctx-signals-abc123", + "adcp_version": "1.0.0", "signals": [ { "signal_agent_segment_id": "acme_affluent_shoppers", @@ -416,12 +425,69 @@ Discover all available deployments across platforms: } ``` +### Response - Partial Success with Warnings + +```json +{ + "message": "Found 2 luxury signals, but encountered some platform limitations. The 'Premium Auto Shoppers' signal has limited reach due to data restrictions, and pricing data is unavailable for one platform. Review the warnings below for optimization suggestions.", + "context_id": "ctx-signals-abc123", + "adcp_version": "1.0.0", + "signals": [ + { + "signal_agent_segment_id": "premium_auto_shoppers", + "name": "Premium Auto Shoppers", + "description": "High-value automotive purchase intenders", + "signal_type": "marketplace", + "data_provider": "Experian", + "coverage_percentage": 8, + "deployments": [ + { + "platform": "the-trade-desk", + "account": null, + "is_live": true, + "scope": "platform-wide", + "decisioning_platform_segment_id": "ttd_exp_auto_premium" + } + ], + "pricing": { + "cpm": 4.50, + "currency": "USD" + } + } + ], + "errors": [ + { + "code": "PRICING_UNAVAILABLE", + "message": "Pricing data temporarily unavailable for The Trade Desk platform", + "field": "signals[0].pricing", + "suggestion": "Retry in 15-30 minutes when platform pricing feed updates", + "details": { + "affected_platform": "the-trade-desk", + "last_updated": "2025-01-15T12:00:00Z", + "retry_after": 1800 + } + }, + { + "code": "PRICING_UNAVAILABLE", + "message": "Pricing data temporarily unavailable for Amazon DSP", + "field": "filters.platforms", + "suggestion": "Pricing will be available during activation, or try again later", + "details": { + "affected_platform": "amazon-dsp", + "retry_after": 1800 + } + } + ] +} +``` + ### Response - No Signals Found ```json { "message": "I couldn't find any signals matching 'underwater basket weavers' in the requested platforms. This appears to be a very niche audience. Consider broadening your criteria to 'craft enthusiasts' or 'hobby communities' for better results. Alternatively, we could create a custom signal for this specific audience.", "context_id": "ctx-signals-abc123", + "adcp_version": "1.0.0", "signals": [] } ``` diff --git a/static/schemas/v1/signals/activate-signal-response.json b/static/schemas/v1/signals/activate-signal-response.json index d6f98ef69e..6c15c13264 100644 --- a/static/schemas/v1/signals/activate-signal-response.json +++ b/static/schemas/v1/signals/activate-signal-response.json @@ -10,6 +10,14 @@ "description": "AdCP schema version used for this response", "pattern": "^\\d+\\.\\d+\\.\\d+$" }, + "message": { + "type": "string", + "description": "Human-readable summary of the activation status" + }, + "context_id": { + "type": "string", + "description": "Session continuity identifier for tracking progress" + }, "task_id": { "type": "string", "description": "Unique identifier for tracking the activation" @@ -41,6 +49,6 @@ } } }, - "required": ["adcp_version", "task_id", "status"], + "required": ["adcp_version", "message", "context_id", "task_id", "status"], "additionalProperties": false } \ No newline at end of file diff --git a/static/schemas/v1/signals/get-signals-response.json b/static/schemas/v1/signals/get-signals-response.json index 5e1f47c7de..c6d326e5ab 100644 --- a/static/schemas/v1/signals/get-signals-response.json +++ b/static/schemas/v1/signals/get-signals-response.json @@ -10,6 +10,14 @@ "description": "AdCP schema version used for this response", "pattern": "^\\d+\\.\\d+\\.\\d+$" }, + "message": { + "type": "string", + "description": "Human-readable summary of the signal discovery results" + }, + "context_id": { + "type": "string", + "description": "Session continuity identifier for follow-up requests" + }, "signals": { "type": "array", "description": "Array of matching signals", @@ -111,6 +119,6 @@ } } }, - "required": ["adcp_version", "signals"], + "required": ["adcp_version", "message", "context_id", "signals"], "additionalProperties": false } \ No newline at end of file