# Redline Ghidra MCP

Canonical overview: https://api.redlineautohaus.com/docs/ghidra-mcp/

Source: https://github.com/amirdauti/redline-autohaus-ghidra-mcp

Ghidra MCP is an MIT-licensed Rust MCP server with a Java adapter for native Ghidra analysis. It exposes 85 typed tools through local stdio. A local file mailbox connects the Rust server to either a background Ghidra process or the CodeBrowser extension. This documentation site is not a hosted Ghidra endpoint.

The adapter supports project and program workflows, call/control-flow graphs, decompiler SSA tracing, typed variables and structures, batch inspection, function matching, isolated emulation, annotations, and explicit saving. It does not automatically identify calibration maps, establish their units or scaling, patch firmware, or flash an ECU.

## Requirements and validation status

- Ghidra 12.1.3 PUBLIC and JDK 21. Build the Java adapter against the same Ghidra installation/version you will run.
- Rust 1.88.0, pinned by the source repository, to build the server.
- PowerShell on Windows for the documented setup. A shell build helper is also included in the repository.
- Node.js only for the optional native acceptance test; normal server operation does not require Node.js or Python.
- Your own Ghidra and Java installations. Neither is bundled.

The repository records native headless acceptance on Windows with Ghidra 12.1.3 and JDK 21, using generated synthetic binaries. It covers real import, x86 decompilation, metadata changes, analysis, saving, export, and close/reopen. A separate native GUI harness exercises the actual extension, program selection, annotations, save, and navigation in a hidden CodeBrowser window. It does not automate the installation/configuration dialogs or certify every processor, analyzer, firmware image, or ECU layout.

Transport tests with a synthetic mailbox peer are separate from native acceptance. The synthetic TriCore import fixture validates a selected language/compiler and bytes. Separate generated arithmetic fixtures exercise isolated emulation; they do not establish whole-ECU emulation, TC298 raw-import support, peripheral behavior or a complete firmware model. Read the current limitations below before importing raw firmware.

Acceptance scope: https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/docs/live-acceptance.md

## Build on Windows

The paths below are placeholders. Substitute your own absolute paths.

```powershell
git clone https://github.com/amirdauti/redline-autohaus-ghidra-mcp.git
cd redline-autohaus-ghidra-mcp
cargo build --locked --release
.\scripts\build-extension.ps1 -GhidraHome 'C:\tools\ghidra_12.1.3_PUBLIC' -JavaHome 'C:\tools\jdk-21'
```

The MCP executable is `target/release/ghidra-mcp.exe`. The extension ZIP and `latest-build.json` are written under `dist/`. The build metadata identifies the adapter JAR; use that actual JAR path when configuring automatic startup. The bridge launcher can read `dist/latest-build.json` when `-JarPath` is omitted.

## Start the background bridge

Create private directories for projects, inputs, and the mailbox. Project and input directories must exist before startup.

```powershell
New-Item -ItemType Directory -Force -Path 'C:\GhidraWork\projects', 'C:\GhidraWork\inputs', 'C:\GhidraWork\mailbox'
.\scripts\start-bridge.ps1 `
  -GhidraHome 'C:\tools\ghidra_12.1.3_PUBLIC' `
  -JavaHome 'C:\tools\jdk-21' `
  -Mailbox 'C:\GhidraWork\mailbox' `
  -ProjectRoot 'C:\GhidraWork\projects' `
  -ImportRoot 'C:\GhidraWork\inputs'
