BYOA Recovery — Bring Your Own Agent
Use your own AI agent as the first responder when a workflow step fails.
Overview
When a step fails, CFFBRW's Tier 2 recovery normally calls a built-in flash-lite model to diagnose the error and suggest a fix. With BYOA Recovery, you can configure your own MCP server to handle this first. Your agent gets full context — the step spec, the error, prior outputs, the actual HTTP request that failed — and can return a recovery action using the same 5-action protocol as the built-in provider.
If your agent responds within the timeout with a valid action, it is used. If it times out, errors, or returns invalid data, the built-in provider takes over silently. You lose nothing; you only gain.
Why this matters: A flash-lite model knows nothing about your APIs, business rules, or data schemas. Your agent does. Recovery decisions your agent makes will be more accurate — fewer needs_human escalations, better retry parameters, domain-aware fixed outputs.
How It Works
Step fails
│
▼
Is recovery.mcp_server_id configured?
├── YES → POST diagnose_step_failure to your MCP server
│ ├── Valid response → execute action (same pipeline as built-in)
│ └── Timeout / error / invalid → log + fall through
└── NO → skip
│
▼
Built-in runtime provider (flash-lite, unchanged behavior)
Your agent is called on the first recovery attempt only. If it fails, the built-in provider handles attempts 1+ as normal. This keeps the total recovery time within budget.
Setting Up
1. Build a recovery MCP server
Your MCP server must expose a tool named diagnose_step_failure. You can get the full JSON Schema for input and output from the BYOM MCP resource:
Resource URI: cffbrw://recovery/diagnose-step-failure-schema
Connect your BYOM MCP client and read this resource to get the complete input/output contract.
2. Input your agent receives
{
"schema_version": 1,
"step": {
"type": "http_request",
"title": "Fetch user profile",
"intent": "GET user data from /api/users/:id",
"index": 2,
"config": { "url": "https://api.example.com/users/42", "method": "GET" }
},
"error": "HTTP 429: Too Many Requests",
"prior_outputs": {
"0": "{ \"token\": \"abc...\" }",
"1": "{ \"userId\": 42 }"
},
"workflow_input": { "userId": 42 },
"resolved_request": {
"url": "https://api.example.com/users/42",
"body": null
}
}
Fields your agent receives:
step— step type, title, intent, index, and type-specific configerror— the exact error string from the failed executionprior_outputs— summarized outputs from previous steps (500 chars each)workflow_input— the workflow's runtime input (OAuth tokens and connector credentials are stripped)catalog_action— forconnectorsteps: the catalog action params, required fields, and param descriptionsresolved_request— for HTTP/connector steps: the actual URL and request body that was senthuman_hint— set when a human re-triggered recovery with a hint
3. Output your agent must return
Return one of these 5 actions. They are the same 5 actions the built-in provider uses:
fixed_output — Provide the result directly without retrying:
{
"type": "fixed_output",
"diagnosis": "Rate limited. Returning cached profile from prior run context.",
"output": { "id": 42, "name": "Alice" }
}
retry_with_params — Retry the step with modified parameters:
{
"type": "retry_with_params",
"diagnosis": "Wrong endpoint version. Correcting to v2.",
"modifications": {
"httpRequest": { "url": "https://api.example.com/v2/users/42" }
}
}
Allowed modifications keys: httpRequest, connectorCall, timeout, headers, body. The same SAFE_FIELDS guard that applies to built-in recovery applies here — your modifications are deep-merged, not replaced wholesale.
code_fix — Provide JavaScript to compute the output:
{
"type": "code_fix",
"diagnosis": "Response shape changed. Transforming to expected format.",
"code": "return { id: input.userId, name: input.fullName };"
}
Note: code_fix still requires human approval if your workspace has that policy enabled.
retry_with_tool — Call a different MCP tool instead:
{
"type": "retry_with_tool",
"diagnosis": "Primary tool unavailable. Using fallback.",
"toolCall": {
"server": "my-mcp-server-id",
"tool": "get_user_fallback",
"params": { "userId": 42 }
}
}
needs_human — Escalate to human review:
{
"type": "needs_human",
"diagnosis": "Authentication expired and cannot be refreshed automatically.",
"suggestion": "Re-authorize the API connection in Settings → Connectors."
}
4. Register your MCP server
Register your server under Settings → MCP Servers. It must be reachable from the CFFBRW gateway.
5. Configure recovery settings
Go to Settings → Recovery in the dashboard:
- Select your MCP server from the dropdown
- Set a timeout (default: 15 seconds, range: 1-60 seconds)
- Click Save
Or via API:
curl -X PUT https://api.cffbrw.com/v1/settings/recovery.mcp_server_id \
-H "Authorization: Bearer <token>" \
-d '{"value": "your-mcp-server-id"}'
curl -X PUT https://api.cffbrw.com/v1/settings/recovery.timeout_ms \
-H "Authorization: Bearer <token>" \
-d '{"value": "15000"}'
Debugging
Step logs
Every step log entry has a recovery_source field:
"byoa"— your agent's response was used"builtin"— the built-in provider was used (BYOA was not configured, timed out, or returned invalid data)null— the step did not need recovery
Query via GET /v1/runs/:runId/logs.
WebSocket events
The step:adapting event includes BYOA details (truncated to 1KB):
byoa_attempted— whether your agent was calledbyoa_result_summary— the action type and diagnosis (if valid)byoa_timing_ms— how long your agent tookbyoa_rejection_reason— why the response was rejected (if applicable)
Common issues
Your agent is never called: Check that recovery.mcp_server_id is set and points to a registered server in your workspace. Use the Recovery settings page — the tool detection badge shows whether diagnose_step_failure is detected.
Response rejected as invalid: Your tool must return exactly one of the 5 action types with all required fields. Get the full JSON Schema from the BYOM MCP resource cffbrw://recovery/diagnose-step-failure-schema.
Agent times out: The default timeout is 15 seconds. Increase via recovery.timeout_ms (max 60 seconds). The built-in provider recovers after timeout — the workflow does not fail.
retry_with_tool references unknown tool: The tool call must reference a server and tool that are registered in your workspace. Specific error is logged to the step's message field.
Tips for building a good recovery agent
- Load your API specs and error codes into context. Your agent should know what a 429 means for your specific API and what the backoff strategy is.
- Use
prior_outputsto infer state. If step 0 fetched an auth token, it's inprior_outputs["0"]. - Prefer
retry_with_paramsoverfixed_outputfor transient errors. Fixed outputs bypass the actual API call — retry_with_params lets the step run again with corrections. - Use
needs_humanconservatively. Only escalate when you genuinely cannot recover. The built-in provider will also attempt before needs_human — your agent and the built-in together give 2 recovery attempts. - Keep your agent fast. 15 seconds is the default budget. Long-running agents eat into step timeout headroom.
A2A (Agent-to-Agent) Recovery
What is A2A?
A2A is Google's Agent-to-Agent protocol (v1.0.0), an open standard for agent interoperability over JSON-RPC 2.0. Instead of building an MCP server with a diagnose_step_failure tool, you can point CFFBRW at any A2A-compatible agent. The agent receives the same failure context and returns the same 5 recovery action types — the only difference is the transport envelope.
How it works
- You have an A2A-compatible agent running somewhere (any language, any platform)
- Paste its URL in CFFBRW Settings > Recovery > A2A Agent URL
- When a step fails, CFFBRW sends a JSON-RPC 2.0
SendMessagerequest with the failure context as adatapart - Your agent responds with a recovery action in a
datapart - Same 5 action types as MCP recovery:
fixed_output,retry_with_params,code_fix,retry_with_tool,needs_human
Step fails
|
v
Is recovery.a2a_agent_url configured?
|-- YES --> POST /message:send to your A2A agent
| |-- Valid response --> execute action
| +-- Timeout / error / invalid --> log + fall through
+-- NO --> skip
|
v
Is recovery.mcp_server_id configured?
|-- YES --> POST diagnose_step_failure to your MCP server
+-- NO --> skip
|
v
Built-in runtime provider (flash-lite)
Configuration
Dashboard: Settings > Recovery > paste your A2A Agent URL. Optionally add a Bearer token for authentication.
API:
# Set A2A agent URL
curl -X PUT https://api.cffbrw.com/v1/settings/recovery.a2a_agent_url \
-H "Authorization: Bearer <token>" \
-d '{"value": "https://my-agent.example.com"}'
# Set auth token (optional)
curl -X PUT https://api.cffbrw.com/v1/settings/recovery.a2a_auth_token \
-H "Authorization: Bearer <token>" \
-d '{"value": "your-bearer-token"}'
The agent card is fetched automatically when you save the URL. CFFBRW reads /.well-known/agent-card.json from your agent and stores the name and description for display in the dashboard.
Input/Output Contract
The payload inside the A2A envelope is identical to MCP recovery. Your agent receives RecoveryToolInput (step spec, error, prior outputs, workflow input, resolved request) and must return a valid RecoveryAction (one of the 5 action types).
The difference is the transport: instead of an MCP tool call, the data is wrapped in a JSON-RPC 2.0 SendMessage envelope:
Request (CFFBRW to your agent):
{
"jsonrpc": "2.0",
"id": "recovery-{runId}-{stepIndex}",
"method": "SendMessage",
"params": {
"message": {
"messageId": "{uuid}",
"role": "ROLE_USER",
"parts": [{ "type": "data", "data": { /* RecoveryToolInput */ } }]
}
}
}
Response (your agent to CFFBRW):
{
"jsonrpc": "2.0",
"id": "recovery-{runId}-{stepIndex}",
"result": {
"message": {
"role": "ROLE_AGENT",
"parts": [{ "type": "data", "data": { /* RecoveryAction */ } }],
"messageId": "..."
}
}
}
Error response:
{
"jsonrpc": "2.0",
"id": "...",
"error": { "code": -32000, "message": "reason" }
}
See the BYOM MCP resource cffbrw://recovery/diagnose-step-failure-schema for the full JSON Schema of RecoveryToolInput and RecoveryAction.
Building an A2A Recovery Agent
Your agent must:
- Expose
POST /message:send— This is the A2A spec endpoint for receiving messages. - Publish
/.well-known/agent-card.json— CFFBRW fetches this at configuration time to validate your agent and display its name/description. The card should includename,description, andurlfields. - Read the
datapart — The firstdatapart in the message contains theRecoveryToolInputpayload (same schema as the MCPdiagnose_step_failuretool). - Return a
datapart — Your response message must include adatapart containing a validRecoveryAction(one of the 5 action types). - Respond within the timeout — Default is 15 seconds, configurable up to 60 seconds via
recovery.timeout_ms.
Any language or framework works. If your agent framework supports the A2A spec, you only need to implement the message handler — the framework handles JSON-RPC routing.
Priority
When both A2A and MCP are configured, A2A takes priority. The MCP server is not called. To use MCP recovery instead, remove the A2A Agent URL from settings.
Agent Mode — Async Recovery via Inbox
The MCP and A2A methods above are synchronous — CFFBRW calls your server in real time when a step fails. Agent mode is asynchronous — the workflow pauses, your agent polls an inbox, and submits a fix whenever it's ready.
This is for AI coding agents (Claude Code, Cursor, Copilot) that can't run a server but can poll an API or read MCP resources.
How it works
Step fails
│
▼
recovery.mode = "agent"?
├── YES → open needs_agent gate (waitForEvent, zero cost)
│ ├── Agent polls GET /v1/recovery/pending (or MCP resource)
│ ├── Agent reads failure context
│ ├── Agent submits fix via POST /v1/recovery/:runId/:stepIndex/action
│ │ (or MCP tool: submit_recovery_action)
│ └── Workflow resumes with fix applied
│ └── Timeout (default 300s) → fall through to built-in
└── NO → synchronous recovery (A2A → MCP → built-in)
Configuration
Set recovery.mode to agent:
curl -X PUT https://api.cffbrw.com/v1/settings/recovery.mode \
-H "Authorization: Bearer <token>" \
-d '{"value": "agent"}'
Optional: set timeout (default 300 seconds, range 30–3600):
curl -X PUT https://api.cffbrw.com/v1/settings/recovery.agent_timeout \
-H "Authorization: Bearer <token>" \
-d '{"value": "600"}'
Agent workflow
-
Poll for pending gates:
- API:
GET /v1/recovery/pending(returns allneeds_agentgates in workspace) - MCP resource:
cffbrw://recovery/pending
- API:
-
Read failure context:
- API:
GET /v1/recovery/:runId/:stepIndex(fullRecoveryToolInputwith step spec, error, prior outputs)
- API:
-
Submit a fix:
- API:
POST /v1/recovery/:runId/:stepIndex/action - MCP tool:
submit_recovery_action - Same 5 action types:
fixed_output,retry_with_params,code_fix,retry_with_tool,needs_human
- API:
-
Workflow resumes with the submitted action, same as synchronous recovery.
When to use which mode
| Mode | Set recovery.mode to | Best for | Requires |
|---|---|---|---|
| Built-in only | off (default) | No custom recovery | Nothing |
| Agent (async) | agent | AI coding agents (Claude Code, Cursor) that can poll but can't host a server | Agent with MCP or API access |
| Server (sync) | server | Dedicated recovery servers, production automation | Running A2A or MCP server |
Security
- OAuth tokens and connector credentials are stripped from
workflow_inputbefore sending to your agent. - Your agent cannot access anything outside what is provided in the tool input.
code_fixresponses go through the same human approval gate as built-in code fixes.retry_with_paramsmodifications are deep-merged through the sameSAFE_FIELDSguard — your agent cannot inject arbitrary fields into the step spec.- Your agent's MCP server must be registered in your workspace. Cross-workspace agents are not supported.
- A2A agent URLs must use HTTPS and pass SSRF validation (no localhost, private IPs, metadata endpoints).