reversi_sdk.integrations. Each one routes tool calls through a run, so every
call is authorized, journaled before it executes, and given an outcome afterwards. The SDK core stays standard
library only: each adapter imports its framework lazily, and import reversi_sdk never needs one.
Every tool name you pass must have a manifest on the server, and the run must hold its
permission. A refusal is a normal result, not an error: the adapters either return an explanation the model can read
(on_block="message", the default) or raise (on_block="raise"). When the refusal is a pending approval, the message
carries the approval id so a human can approve it and the agent can retry once.
LangGraph and LangChain
reversible_tool wraps a plain function (apply it under @tool so LangChain still sees the signature and docstring)
or a BaseTool-like object that has name and invoke(input). Async functions and ainvoke are supported.
rollback_node returns a function you can add to a graph as a node. It runs RunHandle.rollback (needs an
operator key) and puts the report in the state under reversi_rollback:
ToolNode and create_react_agent need real BaseTool instances. Wrap the function before @tool, as
above; the proxy returned for an existing tool object is not itself a BaseTool subclass.
CrewAI
ReversibleCrewTool has the shape of crewai.tools.BaseTool (name, description, _run) and wraps an existing
tool. CrewAI validates its tools= list against the real BaseTool class, so call to_crewai() to get a genuine
subclass instance that delegates to the adapter (it imports crewai at that moment).
to_crewai() builds the subclass dynamically and copies name, description and args_schema; custom
pydantic fields on your own tool class are not carried over.
OpenAI Agents SDK
Putreversi_function_tool under @function_tool. A ready FunctionTool is also accepted: its
on_invoke_tool(ctx, input_json) is wrapped and the parsed JSON arguments are journaled.
on_block="raise" the SDK hands the Blocked error to tool_error_formatter, which turns it into text for the
model (including the approval id). With the default "message" the tool simply returns that text.
guardrail(client, run) returns an input guardrail function that trips while the kill switch is on
(client.status()["halted"]). It also trips if Reversi cannot be reached.
agents.GuardrailFunctionOutput when that package is installed. The kill switch is also
enforced at every tool call, so the guardrail only stops a halted agent earlier.
FastMCP
reversible replaces @mcp.tool(): it wraps the function and then registers it. run_factory(ctx) returns the run
for the call; ctx is the tool’s Context parameter (named ctx or context) or None. per_session_runs is a
ready factory that opens one run per MCP session.
ReversiBlocked (with approval_id and reasons). FastMCP returns its message to the client as a
tool error, so the model sees the approval id. Pass extra mcp.tool arguments as keyword arguments to reversible,
or tool_name= when the Reversi manifest name differs from the function name.
Limits: sessions are keyed by the identity of ctx.session; a run is kept in memory for the life of the process.
Claude Code hook
reversi-claude-hook implements Claude Code’s PreToolUse and PostToolUse hooks. It opens one Reversi run per
Claude session (claude-<session_id>), asks Reversi before each tool call, and records the outcome afterwards.
Claude Code tool names are used as Reversi tool names (
Bash, Write, …), and mcp__server__tool becomes
server.tool. Register a manifest for each one you want to allow; an unknown tool is blocked by the server.
Install the package, export the configuration in the shell that starts Claude Code, and add the hooks:
.claude/settings.json:
Limits to know about:
askshows Claude Code’s own permission prompt. If the human says yes there, the tool runs even though the Reversi approval is still pending, and no step is recorded for that call. For a hard gate, usedenysemantics by not granting the tool, or have the approver decide first and let the agent retry.- The Post hook never blocks. If it cannot reach Reversi it prints a note on stderr and the step stays unresolved, which the server reports instead of hiding.
- Reversi can only undo what a manifest describes. Claude Code’s shell and file tools are usually
irreversibleoridempotent, so rollback will list them as not undoable rather than undo them. - Hook input fields (
tool_use_id,PostToolUseFailure) follow Claude Code’s documented format; without atool_use_idthe hook falls back to matching on the tool name and input.
Webhook signatures
If your Reversi server signs outbound webhooks, verify them with the raw body bytes before parsing:sha256=<hex>, the HMAC-SHA256 of the body under the shared secret. The comparison is
constant time, and a missing or malformed header returns False. The header name depends on the server
configuration; the helper only checks the value you pass.
Optional pydantic models
reversi_sdk.pydantic_models provides pydantic v2 ToolManifest and Compensation models with the same rules as
the standard library validator. Install with pip install 'reversi-sdk[pydantic]'. The SDK does not depend on pydantic
and the standard library validator (validate_manifest) stays authoritative; a test checks both against the shared
conformance cases.