.\target\release\ghidra-mcp.exe --bridge-dir 'C:\GhidraWork\mailbox' --doctor
```

The launcher starts Java in the background and reports its PID and log paths. It does not create a project or import an image. Create/open paths are restricted to the configured project root; imported files must resolve under an allowed import root. Multiple import roots can be supplied as a PowerShell array. Existing project markers, storage, program names, and export files are not overwritten.

`--doctor` prints one JSON status and exits. Without it, the executable serves MCP over stdin/stdout. Diagnostic output belongs on stderr. A silent process waiting for MCP input is expected.

## Connect a local MCP client

Register a stdio server using your client's configuration format and these launch settings. Use the same mailbox as the Java bridge and replace the executable path with the absolute build output path.

```json
{
  "command": "C:\\tools\\ghidra-mcp\\ghidra-mcp.exe",
  "args": ["--bridge-dir", "C:\\GhidraWork\\mailbox", "--timeout-ms", "45000"]
}
```

Do not add `--doctor` to the persistent MCP connection. Allow a client tool timeout of at least 60 seconds for the documented 45-second bridge deadline when the bridge is already running; use 120 seconds for automatic startup. Each mailbox permits one bridge and one MCP client.

### Optional automatic startup on Windows

Add `--launch-config` and the absolute path to a launch JSON file to the server arguments. MCP initialization and tool discovery complete before Java starts. The first native tool call starts Ghidra when needed, so allow a client tool timeout of 120 seconds for cold startup plus the native request. A client startup timeout covers MCP initialization only. The same launch option works with `--doctor`.

An offline bridge or a mailbox owned by another client produces a tool error while the tool catalogue remains available. After starting the bridge or disconnecting its owning client, call `ghidra_status` again in the same MCP session. Interrupted native startup, stale mailbox evidence or an uncertain dispatched request requires the recovery procedure before restarting. Do not treat a startup timeout as proof that Java did not start.

```json
{
  "launcher_script": "C:\\tools\\ghidra-mcp\\scripts\\start-config.ps1",
  "ghidra_home": "C:\\tools\\ghidra_12.1.3_PUBLIC",
  "java_home": "C:\\tools\\jdk-21",
  "adapter_jar": "C:\\tools\\ghidra-mcp\\RedlineGhidraMcp.jar",
  "bridge_dir": "C:\\GhidraWork\\mailbox",
  "project_root": "C:\\GhidraWork\\projects",
  "import_roots": ["C:\\GhidraWork\\inputs"]
}
```

All paths are examples. Point `adapter_jar` to the JAR recorded by your build, and keep `start-config.ps1`, `start-bridge.ps1`, and `common.ps1` together. `bridge_dir` must match `--bridge-dir`. Omit `--launch-config` for a manually started bridge or the GUI extension.

The launcher reuses a running bridge but refuses pending state from an interrupted exchange. Disconnecting the MCP client leaves the background Java process running; unsaved work remains only in that process until explicitly saved.

Source examples: https://github.com/amirdauti/redline-autohaus-ghidra-mcp/tree/main/examples

## GUI extension

1. Install the generated ZIP through Ghidra's **File > Install Extensions**, then restart Ghidra. Alternatively, use the repository's `scripts/install-extension.ps1` with the exact target version.
2. Open a project and CodeBrowser. Enable **RedlineMcpPlugin** through **File > Configure**.
3. Choose **Tools > Redline MCP > Start bridge...** and supply the mailbox, project root, and allowed import directories.
4. Connect the MCP client to that mailbox without automatic background startup. Call `ghidra_status` and inspect `capabilities` before operating.

The same 85 tool names are discoverable in both modes, but actual capabilities differ:

| Operation | Background mode | GUI mode |
| --- | --- | --- |
| Create/open/close a project | Supported | Returns unsupported; use Ghidra UI or background workflow |
| Import/select programs, inspect, annotate, analyze, save, export | Supported within documented limits | Supported within documented limits in the active CodeBrowser |
| `ghidra_go_to` | Returns unsupported | Navigates the selected program's CodeBrowser cursor |

To hand a background project to the GUI, finish analysis, explicitly save each changed program, and call `ghidra_close_project`. Then open its `.gpr` in Ghidra. A project already owned by another process is not taken over. Use separate mailboxes or run the bridges at different times. Stopping the GUI bridge leaves GUI-owned programs open.

## Identity, addresses, and raw import limits

- Project and program `id` fields are opaque runtime guards. Obtain them from the bridge; do not invent IDs, use source hashes as IDs, or reuse them after reopening without checking.
- Project operations use `expected_project_id`; program operations use `expected_program_id` and verify the active program. These checks do not lock out independent GUI edits to the same program.
- `source_sha256` describes the imported source file. It is not a hash of current Ghidra memory. Use `ghidra_read_bytes` or `ghidra_hash_memory` to verify the current bytes relevant to your work.
- `program_path` is a project domain path such as `/Synthetic`, not a filesystem path.
- CPU addresses include their address space, such as `ram:00400000`. File offsets are nonnegative integers, such as `128`. They are different coordinates.
- Raw import requires explicit `language_id`, `compiler_spec_id`, and hexadecimal `image_base`. Obtain compatible choices from `ghidra_list_languages`. Selecting a processor does not infer an ECU's memory layout.
- Current raw import accepts a 1-byte to 512-MiB file and a byte-addressed language only. The native loader must initially produce exactly one initialized block at zero, the size of the input. The adapter then relocates that block to the requested image base and saves the newly imported program.
- **TC29x limitation:** `tricore:LE:32:tc29x` supplies processor-default memory blocks, so the current raw-import guard rejects that multiple-block layout. The tool does not automatically load TC298 firmware, split flash banks, or create mapped cached/uncached aliases. Prepare complex layouts separately in Ghidra and inspect their resulting blocks and bytes before analysis.
- `ghidra_create_memory_block` adds uninitialized memory, useful for modeled RAM. It does not load another part of a file, split an existing flash block, or create a byte-mapped alias. `ghidra_set_image_base` relocates an image; it does not establish a complex memory map.
- `ghidra_map_file_offset` requires exactly one stored source-file record and returns the addresses reported by Ghidra's file-offset lookup, with provenance and an `ambiguous` flag. Multiple direct mappings are exposed. Transitive byte-mapped aliases may be absent: a cached file-backed address can be returned without its uncached alias. A single result or `ambiguous: false` is not proof that no alias exists. Inspect memory blocks and verify bytes at each relevant CPU address.

Implementation contract: https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/docs/bridge-protocol.md

## Tool reference

All names below are complete MCP tool names. There are **85 tools**, with **84 supported headlessly**; call `ghidra_status` for active-mode capabilities. Optional fields are marked `?`. Every program operation requires a fresh `expected_program_id`, and project operations use `expected_project_id`. Nested descriptors/selectors and numeric limits are covered in [Research limits and evidence](#research-limits-and-evidence) and the linked native contracts.

### Context and projects

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_status` | None | Mode, version, active context, capabilities |
| `ghidra_list_languages` | None | Installed language IDs, processor/endian/size, compatible compiler IDs |
| `ghidra_get_project` | None | Active project's runtime ID, name, path |
| `ghidra_get_program` | None | Active program ID, project ID, domain path, language/compiler, source SHA-256, image base, blocks, changed state |
| `ghidra_create_project` | `path`, `name` | Create under an existing allowed parent directory; reject collisions; background only |
| `ghidra_open_project` | `path`, `name` | Open an existing project; refuse switching while one is open; background only |
| `ghidra_close_project` | `expected_project_id` | Close; refuse unsaved programs or active analysis; background only |
| `ghidra_list_programs` | `expected_project_id` | Recursive project domain paths and names |

