CurbiCurbi

Curbi / MCP Chaos · Local fault workbench

Break your MCP integration
before production does.

A local fault-injection workbench for MCP tool calls. The included refund fixtures exercise six failure scenarios; extend the server adapter and oracles for your integration.

Download MCP Chaos source ↓See the matrixRepair Lab →
01 · TARGETIncluded payments fixture, over MCP stdio.
02 · PROXYSeeded faults at the tool-call boundary.
03 · INTEGRATIONThree included client integration patterns.
04 · ORACLESRefund-specific duplicate and evidence checks.
05 · VERDICTCompared with fixture server effect counts.

One refund integration, six ways to fail.

Each cell is a fresh fixture payments server. Refund counts are the server’s ground truth; the oracles agreed with them on every cell.

FailureTypical first versionNew key per retryRepaired pattern
Timeout after a committed write
A successful write times out and the retry duplicates it
FAIL
2 refunds · duplicate_write
FAIL
2 refunds · duplicate_write
PASS
1 refund
Request lost before the server
The call never arrived; the client cannot tell this from case 01
PASS
1 refund
PASS
1 refund
PASS
0 refunds · held
Authentication expired mid-workflow
Token expires between steps; blind retries or silent success follow
PASS
1 refund
PASS
1 refund
PASS
0 refunds
Provider changed its response shape
The tool's declared output and actual response disagree
FAIL
1 refund · false_success, unhandled_schema_drift
FAIL
1 refund · false_success, unhandled_schema_drift
PASS
1 refund
Silent failure: success with no data
The API returns a success code with an empty payload
FAIL
1 refund · false_success
FAIL
1 refund · false_success
PASS
1 refund
The whole operation is retried (crash, double-tap, queue redelivery)
The integration runs twice for one logical operation; a per-invocation key cannot de-duplicate it
FAIL
3 refunds · duplicate_write
FAIL
2 refunds · duplicate_write
PASS
1 refund

“Held” means the repair kept the outcome pending instead of guessing; retrying with the same key is a per-provider policy decision.

The failure catalog.

timeout_after_forward

Timeout after a committed write

A successful write times out and the retry duplicates it

Repair pattern: Stable idempotency key per logical operation; reconcile by lookup; never a second write after an unknown outcome

drop_before_forward

Request lost before the server

The call never arrived; the client cannot tell this from case 01

Repair pattern: Same as 01: from the client this is indistinguishable, so it must reconcile, not guess

auth_expired

Authentication expired mid-workflow

Token expires between steps; blind retries or silent success follow

Repair pattern: Recognise the auth failure, refresh credentials out of band, retry with the SAME key

schema_drift

Provider changed its response shape

The tool's declared output and actual response disagree

Repair pattern: Validate against the declared output schema; on mismatch treat the outcome as unknown and reconcile

empty_success

Silent failure: success with no data

The API returns a success code with an empty payload

Repair pattern: Never accept success without the declared evidence fields; reconcile

APP_LEVEL_RETRY

The whole operation is retried (crash, double-tap, queue redelivery)

The integration runs twice for one logical operation; a per-invocation key cannot de-duplicate it

Repair pattern: Derive the idempotency key from the logical operation id (stable across invocations and restarts), never from a per-call random or attempt counter

Run it locally.

Terminal · after extracting the download
cd curbi-mcp-chaos-v1
npm ci --ignore-scripts
node src/matrix.mjs     # the matrix above
npm test                # 10 tests, including oracle-vs-ground-truth
Local source distribution: faults at the MCP boundary, not the network. Proxy forwarding is reusable, but this campaign runner and its oracles are refund-specific; generic target support is not proven. No measured speed comparison with an engineer yet.

Run a check. Keep the result.

Check a reply Connect your agent →

A pass means no configured rule fired. It grants no execution authority or provider-completion proof.