Raxol.MCP.CircuitBreaker (Raxol MCP v2.8.0)

Copy Markdown View Source

Lightweight ETS-backed circuit breaker for MCP tool/resource callbacks.

Tracks failure counts per key and transitions through three states:

  • closed -- normal operation, failures below threshold
  • open -- callback blocked after repeated failures, returns {:error, :circuit_open}
  • half_open -- recovery probe allowed after cooldown; success resets, failure re-opens

No GenServer -- all state lives in a public ETS table with atomic counter updates. The table is created by the owning process (typically MCP.Registry), and it lives exactly as long as that process: a caller that creates its own gets a breaker nobody else can see, which for a failover gate means failover never trips.

Keys

A key names what is being guarded. {:tool, _}, {:resource, _} and {:prompt, _} are MCP callbacks; {:origin, _} is an outbound upstream, used by raxol_web3's guarded client so a challenge-serving or hard-down explorer is not retried on every call. Nothing here is MCP-specific but the module's home, which is why the type widened rather than a second breaker being written.

Configuration

Defaults can be overridden via application env or per-call opts:

config :raxol_mcp, :circuit_breaker,
  failure_threshold: 5,
  recovery_ms: 30_000

Summary

Functions

Check the circuit state for a key.

The failure count that opens a circuit: opts[:failure_threshold], then app config, then the default (5). What record_failure/3 applies.

Create a new circuit breaker ETS table.

Record a failed callback invocation.

Record a successful callback invocation. Resets the circuit to closed.

Manually reset a circuit to closed.

Reset all circuit breaker state.

Get the current status of a circuit.

Types

key()

@type key() ::
  {:tool, String.t()}
  | {:resource, String.t()}
  | {:prompt, String.t()}
  | {:origin, String.t()}

state()

@type state() :: :closed | :open | :half_open

Functions

check(table, key, opts \\ [])

@spec check(:ets.tid(), key(), keyword()) :: state()

Check the circuit state for a key.

Returns :closed (proceed), :open (block), or :half_open (probe). Automatically transitions from open to half_open after the recovery period.

failure_threshold(opts \\ [])

@spec failure_threshold(keyword()) :: pos_integer()

The failure count that opens a circuit: opts[:failure_threshold], then app config, then the default (5). What record_failure/3 applies.

new(name \\ :raxol_mcp_breakers)

@spec new(atom()) :: :ets.tid()

Create a new circuit breaker ETS table.

record_failure(table, key, opts \\ [])

@spec record_failure(:ets.tid(), key(), keyword()) :: :ok

Record a failed callback invocation.

Increments the failure counter. Transitions to open when the threshold is reached. In half_open state, a single failure re-opens the circuit.

record_success(table, key)

@spec record_success(:ets.tid(), key()) :: :ok

Record a successful callback invocation. Resets the circuit to closed.

reset(table, key)

@spec reset(:ets.tid(), key()) :: :ok

Manually reset a circuit to closed.

reset_all(table)

@spec reset_all(:ets.tid()) :: :ok

Reset all circuit breaker state.

status(table, key)

@spec status(:ets.tid(), key()) :: %{state: state(), failures: non_neg_integer()}

Get the current status of a circuit.