### Programs and persistence

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_import_program` | `expected_project_id`, `path`, `name`, `language_id`, `compiler_spec_id`, `image_base` | Raw file import with the restrictions above; simple program name, no overwrite |
| `ghidra_select_program` | `expected_project_id`, `program_path` | Select an existing program; obtain its returned identity |
| `ghidra_save_program` | `expected_program_id` | Explicitly save and verify changed state |
| `ghidra_export_program` | `expected_program_id`, `path` | Export a Ghidra packed program (`.gzf`), including analysis, to a new path under the project root; not a flashable BIN |

### Analysis jobs

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_get_analysis_options` | `expected_program_id` | Read current analyzer option names, types, values |
| `ghidra_set_analysis_options` | `expected_program_id`, `options` | Update existing Boolean options only; reject unknown or non-Boolean options |
| `ghidra_analyze` | `expected_program_id` | Start native background analysis; return a job ID |
| `ghidra_job_status` | `job_id` | Poll state and any error |
| `ghidra_cancel_analysis` | `job_id` | Request cancellation; keep waiting until native work has stopped |

### Memory

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_read_bytes` | `expected_program_id`, `address`, `count` | Current memory bytes; 1–4,096 bytes |
| `ghidra_map_file_offset` | `expected_program_id`, `file_offset` | File-backed address matches and provenance; observe alias limitation above |
| `ghidra_set_image_base` | `expected_program_id`, `address` | Relocate image and read back program details |
| `ghidra_create_memory_block` | `expected_program_id`, `name`, `address`, `size`, `read`, `write`, `execute` | Add an uninitialized block; 1–67,108,864 bytes; reject overlaps |

### Code and functions

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_list_functions` | `expected_program_id`, `offset?`, `limit?` | Defaults 0/50; limit 1–200 |
| `ghidra_get_function` | `expected_program_id`, `address` | Containing function, signature, parameters, bounded callers/callees |
| `ghidra_create_instructions` | `expected_program_id`, `address`, `length` | Decode 1–65,536 bytes in initialized memory; do not clear existing data/code |
| `ghidra_create_function` | `expected_program_id`, `address`, `name?` | Create from decoded instructions using native flow detection |
| `ghidra_rename_function` | `expected_program_id`, `address`, `name` | Rename a defined function and read back |
| `ghidra_disassemble` | `expected_program_id`, `address`, `count` | Read 1–200 existing listing instructions; does not create instructions |
| `ghidra_decompile` | `expected_program_id`, `address` | Native C output for a defined containing function; 30-second timeout, 1-MiB text cap |

