MCP server for radare2. Source lives in src/. Files named *.inc.c are #included into other .c files — they are not separate compilation units.
make -j— fast incremental build (from repo root)make asan— AddressSanitizer buildmake fmt— auto-format withclang-format-radare2src/r2mcp -h— CLI helpsrc/r2mcp -t— list available toolssrc/r2mcp -T 'open_file file_path="/bin/ls"; list_functions; close_file'— run DSL testsmake test— run test suite (test.sh,test2.sh)
- Tabs for indentation
- Space before parens:
foo (),if (,for ( - Always use braces
{}even for single-statement blocks - Declare loop variables before
for(C89 style) - Prefer
!strcmp ()overstrcmp () == 0 - Prefer
r_str_startswith ()over!strncmp () - Use
R_STR_ISEMPTY()/R_STR_ISNOTEMPTY()instead of manual null+empty checks - Use
R_RETURN_VAL_IF_FAIL()/R_RETURN_IF_FAIL()for preconditions inR_APIfunctions - Use
R_LOG_ERROR(),R_LOG_WARN(),R_LOG_INFO()for diagnostics
R_NEW()/R_NEW0()— assumed never to return NULLR_FREE(ptr)— frees and sets to NULL- No NULL-check before
free()or*_free()helpers r_str_newf()for formatted string allocation (not manual malloc+snprintf)RStrBuffor building strings in loops:RStrBuf *sb = r_strbuf_new (""); r_strbuf_appendf (sb, "...", ...); return r_strbuf_drain (sb); // caller frees
r_json_parse()does NOT own the input buffer — caller must free it separately afterr_json_free()
Build JSON responses with the pj_* helpers:
PJ *pj = pj_new ();
pj_o (pj); // open object
pj_ks (pj, "type", "text"); // key-string
pj_ki (pj, "count", 5); // key-int
pj_kb (pj, "ok", true); // key-bool
pj_k (pj, "items"); pj_a (pj); /* ... */ pj_end (pj); // key + array
pj_end (pj); // close object
return pj_drain (pj); // extract string, free builderParse with r_json_parse(), query with r_json_get() / r_json_get_str() / r_json_get_num(). Check types via field->type == R_JSON_STRING, etc.
Tool handler signature — returns heap-allocated JSON, caller frees:
static char *tool_example(ServerState *ss, RJson *tool_args) { ... }Register in tool_specs[] array in tools.c:
{ "tool_name", "description", "{\"type\":\"object\",\"properties\":{...}}", TOOL_MODE_NORMAL, tool_example },Tool modes are a bitmask: TOOL_MODE_MINI, TOOL_MODE_HTTP, TOOL_MODE_NORMAL, TOOL_MODE_RO, TOOL_MODE_SESSIONS, TOOL_MODE_FRIDA. Combine with |.
Implementation pattern:
- Validate params:
validate_required_string_param(),validate_address_param() - On failure:
return jsonrpc_error_missing_param("param_name"); - Execute r2 command:
r2mcp_cmd(ss, "cmd")orr2mcp_cmdf(ss, "cmd %s", arg) - Wrap result:
return tool_cmd_response(res);(wraps in JSON + freesres) - Or build custom response with
jsonrpc_tooltext_response(),jsonrpc_tooltext_response_paginated()
Use fx(ss) prefix for commands that need Frida mode support (returns ":" or "").
- JSON-RPC 2.0. Notifications (no
id) must not produce a response. - Use
jsonrpc_error_response()/jsonrpc_success_response()for building responses. r2_cmd_filter()strips dangerous patterns (!,$(...),|,`,>) from r2 commands.
- Use
R2__UNIX__/R2__WINDOWS__for platform-specific code. - No new dependencies — build only against radare2 headers (
r_core.h,r_util/*).