Error Handling
The ChatCast MCP server returns errors at three distinct levels. Understanding these levels helps you build agents that degrade gracefully.
Error Levels
Errors can occur at three levels, each with a different structure and resolution approach:
| Level | When | Format |
|---|---|---|
| HTTP | Before MCP protocol starts | HTTP status code + JSON body |
| MCP | During MCP protocol handling | MCP error with numeric code |
| Tool | Within a tool's response | { error, hint } in the tool result |
HTTP-Level Errors
These errors occur before the MCP protocol is established, typically when the endpoint URL is invalid.
| Status | Body | Cause | Resolution |
|---|---|---|---|
404 | {"error": "Not found"} | Store public ID does not exist, or the store has disabled public MCP | Verify the store public ID. Contact the merchant to confirm MCP is enabled. |
MCP-Level Errors
These errors are returned as MCP protocol error responses with numeric codes.
| Code | Message | Cause | Resolution |
|---|---|---|---|
-32000 | Rate limit exceeded. Retry after {N} seconds. | More than 100 requests per minute from the same IP to the same store | Wait the indicated number of seconds. Implement exponential backoff. See Rate Limits. |
// MCP error response structure
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "Rate limit exceeded. Retry after 12 seconds."
}
}Tool-Level Errors
Tool-level errors are returned as part of the tool's successful MCP response. The tool call itself succeeds at the protocol level, but the result contains an error object with an optional hint field.
| Tool | Error | Cause | Resolution |
|---|---|---|---|
get_brand_info | Store not found | Internal store lookup failure | Retry. If persistent, the store may have been removed. |
get_agent_offers | Agent not registered. Call register_agent first. | Using an agent_id that is not registered with this store | Call register_agent first. |
get_my_discount_code | Agent not registered. Call register_agent first. | Using an unregistered agent_id | Call register_agent first. |
get_my_discount_code | Campaign {id} not found. | Invalid campaign_id or campaign belongs to a different store | Call get_active_campaigns to get valid campaign IDs. |
get_my_earnings | Agent not registered. Call register_agent first. | Using an unregistered agent_id | Call register_agent first. |
get_product_details | Product not found | Invalid public_id or product belongs to a different store | Verify the product public_id. Use search_products to find valid product IDs. |
Error Response Shape
Tool-level errors follow a consistent pattern with an error field and an optional hint field:
// Tool error with hint
{
"error": "Agent not registered. Call register_agent first.",
"hint": "Use register_agent to get an agent_id."
}
// Tool error without hint
{
"error": "Store not found"
}
// Array-wrapped tool error (some tools)
[
{
"error": "Agent not registered. Call register_agent first."
}
]Retry Guidance
When your agent encounters an error, follow this retry strategy:
- 1.On a rate limit error, wait the number of seconds indicated in the error message.
- 2.Use exponential backoff: 1s, 2s, 4s, 8s between retries.
- 3.Maximum 3 retries before surfacing the error to the user.
- 4.Cache responses where possible. Product data, FAQs, and policies change infrequently.
async function callWithRetry(fn, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
// Check for rate limit error (MCP code -32000)
if (error.code === -32000 && attempt < maxRetries) {
// Parse retry delay from error message
const match = error.message?.match(/Retry after (\d+) seconds/);
const retryAfter = match ? parseInt(match[1], 10) : Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
throw error;
}
}
}Cache aggressively
Product data, FAQs, policies, and brand info rarely change. Cache these responses for at least a few minutes to reduce the number of requests and avoid hitting rate limits. Commission rates and active campaigns change less frequently and can be cached for longer periods.