### Search and annotations

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_get_references` | `expected_program_id`, `address`, `direction?`, `limit?` | `to` or `from`; defaults `to`/100; limit 1–500 |
| `ghidra_search_bytes` | `expected_program_id`, `pattern`, `limit?` | Exact 1–256-byte hexadecimal pattern; default 100, maximum 500 results |
| `ghidra_list_symbols` | `expected_program_id`, `query?`, `offset?`, `limit?` | Substring filter; defaults 0/100, maximum 500 per page |
| `ghidra_list_strings` | `expected_program_id`, `offset?`, `limit?` | Existing defined string data; defaults 0/100, maximum 500 per page |
| `ghidra_define_data` | `expected_program_id`, `address`, `type_name`, `count` | Primitive scalar/array in undefined storage; `u8`, `s8`, `u16`, `s16`, `u32`, `s32`, `u64`, `s64`, `f32`, `f64`; count 1–65,536 |
| `ghidra_set_label` | `expected_program_id`, `address`, `name` | Set a user-defined symbol and read back |
| `ghidra_set_comment` | `expected_program_id`, `address`, `comment` | Set an EOL comment; maximum 8,192 characters; read back |
| `ghidra_go_to` | `expected_program_id`, `address` | Navigate the active GUI CodeBrowser; unsupported in background mode |

### Stored analysis inspection

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_get_comments` | `expected_program_id`, `address` | Read EOL, pre, post, plate and repeatable comments; each capped at 8,192 characters with truncation flags. |
| `ghidra_get_data` | `expected_program_id`, `address`, `component_offset?`, `component_limit?` | Inspect existing containing data and paginated immediate components. Defaults 0/32; maximum 128 components; large values explicitly omitted. |
| `ghidra_get_pcode` | `expected_program_id`, `address`, `count` | Read raw P-code for 1–200 contiguous existing instructions; at most 2,048 operations/8,192 varnodes. Ignores flow overrides; not decompiler SSA. |

### Function graphs and instruction searches

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_get_function_details` | `expected_program_id`, `address`, `limit?` | Inspect existing function ranges, convention, parameters, locals and exact native register/stack storage; bounded read-only metadata. |
| `ghidra_get_control_flow` | `expected_program_id`, `address`, `max_blocks?`, `max_edges?` | Inspect native basic blocks and typed control-flow edges of an existing function, with explicit bounded truncation. |
| `ghidra_get_call_graph` | `expected_program_id`, `address`, `direction?`, `max_depth?`, `max_nodes?`, `max_edges?` | Traverse existing native call references toward callers, callees or both; bounded depth, nodes, edges and scan budget. |
| `ghidra_find_call_paths` | `expected_program_id`, `source`, `target`, `max_depth?`, `max_paths?`, `max_nodes?`, `max_edges?` | Find bounded simple call paths between existing functions using resolved native call references; no dynamic-target inference. |
| `ghidra_search_constants` | `expected_program_id`, `value`, `scalar_bits?`, `start?`, `end?`, `cursor?`, `limit?`, `scan_limit?` | Search scalar instruction operand bit patterns in existing instructions, with optional CPU range, bounded scans and cursor continuation. |
| `ghidra_search_instructions` | `expected_program_id`, `mnemonic?`, `operand_contains?`, `start?`, `end?`, `cursor?`, `limit?`, `scan_limit?` | Search existing instructions by exact mnemonic and literal operand substring; bounded CPU-address cursor pagination. |
| `ghidra_search_pcode` | `expected_program_id`, `opcode`, `start?`, `end?`, `cursor?`, `limit?`, `scan_limit?` | Search existing instruction raw P-code by exact native opcode; bounded scan and CPU-address continuation, without decompilation. |
| `ghidra_get_references_range` | `expected_program_id`, `start`, `end`, `direction?`, `cursor?`, `cursor_offset?`, `limit?`, `scan_limit?` | Inspect native references sourced from or targeting an inclusive CPU range, with exact address/reference-index continuation. |

### Decompiler data flow, batches and matching

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_get_high_pcode` | `expected_program_id`, `address`, `offset?`, `limit?` | Read bounded decompiler SSA operations and varnodes with a content snapshot ID and operation pagination; no listing changes. |
| `ghidra_trace_data_flow` | `expected_program_id`, `address`, `expected_snapshot_id`, `operation_address`, `operation_time`, `operand_index`, `direction`, `max_depth?`, `max_nodes?` | Trace a snapshot-validated SSA varnode forward or backward within one function, bounded by depth and nodes; calls and memory boundaries stay explicit. |
| `ghidra_batch_decompile` | `expected_program_id`, `addresses` | Decompile up to eight requested functions with per-item outcomes, text caps and native time bounds. |
| `ghidra_batch_references` | `expected_program_id`, `addresses`, `direction`, `limit_per_address?` | Read bounded native references for up to 32 addresses with explicit per-address truncation. |
| `ghidra_search_decompiled_code` | `expected_program_id`, `text`, `start_after?`, `function_limit?` | Search literal case-sensitive text in a bounded window of decompiled functions; return per-function outcomes and an exclusive resume cursor. |
| `ghidra_get_function_fingerprint` | `expected_program_id`, `address`, `max_instructions?` | Hash a bounded normalized instruction sequence; constants and addresses are abstracted and semantic equivalence is not implied. |
| `ghidra_compare_functions` | `expected_program_id`, `address`, `other_address`, `max_instructions?` | Compare two functions in the same active program using normalized instruction fingerprints and token similarity; no semantic equivalence claim. |
| `ghidra_find_similar_functions` | `expected_program_id`, `address`, `start_after?`, `function_limit?`, `result_limit?`, `max_instructions?` | Rank a bounded, resumable window of functions by normalized instruction-token similarity; candidates require manual verification. |
| `ghidra_emulate_function` | `expected_program_id`, `address`, `stop_address`, `registers?`, `memory?`, `output_registers`, `output_memory?`, `max_steps?` | Run an isolated emulator from a defined function entry to an explicit stop address with bounded steps/time and explicit register/memory inputs; program bytes are never committed. |

