No description
  • Python 92.4%
  • Nix 7.3%
  • Shell 0.3%
Find a file
2026-07-14 04:43:06 -04:00
.gitignore feat: init 2026-07-13 15:48:09 -04:00
flake.lock feat: reuse 2026-07-13 16:06:28 -04:00
flake.nix feat: reuse 2026-07-13 16:06:28 -04:00
README.md docs: recommended config 2026-07-14 04:43:06 -04:00
run feat: init 2026-07-13 15:48:09 -04:00
server.py feat: reuse 2026-07-13 16:06:28 -04:00
test_server.py feat: reuse 2026-07-13 16:06:28 -04:00

Persistent Rizin MCP server

This MCP server keeps each opened binary in a long-lived rizin -q0 subprocess and can checkpoint analysis for later MCP processes. Checkpoints are keyed by the binary's SHA-256, so an unchanged binary can restore its analysis while a changed binary starts fresh.

It uses only the Python standard library. The Nix package supplies both Python and Rizin at runtime. The flake provides x86-64 and ARM outputs for Linux and macOS.

Run with Nix

nix run "git+https://git.kunet.dev/wslop/rizin-mcp"

The process speaks MCP over standard input/output and normally runs under an MCP host. Build and test the complete package with:

nix flake check "git+https://git.kunet.dev/wslop/rizin-mcp"

Register with Codex

codex mcp add rizin -- nix run "git+https://git.kunet.dev/wslop/rizin-mcp"

Then add a long tool timeout to the generated ~/.codex/config.toml entry so aaa is not cut off by Codex's 60-second default:

[mcp_servers.rizin]
tool_timeout_sec = 7200

Recommended configuration:

[mcp_servers.rizin]
command = "nix"
args = ["run", "git+https://git.kunet.dev/wslop/rizin-mcp"]
tool_timeout_sec = 7200
default_tools_approval_mode = "approve"

Restart the Codex session after registration. Live analysis remains resident for the lifetime of the MCP process; SHA-keyed checkpoints provide reuse after that process exits.

Typical workflow

  1. Always call rizin_acquire with the DLL path, session server, and required analysis aaa.
  2. If it returns state: ready, reuse the resident or restored analysis without running aaa.
  3. If it returns state: analysis_required, call rizin_analyze with level aaa, followed by rizin_checkpoint.
  4. Reuse rizin_command for the rest of the session. Prefer JSON commands such as aflj, isj, axtj, and pdfj, with parse_json enabled.
  5. Checkpoint again after meaningful renames, flags, comments, types, or other analysis changes.

rizin_acquire reports source: resident, checkpoint, or fresh, along with the available and required analysis levels. Multiple named sessions can remain open at once (for example, server and client). The binary is opened read-only.

Checkpoints default to ~/.rizin-mcp/checkpoints. Set RIZIN_MCP_STATE_DIR to choose another cross-platform location. A checkpoint created by a different Rizin version is reported as incompatible rather than loaded automatically; allow_incompatible can override that safeguard.

Rizin shell escapes are rejected by default. Set RIZIN_MCP_ALLOW_SHELL=1 in the MCP server's environment only if that behavior is explicitly needed. The default command timeout is one hour; override it with RIZIN_MCP_TIMEOUT_SECONDS or per rizin_command call.

Manual smoke test

Every stdio MCP message is one line of JSON:

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"smoke","version":"1"}}}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
  | nix run "git+https://git.kunet.dev/wslop/rizin-mcp"