pub const DRIVER_SKILL: &str = "<!--\nSPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\nCopyright the ghidra-mcp authors. License: https://polyformproject.org/licenses/noncommercial/1.0.0\nThe canonical source-of-truth for the driver skill lives at `shared/ghidra-mcp/skill/SKILL.md`.\nEach agent plugin\'s binary embeds it at compile time (include_str!); regenerate that plugin\'s copy from the\nshipped binary with `<plugin-binary> emit-skill > SKILL.md` so it can never drift. Edit ONLY that canonical\nfile - if the copy you are viewing is under a plugin/CLI `skills/` dir, it is generated; do NOT edit it,\nyour changes will be overwritten on the next emit.\n-->\n---\nname: ghidra-re-driver\ndescription: Use when reverse-engineering, triaging, or making sense of an unfamiliar binary through the ghidra-mcp Ghidra tools - reading decompiled C, navigating a call graph, or judging whether a decompile can be trusted.\n---\n\n# Driving ghidra-mcp - read, triage & edit\n\n## Overview\n\nghidra-mcp attaches a persistent headless Ghidra to your session and exposes read/navigation tools plus\na set of durable, CAS-guarded write tools (`rename`, `comment`, `set_datatype`, `set_prototype`,\n`set_local`). This skill is the macro workflow that ties them together; per-tool parameters, error codes, and\nthe address format live in each tool\'s own description - read those for contracts, read this for the\nloop.\n\n**Core principle: narrow in the worker, then inspect one function, then hop to its neighbors.** Every\nlist tool takes a server-side `filter` so only matches cross the wire - never page through an entire\nsymbol table or string set to search client-side. `inspect_function` returns the decompiled C *plus the\nfunction\'s callers, callees, and data references inline*, so a single call answers \"what is this?\",\n\"where can I go next?\", and \"what does it touch?\" You navigate the call graph by re-inspecting a\nneighbor\'s address - you do not dump the whole program.\n\n## The tools (reads + writes: `rename`, `comment`, `set_datatype`, `set_prototype`, `set_local`)\n\n**Attach & inspect:**\n- `list_project_programs` - the programs (binaries) in the open project, as VFS paths. Start here to\n discover a `program_path`.\n- `attach_program` - attach to one program by its VFS path. A bootstrap program is already attached at\n startup, so you can often skip straight to inspecting.\n- `inspect_function` - the workhorse: decompile + describe one function (by name OR address), with\n callers/callees inline, the structured `prototype` (return type, params, calling convention, varargs -\n the read half of the `set_prototype` write pair), `locals[]` (each function LOCAL variable\'s `storage`\n handle, type, and name - the read half of the `set_local` write pair), plus the data/globals it\n references (`data_refs`, capped with `data_refs_truncated`), plus every comment anchored anywhere in the\n function\'s body - `comments[]`, each `{address,type,comment}`, capped with `comments_truncated`.\n\n**Discovery** (filters are a server-side substring match by default; pass `regex:true` for a\nlinear-time, length-capped regex; all paginated via `offset`/`limit` + `has_more`):\n- `find_functions` - list/search defined functions by a name filter; cheap metadata (name, entry_point,\n signature, calling_convention, is_thunk, size, external) - NOT the decompiled C or call edges, use\n `inspect_function` for those.\n- `list_symbols` - raw symbol-table entries, filterable by `kind` (import | export | defined | all) and\n by name.\n- `list_strings` - defined string data, filterable (e.g. `filter:\"https?://.*\", regex:true` to find\n URL-shaped strings without pulling every string across the wire).\n- `list_data_items` - defined data INSTANCES/globals, filterable by label. For type DEFINITIONS use\n `get_datatype`.\n- `list_segments` - the memory map (blocks: start/end/size + readable/writable/executable/initialized).\n Not paginated - segment counts are small.\n\n**Resolve & identify** (duals of each other):\n- `resolve_symbol` - a NAME -> its address(es); enumerates every match; 0 -> `SYMBOL_NOT_FOUND`.\n- `describe_address` - an ADDRESS -> what is here (segment, is_code, containing_function, symbol,\n data_type); total - an unmapped address returns `mapped:false`, never an error. Also carries a\n `comments` object keyed by type (`eol`/`pre`/`post`/`plate`/`repeatable` - present types only) when the\n address has any.\n\n**Deep read:**\n- `get_xrefs` - references `to` / `from` / `both` a name-or-address `target` (any symbol - function,\n data label, or string); enumerates candidates on an ambiguous name (`AMBIGUOUS_SYMBOL`).\n- `get_disassembly` - the instruction listing for a function (name-or-address target), paginated, with\n an exact `total`. Each instruction row carries a `comments` object keyed by type (present types only)\n when that address has any.\n\n**Low-level:**\n- `read_bytes` - raw bytes at an address (+ decimal `offset`), hex, clamped to 4096 (`truncated` flag).\n- `get_datatype` - a type DEFINITION by name (struct/union: members; enum: values); `AMBIGUOUS_DATATYPE`\n on a name collision - re-call with the category-path-qualified name from `details.candidates`. For\n defined data INSTANCES use `list_data_items`.\n\n**Write** (durable edits, all CAS-guarded and saved to disk immediately):\n- `rename` - rename the primary symbol (function or label) at an address. **Compare-and-swap:** pass\n `expected_name` = the symbol\'s CURRENT name; the edit applies only if it still matches, else\n `RENAME_CONFLICT` echoes the actual current name (re-read, then retry with that). If the symbol already\n equals `new_name` you get `status:already_applied` - a safe no-op, so re-issuing the same rename is\n harmless. The edit is **saved to disk immediately (durable)**. `new_name` must be a bare local name (no\n `::` namespaces); an invalid name -> `INVALID_PARAMS` with the exact reason in `details.reason`. `target`\n is an address or the symbol\'s current name.\n- `comment` - set or clear a comment on a code unit. `comment_type` is one of `eol` | `pre` | `post` |\n `plate` | `repeatable` (`plate` = the function/block header comment). An empty `new_comment` clears the\n comment. Idempotent: re-applying the same text returns `status:already_applied`. Pass\n `expected_comment` = the CURRENT comment text to guard against drift - the edit applies only if it still\n matches, else `STATE_CONFLICT` echoes the actual current text. Durable (saved to disk immediately).\n Rejects an offcut address - `target` must be a code unit\'s start.\n- `set_datatype` - apply a named data type to the DATA at an address. `datatype_name:\"undefined\"` clears\n the type (frees the space). Idempotent via type equivalence: re-applying an equivalent type returns\n `status:already_applied`. Types are resolved by name - an unresolvable name -> `DATATYPE_NOT_FOUND`, a\n name collision -> `AMBIGUOUS_DATATYPE` (re-call with the category-path-qualified name, same pattern as\n `get_datatype`). Pass `expected_datatype` = the CURRENT type name to guard against drift, else\n `STATE_CONFLICT`. Retyping a function PARAMETER is `set_prototype` (whole-signature); a function LOCAL\n variable is `set_local`. This tool is for DATA/globals. Durable (saved to disk immediately). Rejects\n a code address or an offcut address - `target` must be a data unit\'s start.\n- `set_prototype` - replace a function\'s WHOLE signature at once: return type, ordered parameters, calling\n convention, and varargs. Supply STRUCTURED fields, not a C string: `return_type`, `calling_convention`,\n `params` = an ordered list of `{name?, type}` (an empty `[]` means no arguments - do NOT pass\n `[{type:\"void\"}]`), and `variadic:true` for a trailing `...`. `return_type` and each `params[].type` are\n resolved by name (`DATATYPE_NOT_FOUND` / `AMBIGUOUS_DATATYPE`, same pattern as `get_datatype`);\n `calling_convention` must be one the program recognizes (unknown -> `INVALID_PARAMS` listing the valid\n set). Read the current signature as these SAME fields from `inspect_function`\'s structured `prototype`,\n change what you need, and send the whole thing back. Idempotent by signature equivalence\n (`status:already_applied`). Pass `expected_signature` = the current `signature` string to guard against\n drift, else `STATE_CONFLICT`. Durable (saved to disk immediately). **Not editable this way** - a thunk,\n an external import, or a function with custom variable storage (`__usercall` / explicit registers) is\n rejected with `INVALID_PARAMS`: do not retry it, leave that function\'s signature alone. A **function-pointer\n or otherwise complex declarator** as a param/return type (e.g. `void (*)(int)`) is NOT parseable ->\n `DATATYPE_NOT_FOUND`; give it a named `typedef` first (a pointer-to-named-type like `Rect *` IS fine).\n- `set_local` - retype and/or rename ONE function LOCAL variable (a decompiler body variable). Key it on\n the `storage` handle from `inspect_function`\'s `locals[]` (`stack:-0x18`), NOT the rendered name - the\n decompiler silently renames unlocked locals when the function\'s model shifts, so after any edit re-read\n `locals[]` and use the storage handle for the next edit. `new_type` and/or `new_name` (>=1). Idempotent;\n `expected_type`/`expected_name` guard drift. Stack locals are the durable, trustworthy handles; register/\n unique locals may be restricted (see the tool\'s INVALID_PARAMS). Durable (saved to disk).\n\n## The write loop\n\n1. **Read** - `inspect_function` / `describe_address` / `get_datatype` to see the current state.\n2. **Decide the edit** - the new name, comment text, or datatype.\n3. **CAS-write** - call `rename` / `comment` / `set_datatype`. Pass `expected_*` only to GUARD against\n concurrent drift (someone/something else changed it since your read) - on the normal single-agent path\n you don\'t need it; `already_applied` already makes re-issuing the same edit safe.\n4. **Verify** - re-read (`inspect_function` / `describe_address` / `get_datatype`) to confirm the edit\n landed as intended. A `comment` write closes this loop through all three read tools that surface\n comments: `describe_address` (the `comments` object at that address), `get_disassembly` (the same\n object on the covering instruction row), and `inspect_function` (the whole function body\'s comments in\n `comments[]`) - read back through whichever is already in hand instead of assuming the write \"stuck.\"\n\nThree write-time gotchas:\n- Expanding a datatype at an address can **mask** (not delete) an interior comment sitting at `addr+k` -\n the comment is still in the property map but no longer rendered once that offset is absorbed into the\n new, larger unit.\n- Re-applying the SAME dynamic-length type (e.g. `string`) to a location that already has it returns\n `already_applied` and does **not** resize it - idempotency is by type equivalence, which ignores\n instance length. To force a resize: `set_datatype(addr, \"undefined\")` first, then re-apply the type.\n- `comment` and `set_datatype` both reject an **offcut** address (an address that falls mid-unit rather\n than at its start) - always target a code/data unit\'s start address.\n\n## The loop\n\n1. **Discover** - `find_functions` / `list_symbols` / `list_strings` with a server-side `filter` to\n narrow before anything crosses the wire. The filter runs IN the worker; only matches come back -\n this is the difference between one cheap call and dumping the whole program. The `filter` is a\n **literal substring by default** - to use a regex pattern (`^sub_`, `https?://`) you MUST pass\n `regex:true`, or it is matched literally and you waste the call.\n2. **Resolve** - got a name but it\'s ambiguous, or a raw address and you don\'t know what it names?\n `resolve_symbol` turns a name into its address(es); `describe_address` turns an address into what\'s\n there (segment / function / symbol / data). Use whichever direction you\'re missing.\n3. **Deep read** - `inspect_function` for the decompiled C plus callers/callees/data_refs;\n `get_xrefs` to see who calls, reads, or writes a target; `get_disassembly` when you need the actual\n instructions (a jumptable, a manual calling-convention check) rather than the decompiler\'s model of\n them.\n4. **Low-level** - `read_bytes` for raw memory; `get_datatype` for a struct/union/enum layout;\n `list_segments` for the memory map; `list_data_items` for defined globals.\n\nWithin deep read, walk the call graph by hopping: `inspect_function(<a name, export, or entry-point\naddress>)` -> read the C, pick a caller or callee -> `inspect_function(<that neighbor\'s address>)` -> hop.\nRepeat: walk **callers** to answer \"who uses this?\" (trace a behavior up) or **callees** to answer \"what\nis this wrapper hiding?\" (drill down).\n\nTo reach a function you can\'t name, either start from one you *can* - an export, `main`, an entry\npoint - and walk the call graph from there, or use `find_functions`/`list_symbols` with a `filter` to\nfind it by a name pattern first.\n\n## Reading the results\n\n- **Addresses are canonical on the way out.** You may pass an address loosely; every result echoes ONE\n canonical form (default space -> `0x...`, other spaces -> `space:0x...`). Use that echoed string as the\n address\'s identity when correlating across calls - not your own spelling.\n- **`external: true` on a caller/callee** = an imported library function with no body. Identify it by\n name; do NOT feed it back to `inspect_function` - there is nothing to decompile.\n- **`is_thunk` / `thunked_to`** = a one-line forwarder. Don\'t spend a read on it; pivot to `thunked_to`.\n- **`AMBIGUOUS_SYMBOL` error** = the name matched several functions or symbols (from `inspect_function`,\n `get_xrefs`, or `get_disassembly`); it lists each candidate\'s address. Re-call with the disambiguating\n address - never guess which one. `resolve_symbol` is the standalone way to enumerate the candidates\n for a name up front.\n- **`c_truncated: true`** = the body was longer than `max_c_lines`; raise `max_c_lines` for the tail. The\n signature and top of the function are always intact.\n- **Every list tool paginates the same way** - pass `offset`/`limit`; if the reply\'s `has_more` is true,\n advance by adding `limit` to your current `offset` for the next call (`get_disassembly` additionally\n returns an exact `total` instruction count). `list_segments` is the one exception: it is not paginated,\n since segment counts are always small.\n\n## An empty result is a fact, not a verdict - and strings lie in Rust binaries\n\n`get_xrefs` returning no rows means one of two things - the target genuinely has no references, or you\nasked about an address nothing references - and a single call cannot tell you which. **Get a control:**\nrun the same call against something you know is referenced (a function with callers, an import pointer).\nIf the control returns rows, the empty answer is a fact about your target and the tool is fine.\n\nThe commonest way to ask about the wrong address is a **string in a Rust binary**, where the miss is\nsystematic rather than bad luck:\n\n- Rust `&str` constants carry their length at the use site, so the literals sit in `.rdata` as one run\n with **no NUL separators**: `GHIDRA_INSTALL_DIRGHIDRA_MCP_PROJECT_DIRGHIDRA_MCP_PROJECT_NAME...`\n- Ghidra\'s string analyser terminates on NUL, so it defines an item only where a NUL happens to fall.\n Some literals get no item at all; others get one whose boundaries are wrong, fusing neighbours or\n starting mid-word. Both of these are real items from one binary:\n `\"mid > lenC:\\...\\library\\alloc\\src\\collections\\btree\\node.rs\"` (a panic message welded to a source\n path) and ``\"V` but there is no `support/analyzeHeadless` there...\"`` (leading `` V` `` is the tail of\n the *previous* literal).\n- References are recorded against a **defined item\'s start address**. Ask about the address where your\n substring begins - which is usually mid-item, or in an undefined region - and you correctly get zero\n rows while the code uses that text constantly.\n\nSo when a string search comes back thin, or its address has no xrefs:\n\n1. `list_strings` with a `filter` may legitimately find nothing though the bytes are in the file. That is\n the analyser\'s boundary problem, not proof the string is absent.\n2. `describe_address` on the address you used - it reports what is actually defined there, its type and\n containing item. This is also how you catch having computed an address wrongly in the first place\n (a file offset is not a virtual address).\n3. Re-ask `get_xrefs` at the containing item\'s start.\n\nThe same caution applies to any language that packs constants without terminators, and to data the\nanalyser never typed. **When the fixture is the analysing tool\'s own binary**, add one more: its `.rdata`\nholds the very diagnostics the tool prints, so a string result and a live error message read identically.\nTell them apart by provenance - data has an address and arrives from `list_strings`/`read_bytes`.\n\n## Judge the decompile before you trust it\n\nGhidra\'s output is a *model*, and a wrong model still reads as plausible C. When you see these signals,\neither **correct the model** or **flag the function as low-confidence and say why** - don\'t silently reason\non a broken decompile. `set_datatype` corrects a data-instance type; a wrong function *signature* is the\nhighest-leverage fix - `set_prototype` (return type, parameters, calling convention, varargs) re-derives\nthe decompiled C and clears the `in_<reg>`/`extraout_<reg>` noise below. Two cautions: (1) if `set_prototype`\nreturns a custom-storage `INVALID_PARAMS`, that function is unsupported - leave its signature alone, do NOT\nretry-loop; (2) the structured `prototype` you read back reflects the **locked DB signature** - the\ndecompiled C may still show extra params the decompiler *guessed* but that aren\'t locked in the DB, so\njudge propagation by the C body, not by expecting the C to mirror the DB exactly.\n\n| Signal in the decompiled C | Likely cause |\n|---|---|\n| `in_<reg>` / `unaff_<reg>` variable | wrong function/callee signature, or a missing calling convention |\n| `extraout_<reg>` variable | a callee\'s return type is wrong (a value-returning call treated as void/wrong size) |\n| return `undefined[16]` or an obvious size mismatch | wrong return size/type on this function or a callee |\n| a local assigned a literal *code* address (`x = 0x00401a0`) | broken stack analysis, usually cascading from a bad signature or a non-returning callee |\n| garbage or looping disassembly right after a call | a callee that should be marked non-returning |\n| \"too many branches to recover a jumptable\" | an unrecovered switch/jumptable |\n\nReport the signal and its likely cause; don\'t silently build analysis on top of it.\n\n## Warm-up & crashes\n\n- **The first call after startup may return `WORKER_WARMING`** - the Ghidra JVM is still booting. Wait a\n few seconds and retry; do not hammer it.\n- If the worker crashes mid-session, the server transparently respawns and re-attaches - you see a\n latency spike, not a failure. A request that *reproducibly* crashes it is then blocked with a terminal\n error: change your selector rather than retrying the same thing.\n\n## What this tool set does NOT have\n\n`rename`, `comment`, `set_datatype`, `set_prototype`, and `set_local` are the writes you have.\n`set_prototype` sets a whole function SIGNATURE (return type + parameters + calling convention + varargs);\n`set_local` retypes/renames one function LOCAL variable, keyed on its `storage` handle from\n`inspect_function`\'s `locals[]`. There are **no** composite/graph tools - `get_control_flow`,\n`list_namespaces`, `trace_reachability`, `summarize_neighborhood` and `semantic_search` do not exist.\nNone of them are in your tool list; do not call them - they will dead-end. Where you need what they\nwould have given you, compose it from the primitives below.\n\nEverything else in \"The tools\" above - symbol search and listing, xrefs, strings, disassembly, and\nbyte/type/segment/data-item reads - **is available now**. Use them directly rather than assuming\nthey\'re missing or improvising a workaround via `inspect_function` alone.\n";Expand description
The driver skill, embedded at compile time (version-locked to this binary).