### Variables and function signatures

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_get_function_variables` | `expected_program_id`, `address`, `offset?`, `limit?` | List bounded decompiler variables with exact name, symbol ID and serialized-storage selectors; reports current signature and valid calling conventions. |
| `ghidra_rename_variable` | `expected_program_id`, `address`, `selector`, `name` | Rename exactly one decompiler variable selected by ID, current name and storage; commit and verify the database variable in one transaction. |
| `ghidra_set_variable_type` | `expected_program_id`, `address`, `selector`, `data_type` | Set a selected decompiler variable to a bounded typed descriptor of the same storage size; reject stale or ambiguous selectors. |
| `ghidra_set_function_signature` | `expected_program_id`, `address`, `expected_signature`, `return_type`, `parameters`, `calling_convention`, `varargs` | Replace a function signature with typed parameters and return type, guarded by its exact current signature; use native dynamic storage and a known calling convention. |

### Structures and native data types

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_list_data_types` | `expected_program_id`, `query?`, `offset?`, `limit?` | List a bounded page of program data types, optionally matching a type-path substring. |
| `ghidra_get_data_type` | `expected_program_id`, `path` | Inspect an exact program type path, including bounded structure/union fields, enum members, or referenced type metadata. |
| `ghidra_create_structure` | `expected_program_id`, `path`, `size`, `fields` | Create a new fixed-size structure with explicit nonoverlapping offsets; reject existing type names and out-of-range fields. |
| `ghidra_set_structure_field` | `expected_program_id`, `path`, `offset`, `expected_name`, `expected_type_path`, `name`, `data_type`, `comment?` | Rename or replace exactly one existing structure field, guarded by its current name and type path; preserve its size and surrounding layout. |
| `ghidra_create_enum` | `expected_program_id`, `path`, `size`, `members` | Create a new 1/2/4/8-byte enum with bounded signed integer values and unique member names; reject name conflicts. |
| `ghidra_create_union` | `expected_program_id`, `path`, `fields` | Create a new union from bounded named typed members; reject conflicts and duplicate names. |
| `ghidra_create_typedef` | `expected_program_id`, `path`, `data_type` | Create a new typedef to a bounded builtin, named type, pointer, or array; reject name conflicts. |
| `ghidra_apply_data_type` | `expected_program_id`, `address`, `data_type` | Apply a bounded typed descriptor only to undefined mapped storage; preserve existing code/data and firmware bytes. |
| `ghidra_get_structure_field_references` | `expected_program_id`, `address`, `path`, `field_offset`, `limit?` | Find bounded, verified high-P-code PTRSUB field-pointer derivations in one function for an exact structure and byte offset; explicitly partial, not a program-wide reference index. |

### Listing, memory and processor context

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_get_listing` | `expected_program_id`, `address`, `length`, `limit?` | Inspect existing code, data and undefined storage in a bounded mapped range, with a CPU-address continuation. |
| `ghidra_hash_memory` | `expected_program_id`, `address`, `length` | Compute SHA-256 of up to 16 MiB of current initialized program memory, distinct from imported source-file hash. |
| `ghidra_preview_instructions` | `expected_program_id`, `address`, `count` | Decode sequential candidate instructions with native processor context without creating listing instructions; reports delay slots and decode failures. |
| `ghidra_get_processor_context` | `expected_program_id`, `address`, `register?`, `offset?`, `limit?` | Inspect a bounded register page and recorded context values at an address; these are analysis values, not live CPU registers. |
| `ghidra_set_processor_context` | `expected_program_id`, `address`, `length`, `register`, `value` | Set and read back a register context value over a mapped range without defined instructions; transactionally updates analysis metadata. |
| `ghidra_clear_listing` | `expected_program_id`, `address`, `length`, `expected_kind` | Clear complete code/data units in a bounded range with an expected-kind guard; refuses functions and verifies bytes remain unchanged. |

### Bookmarks and annotation batches

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_list_bookmarks` | `expected_program_id`, `offset?`, `limit?` | List a bounded page of native bookmarks and their categories, comments and addresses. |
| `ghidra_set_bookmark` | `expected_program_id`, `address`, `bookmark_type`, `category`, `comment`, `expected_comment?` | Create or update a native bookmark guarded by its existing comment and verify readback in a transaction. |
| `ghidra_delete_bookmark` | `expected_program_id`, `address`, `bookmark_type`, `category`, `expected_comment` | Remove one exact bookmark only when its expected comment matches, and verify deletion. |
| `ghidra_list_comments` | `expected_program_id`, `address`, `length`, `comment_type`, `query?`, `limit?` | Search one stored comment type by literal text over a bounded address range, with scan limits and continuation. |
| `ghidra_batch_set_comments` | `expected_program_id`, `updates` | Apply up to 64 guarded comment edits across all five stored types atomically, with exact readback and rollback on any mismatch. |
| `ghidra_get_function_tags` | `expected_program_id`, `address` | Read native tags at an exact function entry address. |
| `ghidra_update_function_tags` | `expected_program_id`, `address`, `add`, `remove` | Add or remove bounded function tags transactionally and verify the final tag set. |
| `ghidra_batch_rename` | `expected_program_id`, `updates` | Rename up to 64 functions or primary labels atomically using exact expected-name guards; verify every result. |

### Saved-program comparisons

| Tool | Parameters | Result or behavior |
| --- | --- | --- |
| `ghidra_compare_program_memory` | `expected_program_id`, `address`, `other_program_path`, `expected_other_source_sha256`, `other_address`, `length`, `limit?` | Compare bounded current memory against a guarded saved program in the same project, returning hashes and changed byte ranges without switching programs. |
| `ghidra_compare_saved_function` | `expected_program_id`, `address`, `other_program_path`, `expected_other_source_sha256`, `other_address`, `max_instructions?` | Compare an active function with a guarded saved program function without switching programs. Requires matching language/compiler; normalized similarity is not proof of semantic equivalence. |

## Research limits and evidence

All 85 tools are discoverable in both modes. Headless supports 84; GUI navigation requires CodeBrowser. The catalogue includes stored-comment, data-definition and raw P-code readers alongside the graph, SSA and typed-metadata tools. MCP `tools/list` supplies exact nested schemas and defaults. The native contracts below describe supported fields and restrictions.

- [Research tool contract](https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/docs/research-tools.md)
- [Flow tool contract](https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/docs/flow-tools.md)
- [Type tool contract](https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/docs/type-tools.md)
- [Utility tool contract](https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/docs/utility-tools.md)
- [Headless coverage](https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/docs/headless-tools.md)

| Family | Request bounds and interpretation |
| --- | --- |
| Function details and graphs | Details default to 128 items per range/variable list, maximum 256. Control flow defaults to 128 blocks/512 edges, maximum 256/2,048. Call graph depth defaults to 2, maximum 8; path depth defaults to 8, maximum 16, with 10 paths by default and a maximum of 64. Both use 128 nodes/512 edges by default, maximum 256/2,048. Five-second cooperative query budget; traversal capped at 100,000 steps. Existing resolved call references only; absent edges do not prove absent runtime behavior. |
| Instruction and reference search | Instruction searches default to 100 matches and 20,000 scanned instructions, maximum 200/100,000. Optional start/end must be supplied together; cursor resumes unprocessed instructions. Mnemonics match exactly ignoring ASCII case; operand filters are case-sensitive literal text. Scalar values are unsigned hexadecimal bit patterns. Raw P-code opcode names are uppercase. Reference ranges default to 100 results, maximum 500; preserve returned cursor and cursor_offset together. Truncation and continuation are explicit. |
| SSA and value tracing | High P-code pages default to 128 operations, maximum 512, from at most 20,000 operations/65,536 varnodes. Decompiler timeout is 10 seconds. Trace requires a fresh snapshot ID and operation address/time plus operand index (-1 selects output). Defaults are depth 4/64 nodes, maximum 8/128, with at most 512 edges. Memory, calls and indirect side effects are boundaries; no interprocedural or memory-alias analysis. |
| Batches and text search | Decompile 1–8 addresses with per-item outcomes, four seconds per function and 65,536 UTF-16 units of C; batch budget is 40 seconds. Reference batches accept 1–32 addresses and default to 100 references each, maximum 200. Literal decompiled-text search scans 4 functions per window by default, maximum 8, and returns at most 20 matches per function; only the capped C text is searched. |
| Function matching | Fingerprints default to 1,024 instructions, maximum 4,096, and abstract constants/addresses. Similar-function search ranks only its current window of 25 functions by default, maximum 100, with 10,000 candidate instructions and a 10-second budget. Returns 10 results by default, maximum 25. Equal fingerprints or high scores are candidate evidence, not semantic equivalence. |
| Typed metadata | Type descriptors support builtins, exact native paths, pointers and arrays, nested at most four levels; fixed objects are limited to 1 MiB and arrays to 1–65,536 elements. Structures/unions allow 256 fields; enums allow 256 members of 1/2/4/8-byte width. Function signatures allow 64 parameters. Variable selectors include ID, current name and storage; inferred parameters need an explicit committed signature first. Edits preserve unrelated variables and roll back unexpected helper effects. |
| Listing and annotations | Listing/context/clear ranges are limited to 65,536 bytes; listing pages default to 128 entries, maximum 256. Current-memory SHA-256 covers up to 16 MiB. Preview decodes 1–200 instructions without creating a listing. Comment and rename batches contain 1–64 guarded edits, committed atomically. Type and annotation mutations change metadata, require explicit saving, and do not patch bytes. Clear-listing refuses partial units/functions and can remove derived references. |
| Saved comparisons | Compare at most 1 MiB against an explicitly identified saved program in the same project; default 128 changed ranges, maximum 256. Source-hash guard identifies that imported file; returned region hashes describe actual compared bytes. The active program is not switched. Unsaved edits in the other program are excluded. |
| Isolated emulation | Explicit function entry, existing stop instruction, register/memory inputs and declared outputs; default 1,000 steps, maximum 10,000, with a five-second budget. Up to 32 input/16 output registers of at most 64 bits, 16 input/output memory regions and 8,192 bytes in each direction. No program writes are committed, no ABI setup is inferred, and uninitialized reads or unsupported operations fail. No live debugger, ECU, operating system or peripheral model is supplied. |

Stored processor context is analysis metadata, not live CPU state. Raw instruction P-code and decompiler SSA are different views. Preview, search, comparison and emulation remain read-only; typed signatures and annotations require transactional readback and explicit saving. Preserve truncation, per-item errors and unresolved boundaries when recording conclusions.

## First synthetic workflow

Create a six-byte x86 fixture under your allowed import directory. These bytes encode `mov eax, 42; ret`; they are not ECU firmware. `CreateNew` refuses to replace an existing file.

```powershell
$fixturePath = 'C:\GhidraWork\inputs\synthetic-answer.bin'
$fixture = [byte[]](0xB8, 0x2A, 0x00, 0x00, 0x00, 0xC3)
$fixtureStream = [IO.File]::Open($fixturePath, [IO.FileMode]::CreateNew)
try { $fixtureStream.Write($fixture, 0, $fixture.Length) } finally { $fixtureStream.Dispose() }
```

1. Call `ghidra_status` and confirm the expected mode/capabilities.
2. Call `ghidra_list_languages`. For this fixture, verify `x86:LE:32:default` and a compatible compiler such as `gcc` are present.
3. In background mode, create a new project with `ghidra_create_project`, `path: C:\GhidraWork\projects`, and a unique `name`, such as `SyntheticAnalysis`. Retain its returned `id`. In GUI mode, get the existing project's ID.
4. Call `ghidra_import_program` with the arguments below, replacing the placeholder ID and confirming the compiler against the catalog.

```json
{
  "expected_project_id": "<returned-project-id>",
  "path": "C:\\GhidraWork\\inputs\\synthetic-answer.bin",
  "name": "Synthetic",
  "language_id": "x86:LE:32:default",
  "compiler_spec_id": "gcc",
  "image_base": "00400000"
}
```

5. Retain the returned program ID. Read six bytes at `ram:00400000` with `ghidra_read_bytes` and confirm they match the fixture. Map file offset `0` with `ghidra_map_file_offset` and inspect the program's memory blocks.
6. Call `ghidra_create_instructions` at that address with `length: 6`, then `ghidra_create_function` with `name: synthetic_answer`.
7. Call `ghidra_decompile` at that address. The repository's native x86 acceptance fixture checks the corresponding function for the value 42; generated C formatting may vary.
8. Call `ghidra_save_program`. In background mode, close/reopen the project, select `/Synthetic`, obtain its new runtime ID, and verify bytes and function metadata again.

The repository's optional full acceptance test generates its own larger fixtures. Run it in a separate test mailbox/project root, with its work directory allowed for both projects and imports:

```powershell
node tests/live-ghidra.mjs --server target/release/ghidra-mcp.exe --bridge-dir C:\GhidraTest\mailbox --work-dir C:\GhidraTest\projects
```

## Analysis and saving

Inspect analysis options before calling `ghidra_analyze`; updates currently accept only existing Boolean options. Poll the returned `job_id` using `ghidra_job_status`. The current implementation reports `queued`, `running`, `completed`, or `failed`; cancellation can be reported as `failed` with a cancellation error. The bridge requests cancellation after five minutes. An analyzer that ignores cancellation can remain busy longer.

`ghidra_cancel_analysis` acknowledges a request, not completion. Continue polling. A job may complete before the request arrives. Cancellation can leave partial analysis; it does not roll back work. Busy guards prevent program mutations, selection, and closure while analysis is active. Independently started GUI analysis is detected but is not owned by the MCP cancellation tool.

Import persists the initial program. Later analysis and annotations require `ghidra_save_program`. A connected process, an analysis completion, or client disconnection is not a save. Use `ghidra_get_program` to inspect `changed`, then save and verify. Export creates a separate packed `.gzf`; save the working program explicitly as well.

## WinOLS handoff

Keep the Ghidra program identity, original source SHA-256, current relevant bytes, memory mapping, and the corresponding WinOLS project/version identity together. Match the intended Original or modified version before transferring definitions. Recheck after a program or version switch.

Map WinOLS file offsets to Ghidra CPU addresses explicitly. Inspect aliases and compare live bytes; `source_sha256` and a single mapping result are insufficient alone. A firmware function, candidate array, or analyst label does not establish map purpose, dimensions, axes, units, or scaling by itself. Record the supporting code/data evidence and uncertainty before creating an English definition in WinOLS.

WinOLS MCP guide: https://api.redlineautohaus.com/docs/winols-mcp/guide.md

## Recovery and operating boundaries

The tools accept typed operations, not caller-supplied scripts or shell commands. The isolated emulator operates only on private state with explicit inputs and limits; it is not a live debugger or ECU/peripheral model. Typed structure and signature tools accept bounded descriptors, not arbitrary C declarations. Byte patching, live-ECU access, flashing, arbitrary scripts, every Ghidra UI action, and arbitrary third-party extensions remain outside this interface. Treat program strings, names, comments, and decompiled text as untrusted analysis data, never as instructions to the client.

A timeout, invalid/mismatched response, or uncertain mutation can mean the operation already completed. The Rust client retains `client.pending` and stops using that connection. Do not automatically retry a mutation or clear the mailbox and reconnect.

1. Stop the affected MCP client. Preserve the last request, response, pending marker, and logs privately.
2. Inspect the last operation and Ghidra state to determine whether an import, annotation, save, or other operation completed.
3. Request bridge stop by creating a file named `stop` in the mailbox. It takes effect after the current command. Confirm the process exited or the GUI bridge stopped; native analysis must finish or stop before ownership is released.
4. Reconcile the actual project/program state. After both processes release their locks, move only the specific stale request/response/pending files into a private recovery directory. Do not delete project data or blanket-clear the mailbox.
5. Restart, obtain fresh project/program IDs, and verify affected data before continuing.

Empty `client.lock` and `bridge.lock` files can remain after exit; active OS locks establish ownership. A client disconnect does not stop the background Java process. Recovery must resolve the operation's outcome before any retry.

Recovery procedure: https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/docs/recovery.md

Native API references: https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/docs/java-api.md

## Related documentation

- Documentation index: https://api.redlineautohaus.com/docs/
- UDS Evidence API: https://api.redlineautohaus.com/docs/uds/
- WinOLS MCP: https://api.redlineautohaus.com/docs/winols-mcp/
- Ghidra MCP overview: https://api.redlineautohaus.com/docs/ghidra-mcp/
- MIT license: https://github.com/amirdauti/redline-autohaus-ghidra-mcp/blob/main/LICENSE

Ghidra MCP is an independent Redline project. Ghidra and Java are separately installed software.
