{"id":"91190984-7483-4758-bbed-e6cb2851c001","slug":"ghidra-mcp","kind":"mcp","name":"ghidra-mcp","summary":"Ghidra MCP Server — 200+ MCP tools for AI-powered reverse engineering. GUI plugin + headless server, lazy tool loading, convention enforcement, batch operations, Ghidra Server integration, and Docker ","owner":"bethington","repoUrl":"https://github.com/bethington/ghidra-mcp","license":"Apache-2.0","licenseTier":"permissive","trust":"community","stars":5588,"logoUrl":"https://avatars.githubusercontent.com/u/5455441?u=3f03059fca0829f92507b7811b2875663fc6c616&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["cloud-infra","browser-web","memory-knowledge"],"tags":["binary-analysis","ghidra","java","python","reverse-engineering","static-analysis","ai","ghidra-extension"],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-14T03:44:16.837Z","sample":false,"url":"https://conduits.dev/tools/ghidra-mcp","language":"Java","transports":["stdio"],"install":{"pypi":"ghidra-mcp-bridge"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# Ghidra MCP Server\n\n[![MCP Toplist](https://mcptoplist.com/badge/glama%2Fbethington%2Fghidra-mcp.svg)](https://mcptoplist.com/server/glama%2Fbethington%2Fghidra-mcp)\n\n[![Tests](https://img.shields.io/github/actions/workflow/status/bethington/ghidra-mcp/tests.yml?branch=main&style=for-the-badge&label=Tests&logo=github-actions&logoColor=white)](https://github.com/bethington/ghidra-mcp/actions/workflows/tests.yml)\n[![Release](https://img.shields.io/github/v/release/bethington/ghidra-mcp?style=for-the-badge&logo=github&logoColor=white&color=blue)](https://github.com/bethington/ghidra-mcp/releases/latest)\n[![License](https://img.shields.io/github/license/bethington/ghidra-mcp?style=for-the-badge&color=green)](LICENSE)\n[![GitHub Sponsors](https://img.shields.io/github/sponsors/bethington?style=for-the-badge&logo=githubsponsors&logoColor=white&label=Sponsors&labelColor=ea4aaa&color=ea4aaa)](https://github.com/sponsors/bethington)\n\n[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/)\n[![Java](https://img.shields.io/badge/Java-21-ED8B00?style=for-the-badge&logo=openjdk&logoColor=white)](https://openjdk.org/projects/jdk/21/)\n[![Ghidra](https://img.shields.io/badge/Ghidra-12.1.4-brightgreen?style=for-the-badge&logoColor=white)](https://ghidra-sre.org/)\n[![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-6C5CE7?style=for-the-badge&logoColor=white)](https://modelcontextprotocol.io/)\n\n[![Stars](https://img.shields.io/github/stars/bethington/ghidra-mcp?style=for-the-badge&logo=github&logoColor=white&color=yellow)](https://github.com/bethington/ghidra-mcp/stargazers)\n[![Last commit](https://img.shields.io/github/last-commit/bethington/ghidra-mcp?style=for-the-badge&logo=git&logoColor=white)](https://github.com/bethington/ghidra-mcp/commits/main)\n[![Discussions](https://img.shields.io/badge/discussions-join-7B68EE?style=for-the-badge&logo=github&logoColor=white)](https://github.com/bethington/ghidra-mcp/discussions)\n[![Issues](https://img.shields.io/github/issues/bethington/ghidra-mcp?style=for-the-badge&logo=github&logoColor=white&color=orange)](https://github.com/bethington/ghidra-mcp/issues)\n[![OpenSSF Scorecard](https://img.shields.io/ossf-scorecard/github.com/bethington/ghidra-mcp?style=for-the-badge&logo=securityscorecard&logoColor=white&label=OpenSSF%20Scorecard)](https://scorecard.dev/viewer/?uri=github.com/bethington/ghidra-mcp)\n\n> If you find this useful, please ⭐ star the repo — it helps others discover it!\n>\n> If Ghidra MCP saves you time, consider [sponsoring the project](https://github.com/sponsors/bethington). One-time and recurring support both help fund compatibility updates, production hardening, docs, and new tooling.\n\nA production-ready Model Context Protocol (MCP) server that bridges Ghidra's powerful reverse engineering capabilities with modern AI tools and automation frameworks. **209 MCP tools**, battle-tested AI workflows, and the most comprehensive Ghidra-MCP integration available — now including P-code emulation, live debugger integration, and PCode-graph data flow analysis.\n\n## Why Ghidra MCP?\n\nMost Ghidra MCP implementations give you a handful of read-only tools and call it a day. This project is different — it was built by a reverse engineer who uses it daily on real binaries, not as a demo.\n\n- **209 MCP tools** — 3x more than any competing implementation. Not just read operations — full write access for renaming, typing, commenting, structure creation, script execution, P-code emulation, and live debugging.\n- **Battle-tested AI workflows** — Proven documentation workflows (V5) refined across hundreds of functions. Includes step-by-step prompts, Hungarian notation reference, batch processing guides, and orphaned code discovery.\n- **Production-grade reliability** — Atomic transactions, batch operations (93% API call reduction), configurable timeouts, and graceful error handling. No silent failures.\n- **Cross-binary documentation transfer** — SHA-256 function hash matching propagates documentation across binary versions automatically. Document once, apply everywhere.\n- **Full Ghidra Server integration** — Connect to shared Ghidra servers, manage repositories, version control, checkout/checkin workflows, and multi-user collaboration.\n- **Headless and GUI modes** — Run with or without the Ghidra GUI. Docker-ready for CI/CD pipelines and automated analysis at scale.\n- **Opinionated by design** — v5.0 moves naming conventions, type safety, and documentation standards into the tool layer. AI agents and human engineers produce consistent output without style guides in every prompt.\n\n## Convention Enforcement\n\nYou've been there: six months into a project you find `ProcessItem`, `process_items`, `handleItem`, and `ItemProc` in the same codebase — four functions doing the same thing, named by four different sessions or engineers with no shared contract. Fixing it takes longer than it should, and the problem will happen again.\n\nv5.0 moves conventions from \"things to remember\" into the tool layer, where they can actually be enforced.\n\n| Tier | Behavior | Example |\n| ------ | ---------- | --------- |\n| **Auto-fix** | Applied silently | `count` field on a `uint32` → auto-prefixed `dwCount` on save |\n| **Warn** | Change goes through, warning returned | `processData` → \"name should be PascalCase with a verb: `ProcessData`\" |\n| **Reject** | Change blocked with explanation | `undefined → undefined` type change → \"no-op rejected, type unchanged\" |\n\n**For AI agents**, this means consistent output across every session, every model, every run — without pasting a style guide into every prompt. The tool knows the rules; the model just needs to make the call.\n\n**For teams**, it eliminates the entire class of review comment that says \"that's not our naming convention.\" Convention arbitration stays in the tool, not in code review.\n\n**For solo work at scale**, `analyze_function_completeness` gives you a 0–100% score that measures honestly: structural deductions (unfixable compiler artifacts) are forgiven in your effective score, log-scaling prevents one bad category from burying everything else, and tiered plate comment quality means you know exactly what's missing and why.\n\n## 🌟 Features\n\n### Core MCP Integration\n\n- **Full MCP Compatibility** — Complete implementation of Model Context Protocol\n- **209 MCP tools** — Comprehensive API surface covering every aspect of binary analysis\n- **Production-Ready Reliability** — Atomic transactions, batch operations, configurable timeouts\n- **Real-time Analysis** — Live integration with Ghidra's analysis engine\n\n> **Compatibility note:** MCP tool names are normalized for GitHub Copilot CLI\n> and CAPI validation. Exposed tool names use lowercase letters, digits,\n> underscores, and hyphens only; nested HTTP paths such as `/debugger/status`\n> are advertised as names like `debugger_status_2` when needed to avoid\n> collisions with static bridge tools.\n\n### Binary Analysis Capabilities\n\n- **Function Analysis** — Decompilation, call graphs, cross-references, completeness scoring\n- **Data Flow Analysis** — PCode-graph value propagation (forward / backward) from any variable or register\n- **Data Structure Discovery** — Struct/union/enum creation with field analysis and naming suggestions\n- **String Extraction** — Regex search, quality filtering, and string-anchored function discovery\n- **Import/Export Analysis** — Symbol tables, external locations, ordinal import resolution\n- **Memory & Data Inspection** — Raw memory reads, byte pattern search, array boundary detection\n- **Cross-Binary Documentation** — Function hash matching and documentation propagation across versions\n\n### Dynamic Analysis (v5.4.0)\n\n- **P-code Emulation** — Run any function in isolation via Ghidra's `EmulatorHelper`; brute-force API hash resolution in milliseconds\n- **Live Debugger Integration** — 16 `/debugger/*` endpoints over Ghidra's TraceRmi framework (GUI plugin only; dbgeng on Windows PE, gdb/lldb otherwise): launch, interrupt/resume, step into/over/out, breakpoints, registers, memory reads, stack traces, ASLR-aware static↔dynamic address translation. The bridge can also proxy 22 `debugger_*` tools to an external debugger server; they are opt-in (see [below](#optional-connect-an-external-debugger-server))\n\n### AI-Powered Reverse Engineering Workflows\n\n- **Function Documentation Workflow V5** — 7-step process for complete function documentation with Hungarian notation, type auditing, and automated verification scoring\n- **Batch Documentation** — Parallel subagent dispatch for documenting multiple functions simultaneously\n- **Orphaned Code Discovery** — Automated scanner finds undiscovered functions in gaps between known code\n- **Data Type Investigation** — Systematic workflows for structure discovery and field analysis\n- **Cross-Version Matching** — Hash-based function matching across different binary versions\n\n### Development & Automation\n\n- **Ghidra Script Execution** — List and run Ghidra scripts, or run inline script code, via MCP (running them is opt-in: `GHIDRA_MCP_ALLOW_SCRIPTS=1`)\n- **Multi-Program Support** — Switch between and compare multiple open programs\n- **Batch Operations** — Bulk renaming, commenting, typing, and label management (93% fewer API calls)\n- **Headless Server** — Full analysis without Ghidra GUI — Docker and CI/CD ready\n- **Project & Version Control** — Create projects, manage files, Ghidra Server integration\n- **Analysis Control** — List, configure, and trigger Ghidra analyzers programmatically\n\n## 🚀 Quick Start\n\n### Prerequisites\n\n- **Java 21 LTS** (OpenJDK recommended)\n- **Apache Maven 3.9+** for the `python -m tools.setup` commands below (Maven is their default backend). Not needed if you build with the committed Gradle wrapper instead — see step 6\n- **Ghidra 12.1.4** (or compatible version)\n- **Python 3.10+** with [uv](https://docs.astral.sh/uv/) (recommended) or pip + venv\n\n> Shared Ghidra Server users: Ghidra 12.1.4 clients require a Ghidra\n> Server at 12.1, 12.0.5, or a newer compatible version. Upgrade the\n> server before using this plugin from a 12.1 client.\n>\n> Ghidra 12.1.4 ships Jython as an optional extension. Java scripts work\n> by default, but `.py` scripts in `ghidra_scripts/` require installing\n> the Jython extension from **File > Install Extensions** and restarting\n> Ghidra.\n\n### Installation\n\n> Recommended for all platforms: use `python -m tools.setup` directly.\n>\n> `ensure-prereqs` installs runtime Python requirements plus the Ghidra JARs needed in the local Maven repository.\n> `deploy` copies the build output, installs the user-profile extension, and patches Ghidra user config.\n\n1. **Clone the repository:**\n\n   ```bash\n   git clone https://github.com/bethington/ghidra-mcp.git\n   cd ghidra-mcp\n   ```\n\n2. **Recommended: run environment preflight first:**\n\n   ```text\n   python -m tools.setup preflight --ghidra-path \"F:\\ghidra_12.1.4_PUBLIC\"\n   ```\n\n3. **Build and deploy to Ghidra:**\n\n   ```text\n   python -m tools.setup ensure-prereqs --ghidra-path \"F:\\ghidra_12.1.4_PUBLIC\"\n   python -m tools.setup build\n   python -m tools.setup deploy --ghidra-path \"F:\\ghidra_12.1.4_PUBLIC\"\n   ```\n\n   `deploy` saves/closes an already-running matching Ghidra instance when\n   needed, installs the extension, starts Ghidra, waits for MCP health, and runs\n   schema smoke checks.\n\n   Prefer to click through Ghidra's own dialogs, or installing a release zip on\n   a machine without the repo? Follow the illustrated\n   [manual GUI install guide](docs/INSTALL_GUI.md).\n\n4. **Optional strict/manual mode** (advanced):\n\n   ```text\n   # Skip automatic prerequisite setup\n   python -m tools.setup build\n   python -m tools.setup deploy --ghidra-path \"F:\\ghidra_12.1.4_PUBLIC\"\n   ```\n\n5. **Show command help**:\n\n   ```text\n   python -m tools.setup --help\n   ```\n\n6. **Optional build-only mode** (advanced/troubleshooting):\n\n   ```text\n   python -m tools.setup build\n   ```\n\n   Two Java backends are supported. **Gradle is the default for local work** — it reads Ghidra's jars straight out of the installation, so there is no `install-file` step and nothing to install beyond a JDK. **CI builds and gates with Maven**, so Maven is a maintained peer rather than a fallback.\n\n   ```bash\n   # Gradle (default) -- the wrapper is committed, so no Gradle install is needed.\n   # -PGHIDRA_INSTALL_DIR or the GHIDRA_INSTALL_DIR env var both work.\n   # In Git Bash use forward slashes; a backslash path is mangled before Gradle sees it.\n   ./gradlew buildExtension -PGHIDRA_INSTALL_DIR=/path/to/ghidra\n   ```\n\n   ```bash\n   # Maven (peer backend; what CI uses). Needs Ghidra's jars in the local .m2 first:\n   #   python -m tools.setup ensure-prereqs --ghidra-path /path/to/ghidra\n   mvn clean package assembly:single -DskipTests\n   ```\n\n   `python -m tools.setup build` routes to Maven by default; set `TOOLS_SETUP_BACKEND=gradle` to route it to Gradle instead.\n\n### Installation (Linux — Ubuntu/Debian)\n\n1. **Clone the repository:**\n\n   ```bash\n   git clone https://github.com/bethington/ghidra-mcp.git\n   cd ghidra-mcp\n   ```\n\n2. **Install system prerequisites** (if not already installed):\n\n   ```bash\n   sudo apt update && sudo apt install -y openjdk-21-jdk maven python3 python3-pip python3-venv curl jq unzip\n   ```\n\n   > **Debian/Kali/Ubuntu 23.04+ note (PEP 668):** these distros mark the system\n   > Python as *externally managed*, so a bare `pip install` fails with\n   > `error: externally-managed-environment`. Don't work around it with\n   > `--break-system-packages` — it can corrupt apt-managed tooling. Instead use\n   > [uv](https://docs.astral.sh/uv/) (recommended — it creates and manages a\n   > project-local `.venv` automatically, and is what this repo's commands use):\n   >\n   > ```bash\n   > curl -LsSf https://astral.sh/uv/install.sh | sh\n   > uv run bridge-mcp-ghidra    # resolves deps into .venv and starts the bridge\n   > ```\n   >\n   > or a classic virtual environment:\n   >\n   > ```bash\n   > python3 -m venv .venv && source .venv/bin/activate\n   > pip install -e .\n   > bridge-mcp-ghidra\n   > ```\n\n3. **Run environment preflight:**\n\n   ```bash\n   python -m tools.setup preflight --ghidra-path ~/ghidra_12.1.4_PUBLIC\n   ```\n\n4. **Build and deploy to Ghidra (single command):**\n\n   ```bash\n   python -m tools.setup ensure-prereqs --ghidra-path ~/ghidra_12.1.4_PUBLIC\n   python -m tools.setup build\n   python -m tools.setup deploy --ghidra-path ~/ghidra_12.1.4_PUBLIC\n   ```\n\n   This will:\n   - Install Ghidra JAR dependencies into your local `~/.m2/repository`\n   - Build `GhidraMCP-<version>.zip` with Maven\n   - Extract the extension to `~/.config/ghidra/ghidra_<version>_PUBLIC/Extensions/`\n   - Update `preferences` with `LastExtensionImportDirectory`\n   - Install Python requirements\n\n5. **Optional: setup only Maven dependencies:**\n\n   ```bash\n   python -m tools.setup install-ghidra-deps --ghidra-path ~/ghidra_12.1.4_PUBLIC\n   ```\n\n6. **Show command help:**\n\n   ```bash\n   python -m tools.setup --help\n   ```\n\n> **Linux paths:** The extension is installed to `$HOME/.config/ghidra/ghidra_<version>_PUBLIC/Extensions/GhidraMCP/`.\n> Ghidra config files are in `$HOME/.config/ghidra/ghidra_<version>_PUBLIC/`.\n\n### Installation (macOS — Homebrew)\n\n1. **Install prerequisites:**\n\n   ```bash\n   brew install openjdk@21 maven python ghidra\n   ```\n\n2. **Clone the repository:**\n\n   ```bash\n   git clone https://github.com/bethington/ghidra-mcp.git\n   cd ghidra-mcp\n   ```\n\n3. **Install Ghidra JARs into local Maven:**\n\n   ```bash\n    python -m tools.setup install-ghidra-deps \\\n       --ghidra-path /opt/homebrew/opt/ghidra/libexec\n   ```\n\n4. **Build and deploy:**\n\n   ```bash\n    python -m tools.setup ensure-prereqs \\\n       --ghidra-path /opt/homebrew/opt/ghidra/libexec\n    python -m tools.setup build\n    python -m tools.setup deploy \\\n       --ghidra-path /opt/homebrew/opt/ghidra/libexec\n   ```\n\n   The extension is installed to `~/Library/ghidra/ghidra_12.1.4_PUBLIC/Extensions/GhidraMCP/`.\n\n   > **Note:** the Homebrew path contains no version string, so `tools.setup` reads the Ghidra version from `Ghidra/application.properties` inside the installation instead.\n\n5. **Start Ghidra and enable the plugin:**\n\n   ```bash\n   /opt/homebrew/opt/ghidra/libexec/ghidraRun\n   ```\n\n   The server starts with the plugin. Check it from the project window:\n   **Tools > GhidraMCP > Server Status**\n\n6. **Configure Cursor/Claude MCP** (`~/.cursor/mcp.json`) — use the **absolute\n   path** to `uv` (`which uv`), not the bare name; GUI-launched clients do not\n   inherit your shell's PATH ([#441](https://github.com/bethington/ghidra-mcp/issues/441)):\n\n   ```json\n   {\n     \"mcpServers\": {\n       \"ghidra\": {\n         \"command\": \"/opt/homebrew/bin/uv\",\n         \"args\": [\"run\", \"--directory\", \"/path/to/ghidra-mcp\", \"bridge-mcp-ghidra\"]\n       }\n     }\n   }\n   ```\n\n   macOS is the sharpest case: apps launched from Finder/Dock get `launchd`'s\n   PATH, which never contains `~/.local/bin` or `/opt/homebrew/bin`.\n\n### Installation (Arch Linux — AUR)\n\n[@Pandoriaantje](https://github.com/Pandoriaantje) maintains community AUR packages:\n\n- [`ghidra-mcp-git`](https://aur.archlinux.org/packages/ghidra-mcp-git) — tracks `main`\n- [`ghidra-mcp`](https://aur.archlinux.org/packages/ghidra-mcp) — tracks tagged releases\n\nInstall with your AUR helper of choice, e.g.:\n\n```bash\nyay -S ghidra-mcp        # or ghidra-mcp-git\n```\n\n### Basic Usage\n\n#### Option 1: Stdio Transport (Recommended for AI tools)\n\n```bash\nuv run bridge-mcp-ghidra          # or: python -m bridge_mcp_ghidra\n```\n\nMCP client config (`.mcp.json`, `~/.cursor/mcp.json`, Claude Desktop config, …).\n**Use the absolute path to `uv`** — see the note below for why:\n\n```json\n{\n  \"mcpServers\": {\n    \"ghidra-mcp\": {\n      \"command\": \"/home/<you>/.local/bin/uv\",\n      \"args\": [\"run\", \"--directory\", \"/path/to/ghidra-mcp\", \"bridge-mcp-ghidra\", \"--transport\", \"stdio\"],\n      \"env\": { \"GHIDRA_MCP_URL\": \"http://127.0.0.1:8089\" }\n    }\n  }\n}\n```\n\nOn Windows the same config points at `uv.exe`, e.g.\n`\"command\": \"C:\\\\Users\\\\<you>\\\\.local\\\\bin\\\\uv.exe\"`. Find your own path with\n`which uv` (POSIX) or `where.exe uv` (Windows), or just run\n`python -m tools.setup preflight`, which prints the resolved absolute path and a\nready-to-paste snippet.\n\n> **Why absolute? `\"command\": \"uv\"` fails under service and GUI launchers.**\n> The MCP client resolves `command` with **its own** PATH, not your shell's. A\n> client started from a **systemd user service**, a `.desktop` entry, or any\n> other GUI session inherits that launcher's environment, which routinely lacks\n> `~/.local/bin` and `~/.cargo/bin` — the very directories `uv` installs into.\n> The failure lands at process-spawn time as `spawn uv ENOENT`, before any\n> bridge code runs, so there is nothing in any log to read. An absolute path\n> makes the client's PATH irrelevant and works on the first try. The same\n> applies to `python`, `python3`, and the `bridge-mcp-ghidra` console script.\n> ([#441](https://github.com/bethington/ghidra-mcp/issues/441))\n\nTo add the bridge to [Autohand Code](https://github.com/autohandai/code-cli/) from a cloned checkout:\n\n```bash\nautohand mcp add ghidra /home/<you>/.local/bin/uv run --directory /path/to/ghidra-mcp bridge-mcp-ghidra\n```\n\nAdd `--scope project` before `ghidra` to save the server in the current project's `.autohand` configuration instead of your user configuration.\n\n#### Option 2: Streamable HTTP Transport (Recommended for web/HTTP clients)\n\n```bash\nuv run bridge-mcp-ghidra --transport streamable-http --mcp-host 127.0.0.1 --mcp-port 8081\n```\n\nMCP client config for the HTTP transport (add to your client's MCP config file):\n\n```json\n{\n  \"mcpServers\": {\n    \"ghidra-mcp-http\": {\n      \"url\": \"http://127.0.0.1:8081/mcp\"\n    }\n  }\n}\n```\n\nBrowser-based clients (e.g. [MCP Inspector](https://github.com/modelcontextprotocol/inspector))\nwork out of the box: the HTTP transports answer CORS preflight (`OPTIONS`) requests and expose\nthe `mcp-session-id` / `mcp-protocol-version` headers to scripts. Allowed origins mirror the\nHost-header policy — loopback on any port is always permitted, plus the bind host and any\nhosts listed in `GHIDRA_MCP_ALLOWED_HOSTS`.\n\n`GHIDRA_MCP_ALLOWED_HOSTS` also supports clients that route a loopback-bound\nbridge through another network namespace. For example, a container can address\nthe host as `host.containers.internal` without exposing the bridge on a LAN\ninterface:\n\n```bash\nGHIDRA_MCP_ALLOWED_HOSTS=host.containers.internal \\\n  uv run bridge-mcp-ghidra --transport streamable-http \\\n  --mcp-host 127.0.0.1 --mcp-port 8081\n```\n\nThe setting extends DNS-rebinding Host/Origin validation only; it does not\nchange the bind address or make the listener reachable on additional interfaces.\n\n#### Option 3: SSE Transport (Deprecated — use streamable-http instead)\n\n```bash\nuv run bridge-mcp-ghidra --transport sse --mcp-host 127.0.0.1 --mcp-port 8081\n```\n\n#### Bridge advanced flags\n\n| Flag | Default | Description |\n| ------ | --------- | ------------- |\n| `--transport` | `stdio` | `stdio` (AI tools), `streamable-http` (web clients), `sse` (deprecated) |\n| `--mcp-host` | `127.0.0.1` | Bind host for HTTP transports |\n| `--mcp-port` | — | Port for HTTP transports |\n| `--lazy` | (default) | Load only the default tool groups on connect, and let the model pull in the rest with `search_tools`/`load_tool_group`. |\n| `--no-lazy` | off | Load all tool groups immediately on connect. Needed only by MCP clients that ignore `tools/list_changed`; **rejected outright by the Gemini API** (see below). |\n| `--default-groups` | `listing,function,program` | Comma-separated groups loaded on connect under `--lazy`. |\n| `--tools-page-size` | `0` | Serve `tools/list` in pages of this size (`0` = one page). Only for a client that cannot take one large response; a client that ignores `nextCursor` sees only the first page. |\n| `--json-response` | off | streamable-http: answer POSTs with plain JSON instead of an SSE stream. Server-initiated messages such as `tools/list_changed` are then not delivered. |\n| `--stateless-http` | off | streamable-http: no session id and no server-initiated notifications, for running several bridge workers behind a load balancer. Pair it with `--no-lazy`, since a group loaded later can never be announced. |\n\nTo require a token from MCP clients of an HTTP transport, set\n`GHIDRA_MCP_INBOUND_TOKEN=<secret>`; clients must then send\n`Authorization: Bearer <secret>`. The bridge logs a warning when it binds a\nnon-loopback `--mcp-host` without one. Separately, when `GHIDRA_MCP_AUTH_TOKEN`\n(the token the bridge sends to Ghidra) is set and the bridge binds a\nnon-loopback host, clients must present that same token, so the bridge cannot\nbe used to relay it.\n\n#### Lazy tool loading is the default (issue #440)\n\nAdvertising all 209 endpoints in a single `tools/list` is over a hard limit for\nat least one major provider. Gemini compiles function declarations into a\nconstrained-decoding state machine and rejects the whole request before any tool\nis ever called:\n\n```text\n400 INVALID_ARGUMENT\nThe specified schema produces a constraint that has too many states for serving\n```\n\nThat is not a degradation, it is an outright break, and no client-side setting\ncould work around a server that only ever offered the full set. So the bridge\nnow loads `listing,function,program` (68 endpoints plus the 8 static tools) on\nconnect and registers the rest on demand.\n\n**If your client ignores `tools/list_changed`** it will not notice tools that\nare registered later, and should turn lazy loading off:\n\n```bash\nuv run bridge-mcp-ghidra --no-lazy          # when you control the command line\nexport GHIDRA_MCP_LAZY=0                    # when you don't (Docker, uvx, some client configs)\n```\n\n`GHIDRA_MCP_LAZY` accepts `0/false/no/off` and `1/true/yes/on`; an explicit\n`--lazy`/`--no-lazy` on the command line wins over it. Startup logs which mode\nis in effect.\n\n#### Strict program routing (multi-program safety)\n\nSet `GHIDRA_MCP_REQUIRE_PROGRAM_SELECTORS=1` to make the bridge refuse any program-scoped\ncall that omits a program selector, returning a clear error instead of letting the call\nride the server's shared \"current program\" (the one `switch_program` and the\nactive GUI tab move).\n\n```bash\nexport GHIDRA_MCP_REQUIRE_PROGRAM_SELECTORS=1\nuv run bridge-mcp-ghidra\n```\n\nWithout this, a call that leaves `program=` out runs against whichever program\nis current, which is fine for a single-program workflow but a hazard once\nseveral programs are open: the call can read or edit the wrong binary with no\nerror. The hazard is worse when more than one client shares a server, since\neach one moves that current-program global out from under the others.\n\nWith strict mode on, every program-scoped call must name its target. This\ncovers every selector that picks an open program: plain `program=` and the\ncross-program tools' `source_program`/`target_program` or `program_a`/`program_b`\n(declared required, but the server still falls back to the current program when\none arrives empty). A forgotten selector surfaces as a loud error on the first\nbad call instead of a silent write to the wrong binary. Tools with no program\nselector (`open_program` and `close_program` take `path`/`name`) are unaffected.\nOff by default: with the variable unset the bridge sends calls unchanged.\n\n#### Reducing tool-context overhead\n\nThe bridge exposes a large catalog, so it loads only `listing,function,program`\non connect (see above) and lets the model **discover** the rest on demand\ninstead of registering everything:\n\n- `search_tools(\"rename function\")` — keyword-search the **entire** catalog,\n  including tools whose group isn't loaded. Each result says whether it's\n  callable now and, if not, the exact `load_tool_group(...)` call to enable it.\n- `list_tool_groups()` — list all categories and their load state.\n- `load_tool_group(\"datatype\")` / `unload_tool_group(\"datatype\")` — load or\n  drop a category at runtime.\n- `check_tools(\"rename_symbol,batch_set_comments\")` — confirm specific tools\n  are callable right now.\n\n`search_tools` works in both lazy and `--no-lazy` modes, so agents that honor\n`tools/list_changed` get full discovery without the upfront context cost.\n\n#### Minimal read-only allowlist\n\nSome MCP clients gate tools through an explicit allowlist. Cut it too far and\nthe agent loses **discovery** — it cannot find entry points or enumerate\nfunctions through MCP, so it works around the allowlist by `curl`-ing the HTTP\nAPI on `127.0.0.1:8089` directly, which defeats the point of having one. The\nallowlist has to be small *and* self-sufficient.\n\n**Minimum viable read-only set (4 tools):**\n\n| Tool | Group | What it buys you |\n| --- | --- | --- |\n| `get_metadata` | `program` | Which binary is loaded — name, architecture, image base, function count. Orientation, and it confirms the bridge reached Ghidra at all. |\n| `find_functions` | `listing` | Paginated function enumeration (`offset`, `limit`); with no filter it lists the whole program, and `name_pattern`/`regex` turn it into a name search. **This is the discovery tool** — without it the agent cannot answer \"what is in this binary\". |\n| `get_entry_points` | `listing` | Where execution starts, so analysis has a root to work down from. |\n| `get_functions` | `function` | The payload. Takes `function=` (name or address) **or** `functions=` (comma-separated names *or* addresses, up to 20), and `fields=` to pick what comes back: `decompiled_code`, `signature`, `callers`, `callees`, `xrefs`, `comments`, and more. |\n\nThat set is genuinely closed: `get_entry_points` and `find_functions` supply the\naddresses and names that `get_functions` consumes, and its `callees` field names\nthe next functions to feed straight back into it.\n\nThe three tools suggested in [#441](https://github.com/bethington/ghidra-mcp/issues/441)\nwere `get_metadata`, `get_entry_points` and `decompile_function`. The first two\nstill exist under those names; `decompile_function` was folded into\n`get_functions` in 7.0.0 (`fields=decompiled_code`), and\n[the migration guide](docs/project-management/MIGRATION_7.0.0_TOOL_CONSOLIDATION.md)\nmaps every other removed name. `find_functions` is the one addition worth\nmaking: without it the agent can only reach code that is reachable by name from\nsomething it already decompiled, so anything not referenced from an entry point\nis invisible.\n\n**Useful next additions, in order:**\n\n| Tool | Group | Why |\n| --- | --- | --- |\n| `get_function_call_graph` | `xref` | A multi-level call graph (`depth`, `direction`) in one call. One level of callers and callees already comes from `get_functions`. |\n| `get_xrefs_to` | `xref` | Who touches this address — the standard question about a global. Takes `addresses=` for several at once. |\n| `list_strings` | `listing` | Strings are the cheapest orientation signal in an unknown binary. |\n| `list_program_items` | `listing` | `kind=imports` / `kind=exports`: the binary's external surface. Other kinds list segments, classes, namespaces, data items and external locations. |\n\nEvery tool above is a `GET`; none of them writes to the Ghidra database.\n\n**Two things to check when your allowlist is narrow:**\n\n- **Groups, not just names.** The bridge registers tools by *group*, and the\n  group is the `category` on the Java `@McpTool` annotation (which the running\n  server publishes at `/mcp/schema`) — not the `category` field in\n  `tests/endpoints.json`, which is a separate hand-maintained column and does\n  not always agree. All four tools in the minimum set fall inside the default\n  `listing,function,program` groups, so they are registered even under `--lazy`.\n  Of the additions above, only the `xref` ones fall outside: under `--lazy` you\n  must either allow `load_tool_group` as well, or start the bridge with\n  `--default-groups listing,function,program,xref`.\n- **A narrow allowlist plus `--lazy` needs the group tools.** If you allowlist\n  only leaf tools and run lazily, the agent has no way to load anything else.\n  Either run eagerly (`--no-lazy`; lazy is the default) or add `search_tools`,\n  `list_tool_groups`, `load_tool_group`, and `check_tools` to the allowlist.\n\nVerify any allowlist against the running server rather than against this table:\n`curl http://127.0.0.1:8089/mcp/schema` lists every tool with the `category`\nthe bridge groups it by.\n\n#### Optional: Connect an external debugger server\n\nThe bridge can proxy 22 `debugger_*` tools to an external dbgeng/WinDbg\ndebugger server speaking the bridge's debugger HTTP API. That server is **not\npart of this repository**; this repo ships only the proxies. They are **off by\ndefault** and register only when you opt in:\n\n```bash\n# point the bridge at your debugger server (loopback only)\nexport GHIDRA_DEBUGGER_URL=http://127.0.0.1:8099\n\n# or force registration against the default URL (http://127.0.0.1:8099)\nexport GHIDRA_DEBUGGER_TOOLS=1\n```\n\n`GHIDRA_DEBUGGER_TOOLS` decides outright when set: `1`/`true`/`yes`/`on`\nregisters the tools, anything else (`0`, `false`, ...) keeps them off even with\na URL configured. With neither variable set the tools simply do not appear,\nrather than appearing and failing. The host platform plays no part.\n\nGhidra's own TraceRmi debugger endpoints (`debugger_status`, `debugger_launch`,\n...) are separate: they live in the GUI plugin and need no extra server.\n\nCalls are only ever proxied to a loopback URL (`127.0.0.1`, `localhost` or\n`::1`), so a non-loopback `GHIDRA_DEBUGGER_URL` does not register the tools. To\nreach a debugger server on another machine, forward its port to loopback (for\nexample an SSH tunnel) and point `GHIDRA_DEBUGGER_URL` there.\n\nThe bridge reads these from its own process environment (the MCP client's\n`env` block, or your shell); it does not read a `.env` file.\n\n#### In Ghidra\n\n1. Start Ghidra and open your project\n2. In the **project window**, enable the plugin via **File > Configure > Utility > Configure > GhidraMCPPlugin** (this is what `deploy` does; enabling it in CodeBrowser also works, but then the server runs only while CodeBrowser is open)\n3. Optional: configure a custom port via **Edit > Tool Options > GhidraMCP HTTP Server** in the same window\n4. The server starts with the plugin; check it via **Tools > GhidraMCP > Server Status**\n5. The server runs on `http://127.0.0.1:8089/` by default\n\nScreenshots of every step: [docs/INSTALL_GUI.md](docs/INSTALL_GUI.md).\n\n#### Verify It's Working\n\n```bash\n# Quick health check\ncurl http://127.0.0.1:8089/check_connection\n# Expected: {\"status\": \"ok\", \"server_kind\": \"gui\", \"version\": \"7.0.0\", \"program\": \"<name>\"}\n# (\"program\" appears only while a program is current)\n\n# Fuller health: build details, uptime, open program count, HTTP pool, memory\ncurl http://127.0.0.1:8089/mcp/health\n\n# Every tool the server advertises\ncurl -s http://127.0.0.1:8089/mcp/schema | jq '.tools | length'\n```\n\n## Support This Project\n\nIf Ghidra MCP saves you engineering or reverse-engineering time, consider [sponsoring the project](https://github.com/sponsors/bethington).\n\n- One-time sponsorship helps fund fixes, compatibility updates, and release work.\n- Recurring sponsorship helps keep maintenance, docs, and production hardening moving.\n- Company support helps prioritize long-term reliability for the bridge, headless server, debugger integration, and workflow tooling.\n\n## 🔒 Security\n\nGhidraMCP is designed for **localhost-only development**. The default configuration — HTTP server bound to `127.0.0.1`, no authentication — is safe on a trusted single-user workstation and matches pre-v5.4.1 behavior.\n\n**If you expose the server beyond loopback, configure these three environment variables first.** The server refuses to start on a non-loopback bind without a token.\n\n| Env var | Effect |\n| --- | --- |\n| `GHIDRA_MCP_AUTH_TOKEN` | When set, every HTTP request must carry `Authorization: Bearer <token>`. Timing-safe comparison. `/mcp/health` and `/check_connection` are exempt. |\n| `GHIDRA_MCP_ALLOW_SCRIPTS` | Set to `1`, `true`, or `yes` to enable `/run_script_inline` and `/run_ghidra_script`. **Off by default as of v5.4.1** — these endpoints execute arbitrary Java against the Ghidra process. In headless mode this also triggers OSGi `BundleHost` initialization at server startup (Felix framework, ~hundreds of ms); leave it off if you don't need script execution. |\n| `GHIDRA_MCP_FILE_ROOT` | When set to a directory path, filesystem-path endpoints (`/import_file`, `/open_project`, `/delete_file`, etc.) canonicalize the input and require it to fall under this root. Prevents path-traversal. |\n\nName-quality enforcement is separate from security. By default,\n`rename_function` and global write endpoints reject names that fail\nthe built-in quality gates, and struct field writes apply the built-in field\nprefix convention. Disable the built-in convention layer with **Edit > Tool\nOptions > GhidraMCP HTTP Server > Strict Naming Enforcement**. The same Tool\nOptions checkbox covers `rename_symbol` (all symbol kinds),\n`set_global`, the `apply_data_type` prefix/type guard, and struct-field\nHungarian prefix auto-fixes in `create_struct`, `add_struct_field`, and\n`modify_struct_field`. The setting is read when the MCP server starts or\nrestarts. Function/global convention warnings are still returned when\nenforcement is disabled.\n\n### Example: exposing to a private LAN with auth\n\n```bash\nexport GHIDRA_MCP_AUTH_TOKEN=$(openssl rand -hex 32)\nexport GHIDRA_MCP_ALLOW_SCRIPTS=1     # only if your workflow needs it\nexport GHIDRA_MCP_FILE_ROOT=/srv/ghidra/inputs\n\n# Headless server (`mvn clean package -P headless -DskipTests`). The jar does not\n# bundle Ghidra, so Ghidra's Framework/Features/Processors jars go on the\n# classpath too -- docker/entrypoint.sh builds exactly that command.\njava -cp \"target/GhidraMCP-<version>.jar:<ghidra jars>\" \\\n  com.xebyte.headless.GhidraMCPHeadlessServer --bind 0.0.0.0 --port 8089\n```\n\nThe headless server serves only its Unix domain socket unless `--port` or\n`--bind` is given; either one adds the TCP listener.\n\n### Ghidra Server authentication\n\nWhen connecting to a shared Ghidra Server, GhidraMCP can suppress the password dialog automatically. It resolves credentials in this order (first non-empty value wins):\n\nCompatibility note: Ghidra 12.1.4 clients require Ghidra Server 12.1.2,\n12.0.5, or a newer compatible server. Older shared servers are not safe\ntargets for a 12.1 client upgrade.\n\n1. `GHIDRA_SERVER_PASSWORD` environment variable (or `.env` file in the Ghidra install directory or `~`)\n2. `~/.ghidra-cred` — single-line password file in your home directory\n3. `<ghidra-install-dir>/.ghidra-cred`\n\nUsername resolves similarly: `GHIDRA_SERVER_USER` env var → `user.name` system property.\n\nIf no password is found, Ghidra shows its normal GUI prompt. Set these in `.env` (see `.env.template` for the full block) to enable silent auth.\n\n### Migration from v5.4.0 → v5.4.1\n\n- **Script endpoints now default-off.** If you relied on `/run_script_inline` or `/run_ghidra_script`, export `GHIDRA_MCP_ALLOW_SCRIPTS=1`. This is a deliberate breaking change; the prior default was unsafe.\n- **Localhost-only deployments need no changes.** Auth, bind refusal, and path-root checks are all opt-in.\n\n## ❓ Troubleshooting\n\n### `spawn uv ENOENT` / `spawn python ENOENT` when the client starts the server\n\n**Cause:** the MCP client could not find the `command` on **its own** PATH. This\nhappens before any bridge code runs, so there is nothing in the Ghidra log or\nthe bridge log to look at — the process was never created.\n\nIt shows up when the client is launched by something other than a login shell:\n\n- a **systemd user service** (`systemctl --user`), whose PATH is\n  `/usr/local/bin:/usr/bin:/bin` unless you extend it;\n- a desktop `.desktop` entry, dock icon, or app launcher;\n- macOS apps started from Finder, which inherit `launchd`'s PATH.\n\nNone of those contain `~/.local/bin` (uv's default install location) or\n`~/.cargo/bin`, so `\"command\": \"uv\"` cannot resolve even though `uv` works\nperfectly in your terminal.\n\n**Solution:** use an absolute path in the client config.\n\n```jsonc\n// before — resolves against the client's PATH, fails under a service/GUI launcher\n\"command\": \"uv\"\n// after — no PATH lookup at all\n\"command\": \"/home/<you>/.local/bin/uv\"\n```\n\nFind yours with `which uv` (POSIX) or `where.exe uv` (Windows). Or run:\n\n```bash\npython -m tools.setup preflight\n```\n\nwhich prints the resolved absolute path, the PATH entries it searched when a\nlauncher is missing, and a ready-to-paste config snippet. Note what that check\ncan and cannot tell you: it resolves against **your shell's** PATH, not the\nclient's, so a pass there is evidence, not proof — the absolute path is what\nactually removes the failure mode. ([#441](https://github.com/bethington/ghidra-mcp/issues/441))\n\nIf you must keep a bare command name, give the service manager the PATH instead\n— e.g. `Environment=\"PATH=%h/.local/bin:/usr/local/bin:/usr/bin:/bin\"` in the\nunit file, or `systemctl --user import-environment PATH` — but the absolute\npath is the smaller and more portable fix.\n\n### \"GhidraMCP\" menu not appearing in Tools\n\n**Cause:** Plugin not enabled or installed incorrectly.\n\n**Solution:**\n\n1. Verify extension is installed: **File > Install Extensions** — GhidraMCP should be listed\n2. Enable the plugin: **File > Configure > Utility > Configure > GhidraMCPPlugin** (check the box)\n3. **Restart Ghidra** after installation/enabling\n\nIllustrated walkthrough: [docs/INSTALL_GUI.md](docs/INSTALL_GUI.md).\n\n### Server not responding / Connection refused\n\n**Cause:** Server not started or wrong port.\n\n**Solution:**\n\n1. Check the server state: **Tools > GhidraMCP > Server Status** (it starts with the plugin; use **Restart Server** if it is stopped)\n2. Check configured port: **Edit > Tool Options > GhidraMCP HTTP Server**\n3. Check if port is in use:\n\n   ```bash\n   # Linux/macOS\n   lsof -i :8089\n   # Windows\n   netstat -ano | findstr :8089\n   ```\n\n4. Look for errors in Ghidra console: **Window > Console**\n\n### `pip install` fails with `error: externally-managed-environment`\n\n**Cause:** PEP 668. Debian-family distros (Debian 12+, Kali, Ubuntu 23.04+)\nmark the system Python as externally managed, so global `pip install` is\nblocked to protect apt-managed packages.\n\n**Solution:** Use a virtual environment — never `--break-system-packages`.\nThe recommended path is [uv](https://docs.astral.sh/uv/), which manages a\nproject-local `.venv` automatically:\n\n```bash\ncurl -LsSf https://astral.sh/uv/install.sh | sh\ncd ghidra-mcp\nuv run bridge-mcp-ghidra\n```\n\nOr a classic venv:\n\n```bash\npython3 -m venv .venv && source .venv/bin/activate\npip install -e .\nbridge-mcp-ghidra\n```\n\n### The `debugger_*` tools do not appear\n\n**Cause:** The 22 bridge-side debugger proxies are off by default. They register\nonly when `GHIDRA_DEBUGGER_URL` is set or `GHIDRA_DEBUGGER_TOOLS=1`, and they\nforward to an external debugger server that is not part of this repository.\n\n**Solution:** start your debugger server, then set the URL before launching the\nbridge:\n\n```text\nexport GHIDRA_DEBUGGER_URL=http://127.0.0.1:8099\n```\n\nIf the tools appear but every call reports that the server is not running,\nthe URL is wrong or the server is down. `GHIDRA_DEBUGGER_TOOLS=0` turns them off\nagain even with a URL configured.\n\n### 500 Internal Server Errors\n\n**Cause:** Server-side exception, often due to missing program data.\n\n**Solution:**\n\n1. Ensure a binary is loaded in CodeBrowser\n2. Run auto-analysis first: **Analysis > Auto Analyze**\n3. Check Ghidra console (**Window > Console**) for Java exceptions\n4. Some operations require fully analyzed binaries\n\n### 404 Not Found Errors\n\n**Cause:** Endpoint doesn't exist or wrong URL.\n\n**Solution:**\n\n1. Verify the endpoint exists: `curl -s http://127.0.0.1:8089/mcp/schema` lists every tool the server advertises\n2. Check for typos in endpoint name\n3. Ensure you're using correct HTTP method (GET vs POST)\n4. If a script or prompt calls a tool that worked before 7.0.0 (`decompile_function`, `list_functions`, `search_functions`, `get_function_callers`, `list_imports`, ...), it was consolidated: the [7.0.0 migration guide](docs/project-management/MIGRATION_7.0.0_TOOL_CONSOLIDATION.md) names the replacement for every removed tool\n5. Some routes exist on only one server: `/debugger/*` and `/tool/*` are GUI-only, and `/create_project`, `/close_project`, `/delete_project` and `/list_projects` are headless-only (see the API Reference)\n\n### Python Ghidra scripts fail with \"No script provider found\"\n\n**Cause:** In Ghidra 12.1.4, Jython support is no longer enabled by\ndefault. `.py` scripts need the bundled Jython extension; Python 3\nscripts should use PyGhidra instead of the Ghidra Script Manager.\n\n**Solution:**\n\n1. In the Ghidra Front End, open **File > Install Extensions**.\n2. Check **Jython**, restart Ghidra, then refresh Script Manager.\n3. For new automation, prefer Java Ghidra scripts or PyGhidra.\n\n### Extension not appearing in Install Extensions\n\n**Cause:** JAR file in wrong location.\n\n**Solution:**\n\n1. Manual install location: `~/.config/ghidra/ghidra_12.1.4_PUBLIC/Extensions/GhidraMCP/lib/GhidraMCP.jar`\n   (`%APPDATA%\\ghidra\\...` on Windows, `~/Library/ghidra/...` on macOS)\n2. Or use: **File > Install Extensions > Add** and select the ZIP file — see the\n   [illustrated guide](docs/INSTALL_GUI.md)\n3. Ensure JAR/ZIP was built for your Ghidra version\n\n### Build fails with \"Ghidra dependencies not found\"\n\n**Cause:** Ghidra JARs not installed in local Maven repository (Maven backend only; Gradle reads them straight from the installation).\n\n**Solution:**\n\n```text\n# Windows (recommended)\npython -m tools.setup install-ghidra-deps --ghidra-path \"C:\\ghidra_12.1.4_PUBLIC\"\n```\n\nUnder Gradle, a wall of `package ghidra.program.model.address does not exist`\nerrors instead means `-PGHIDRA_INSTALL_DIR` resolved to nothing — in Git Bash,\nwrite the path with forward slashes.\n\n## 📊 Production Performance\n\n- **MCP Tools**: 209 tools fully implemented (the whole catalog; the GUI plugin serves 205 of them and the headless server 190)\n- **Speed**: Sub-second response for most operations\n- **Efficiency**: 93% reduction in API calls via batch operations\n- **Reliability**: Atomic transactions with all-or-nothing semantics\n- **AI Workflows**: Proven documentation prompts refined across hundreds of real functions\n- **Deployment**: Automated version-aware deployment script\n\n## 🛠️ API Reference\n\n<!-- BEGIN GENERATED API REFERENCE (tools/gen_readme_api_reference.py) -->\n\n209 MCP tools backed by HTTP endpoints, grouped by catalog category. Generated from [tests/endpoints.json](tests/endpoints.json) by `python -m tools.gen_readme_api_reference --write`; the live schema at `/mcp/schema` is authoritative at runtime. Usage patterns: [docs/prompts/TOOL_USAGE_GUIDE.md](docs/prompts/TOOL_USAGE_GUIDE.md).\n\n186 of these are served by both the GUI plugin and the standalone headless server. The rest are marked **(GUI only)** (19) or **(headless only)** (4) — calling one against the other server returns a 404, not an error message. See `python -m tools.audit_server_scope` for how the split is derived.\n\n### Program & Session Management\n\n- `analysis_status` - Get auto-analysis status for open programs\n- `close_program` - Close an open program by project path or name\n- `create_memory_block` - Create memory block, optionally initialized with byte contents (hex or base64)\n- `create_property_map` - Create a user property map to store typed values keyed by address\n- `delete_bookmark` - Delete bookmark\n- `delete_property_map` - Delete a user property map and all values it holds\n- `exit_ghidra` - Save and exit Ghidra\n- `get_address_spaces` - List all physical and overlay address spaces in the program (overlays include is_overlay flag and overlayed_space name)\n- `get_language_metadata` - Dump the program's language description: address spaces, registers, default symbols, endianness, pointer size (issue #192)\n- `get_metadata` - Get program metadata\n- `get_program_options` - Read all options in a program option group with types, current values, defaults, and descriptions\n- `get_property` - Read the value stored at an address in a property map\n- `import_file` - Import a binary file from disk into the current Ghidra project and open it\n- `list_bookmarks` - List bookmarks\n- `list_open_programs` - List open programs\n- `list_project_files` - List project files\n- `list_properties` - List (address, value) entries stored in a property map, with pagination\n- `list_scripts` - List available Ghidra scripts\n- `open_program` - Open program from project\n- `read_memory` - Read raw memory\n- `reanalyze` - Trigger full auto-analysis on a program\n- `remove_program_option` - Remove an option from a program option group\n- `remove_property` - Remove the value stored at a single address in a property map\n- `run_ghidra_script` - Run script with output capture\n- `run_script_inline` - Run inline script code\n- `save_all_programs` - Save all open programs\n- `save_program` - Save current program\n- `set_bookmark` - Set bookmark\n- `set_image_base` - Set the base address of the program (rebases all addresses)\n- `set_memory_block` - Change an existing memory block's permissions or volatility\n- `set_program_option` - Set a typed program option\n- `set_property` - Set a value at an address in a property map\n- `switch_program` - Switch current program\n\n### Project Organization\n\n- `archive_project` - Archive the currently open project to a Ghidra-native .gar file\n- `create_folder` - Create a folder in the project\n- `delete_file` - Delete a file from the project\n- `delete_project` - Delete a Ghidra project **(headless only)**\n- `export_program` - Export an open or project-resident program to a Ghidra Zip File (.gzf)\n- `get_project_info` - Get info about the currently open project\n- `import_program` - Import a Ghidra Zip File (.gzf) into the currently open project as a new DomainFile under target_folder (default '/')\n- `list_projects` - List available Ghidra projects **(headless only)**\n- `move_file` - Move a program file to a different folder in the project, preserving analysis and documentation\n- `move_folder` - Move a project folder and everything under it into another folder\n- `restore_project` - Restore a Ghidra .gar archive into a fresh on-disk project at `parent_dir/project_name`\n\n### Headless Project & Program Lifecycle\n\nAvailable on the standalone headless server (`GhidraMCPHeadlessServer`).\n\n- `close_project` - Close the currently open project **(headless only)**\n- `create_project` - Create a new Ghidra project **(headless only)**\n- `open_project` - Open an existing Ghidra project (.gpr file or directory)\n\n### Listing & Enumeration\n\n- `convert_number` - Convert number between bases\n- `find_functions` - Find functions: every filter is optional, so with none it lists the whole program a page at a time\n- `get_entry_points` - Get program entry points\n- `get_external_location` - Get external location details\n- `get_function_count` - Return the number of functions in the loaded program\n- `list_calling_conventions` - List available calling conventions\n- `list_data_items_by_xrefs` - List data sorted by xref count\n- `list_globals` - List global variables\n- `list_program_items` - List one kind of program inventory with pagination\n- `list_shadowed_globals` - List named global DATA symbols that have NO type of their own because a larger data unit starting at an earlier address covers them\n- `list_strings` - List defined strings\n- `search_strings` - Search defined strings by a regex/substring pattern\n\n### Current GUI Context\n\n- `get_ui_cursor` - What the analyst is looking at right now: cursor address, the function under it, the listing selection, and the focused program — one call instead of four\n\n### Functions: Decompile, Rename, Prototypes & Variables\n\n- `add_function_tag` - Attach tags to ONE function (function + tags) OR MANY in one transaction (assignments=[{function,tags}, ...])\n- `batch_rename_function_components` - Batch rename function components\n- `clear_flow_and_repair` - Run Ghidra's GUI 'Clear Flow and Repair' action on a seed range: clears instruction flow reachable from the seed, then repairs function bodies and re-disassembles retained flow (ClearFlowAndRepairCmd with clear_data=false, clear_labels=false, repair=true)\n- `clear_instruction_flow_override` - Clear flow override\n- `create_function` - Create function at address\n- `delete_function` - Delete function at address\n- `delete_function_tag` - Delete a program-wide function tag definition\n- `disassemble_bytes` - Disassemble byte range\n- `disassemble_function` - Disassemble function\n- `force_decompile` - Force fresh decompilation\n- `get_functions` - Everything about one or many functions in a single call\n- `list_class_members` - List the member functions of a C++ class\n- `list_function_tags` - List all program-wide function tag definitions with their use counts\n- `remove_function_tag` - Detach one or more tags from a function\n- `rename_function` - Rename function by name\n- `rename_variables` - Batch rename variables\n- `set_function_no_return` - Set no-return attribute\n- `set_function_prototype` - Set function prototype (return type, param types, calling convention)\n- `set_function_tag_comment` - Update the comment/description on an existing program-wide function tag\n- `set_function_this_type` - Set the decompiler/database type of the implicit 'this' pointer (ECX on x86 __thiscall/__fastcall)\n- `set_variable_storage` - Set variable storage\n- `set_variable_type` - Set the data type of a function variable (local OR parameter) by name at the decompiler (high-level) layer\n- `set_variables` - Set types and names for multiple variables atomically\n\n### Symbols, Labels & Globals\n\n- `can_rename_at_address` - Check if address can be renamed\n- `create_label` - Create label\n- `delete_label` - Delete label at address\n- `rename_symbol` - Rename a symbol of any kind\n\n### Cross-References & Call Graphs\n\n- `add_memory_reference` - Create a user-defined cross-reference between two memory addresses that the auto-analyzer can't infer (runtime-populated pointer tables, vtables, late-bound function pointers, missed jump/switch tables)\n- `analyze_call_graph` - Analyze function call graph patterns\n- `get_assembly_context` - Get assembly context\n- `get_full_call_graph` - Get full call graph\n- `get_function_call_graph` - Get call graph\n- `get_xrefs_from` - Get references from address\n- `get_xrefs_to` - Get references to address\n- `remove_reference` - Remove memory cross-reference(s) from one address to another — the inverse of add_memory_reference\n\n### Data Types & Structures\n\n- `add_struct_field` - Add struct field\n- `analyze_global_completeness` - Score a global variable's documentation completeness on a budgeted 0-100 scale — the data-address analog of analyze_function_completeness\n- `analyze_struct_field_usage` - Analyze struct field usage\n- `apply_data_classification` - Apply data classification\n- `apply_data_type` - Apply data type\n- `audit_global` - Audit a global variable's documentation state\n- `audit_globals_in_function` - Audit every global variable referenced from within a function in one call\n- `clone_data_type` - Clone data type\n- `create_data_type_category` - Create data type category\n- `create_derived_type` - Create a type built on another: a typedef alias, an array or a pointer\n- `create_enum` - Create enumeration\n- `create_function_signature` - Create function signature type\n- `create_struct` - Create structure\n- `create_union` - Create union\n- `delete_data_type` - Delete data type\n- `find_data_types` - Find data types by name or path pattern, category and kind, one record per type (name, kind, category, size, path)\n- `get_enum_values` - Get enumeration values\n- `get_struct_layout` - Get structure layout\n- `get_type_size` - Get data type size and info\n- `get_valid_data_types` - Get valid data type names\n- `import_data_types` - Import data types from GDT\n- `modify_struct_field` - Modify a field in a structure: retype it (new_type, which also embeds a struct by value, e.g\n- `move_data_type_to_category` - Move data type to category\n- `recreate_struct` - Replace a structure in one step: optionally remove an existing same-named type, then create with fields JSON (same shape as create_struct)\n- `remove_struct_field` - Remove struct field\n- `rename_data_type` - Rename a data type (struct, union, enum, typedef) in place, preserving existing applications of it\n- `resize_struct` - Grow or shrink an existing structure by total byte size\n- `resolve_duplicate_type` - Find duplicate data types by simple name; delete unused /Demangler size-1 stubs when a larger canonical type exists\n- `set_global` - Atomically apply name + type + plate-comment + array length to a global variable\n- `suggest_field_names` - Suggest field names\n- `validate_data_type` - Validate data type syntax\n- `validate_function_prototype` - Validate function prototype\n\n### Comments\n\n- `batch_set_comments` - Set multiple comments\n- `clear_function_comments` - Clear all comments for a function\n- `get_comment` - Get listing comments (plate/pre/eol/post/repeatable) at ANY address, including data addresses (works on functions and data globals alike), for ONE address (address=) or MANY in one call (addresses=a,b,c)\n- `set_comment` - Set a listing comment of a given kind (plate/pre/eol/post/repeatable) at ANY address, including data addresses\n\n### Analysis\n\n- `analyze_control_flow` - Analyze control flow\n- `analyze_data_region` - Analyze data region\n- `analyze_dataflow` - Trace value propagation through a function (PCode graph, forward/backward)\n- `analyze_for_documentation` - Composite RE documentation analysis (decompile + classify + variables + completeness)\n- `analyze_function_complete` - Comprehensive single-call function analysis\n- `analyze_function_completeness` - Analyze documentation completeness\n- `apply_documentation` - Apply documentation to ONE function (fields at the top level) OR MANY (entries=[{address, ...}, ...])\n- `configure_analyzer` - Configure an analysis plugin\n- `detect_array_bounds` - Detect array bounds\n- `find_code_gaps` - Find gaps of undefined bytes between functions in executable memory\n- `find_dead_code` - Find dead code\n- `find_next_undefined_function` - Find next undefined function\n- `find_similar_functions` - Find similar functions\n- `get_field_access_context` - Get field access context\n- `get_function_pcode` - Dump raw P-code for a function (issue #192)\n- `inspect_memory_content` - Inspect memory bytes\n- `list_analyzers` - List available analysis plugins\n- `run_analysis` - Run auto-analysis on the current program\n- `search_byte_patterns` - Search for byte patterns\n- `search_instructions` - Search for instructions by mnemonic and/or operand substring\n\n### Malware & Anti-Analysis\n\n- `analyze_api_call_chains` - Analyze API call chains\n- `detect_crypto_constants` - Detect crypto constants\n- `detect_malware_behaviors` - Detect malware behaviors\n- `extract_iocs_with_context` - Extract IOCs with context\n- `find_anti_analysis_techniques` - Find anti-analysis techniques\n\n### Cross-Binary Documentation & Archive\n\n- `archive_ingest_function` - Ingest a single function's documentation into the cross-version archive (the doc archive service configured via GHIDRA_MCP_ARCHIVE_URL)\n- `archive_ingest_program` - Bulk-ingest every function in a program into the cross-version documentation archive\n- `batch_string_anchor_report` - Report of source file strings and their FUN_* functions\n- `bulk_fuzzy_match` - Bulk cross-binary function matching\n- `compare_programs_documentation` - Compare documentation across programs\n- `diff_functions` - Diff two functions\n- `find_similar_functions_fuzzy` - Cross-binary fuzzy function matching\n- `find_undocumented_by_string` - Find undocumented functions referencing string\n- `get_function_documentation` - Export function documentation\n- `get_function_hash` - Compute the normalized opcode hash of ONE function (function=), or of MANY in one call by omitting it: every function, paged, optionally only the documented or undocumented ones (filter=)\n- `merge_program_documentation` - Bulk merge: copy all RE documentation (function names, signatures, plate comments, instruction comments at EOL/PRE/POST, non-default labels & global symbols) from one program to another at matching addresses\n\n### Health, Schema & Tool Control\n\n- `check_connection` - Liveness probe: {status, server_kind (gui/headless), version, program when one is current}\n- `mcp_health` - Server health: kind (gui/headless), build, current program, uptime, HTTP pool, memory, endpoint count\n- `mcp_schema` - Machine-readable API schema with endpoint metadata\n- `tool_goto_address` - Navigate CodeBrowser listing and decompiler to a specific address **(GUI only)**\n- `tool_running_tools` - List all running Ghidra tool windows **(GUI only)**\n\n### Emulation\n\n- `emulate_function` - Emulate a single function with controlled register/memory inputs\n- `emulate_hash_batch` - Brute-force API hash resolution\n\n### Ghidra Server & Version Control\n\n- `checkin_program` - Check an open program back in to the shared Ghidra Server as a new version\n- `server_admin_set_permissions` - Set user permissions on a repository\n- `server_admin_terminate_all_checkouts` - Terminate all checkouts in a folder recursively\n- `server_admin_terminate_checkout` - Terminate all checkouts on a single file\n- `server_admin_users` - List all users on the server\n- `server_authenticate` - Register server credentials for programmatic authentication\n- `server_checkouts` - List all checked-out files in a folder, including server-side checkouts\n- `server_connect` - Report/establish the Ghidra server connection\n- `server_disconnect` - Disconnect from the Ghidra server\n- `server_repositories` - List repositories on the connected server\n- `server_repository_create` - Create a new repository on the server\n- `server_repository_file` - Get file info from a server repository\n- `server_repository_files` - List files in a server repository folder\n- `server_status` - Check headless server connection status\n- `server_version_control_add` - Add a file to version control\n- `server_version_control_checkout` - Check out a version-controlled file\n- `server_version_control_undo_checkout` - Undo a file checkout\n- `server_version_history` - Get version history for a file\n\n### Debugger (Ghidra TraceRmi)\n\nWhen the bridge's opt-in WinDbg debugger proxies are enabled (`GHIDRA_DEBUGGER_URL` or `GHIDRA_DEBUGGER_TOOLS=1`), a TraceRmi tool below that shares a proxy's name gets a `_2` suffix (e.g. `debugger_status_2`), so both stay reachable.\n\n- `debugger_dynamic_to_static` - Translate a runtime dynamic address from the current trace back to a static Ghidra program address **(GUI only)**\n- `debugger_interrupt` - Interrupt (break into) the running target **(GUI only)**\n- `debugger_launch` - Launch an executable through Ghidra's Trace RMI debugger launcher **(GUI only)**\n- `debugger_launch_offers` - List available debugger launch/attach options for the current program **(GUI only)**\n- `debugger_list_breakpoints` - List all breakpoints in the current trace **(GUI only)**\n- `debugger_modules` - List modules (DLLs/EXEs) loaded in the debugged process **(GUI only)**\n- `debugger_read_memory` - Read memory from the debugged process **(GUI only)**\n- `debugger_registers` - Read CPU registers from the current debug trace snapshot **(GUI only)**\n- `debugger_remove_breakpoint` - Remove a breakpoint at an address **(GUI only)**\n- `debugger_resume` - Resume execution of the debugged process **(GUI only)**\n- `debugger_set_breakpoint` - Set a software execution breakpoint at an address in the trace **(GUI only)**\n- `debugger_stack_trace` - Get the call stack backtrace for the current thread **(GUI only)**\n- `debugger_static_to_dynamic` - Translate a static Ghidra program address to a runtime dynamic address in the current trace **(GUI only)**\n- `debugger_status` - Get debugger status: active trace, thread, execution state, module count **(GUI only)**\n- `debugger_step` - Single-step the debugged process: into the next instruction (follows calls), over it (does not follow calls), or out of the current function (run to return) **(GUI only)**\n- `debugger_traces` - List all open debug traces **(GUI only)**\n\n### System\n\n- `prompt_policy` - Temporarily enable, disable, or query scoped automation prompt handling **(GUI only)**\n\n### Bridge Static Tools\n\nDefined in the Python bridge itself (instance discovery, tool-group management); always available even before a Ghidra connection. The bridge can also proxy 22 `debugger_*` WinDbg tools to an external debugger server; they are off by default and register only when `GHIDRA_DEBUGGER_URL` is set or `GHIDRA_DEBUGGER_TOOLS=1`.\n\n- `check_tools` - Report which tools are currently registered and callable\n- `connect_instance` - Connect the bridge to a specific Ghidra instance\n- `import_file` - Import a binary from disk into the current project and open it\n- `list_instances` - Discover running Ghidra MCP instances (UDS + TCP port scan)\n- `list_tool_groups` - List tool groups and their load state\n- `load_tool_group` - Register a tool group's dynamic tools with the MCP client\n- `search_tools` - Search the full tool catalog by keyword\n- `unload_tool_group` - Unregister a tool group's dynamic tools\n\n<!-- END GENERATED API REFERENCE -->\n\nSee [CHANGELOG.md](CHANGELOG.md) for version history.\n\n## 🏗️ Architecture\n\n```text\n┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐\n│   AI/Automation │◄──►│   MCP Bridge    │◄──►│  Ghidra Plugin  │\n│     Tools       │    │ (bridge_mcp_    │    │ (GhidraMCP.jar) │\n│  (Claude, etc.) │    │  ghidra/)       │    │                 │\n└─────────────────┘    └─────────────────┘    └─────────────────┘\n        │                       │                       │\n   MCP Protocol            HTTP REST              Ghidra API\n(stdio/streamable-http) (localhost:8089)      (Program, Listing)\n```\n\n### Components\n\n- **python/bridge_mcp_ghidra/** — Python MCP server package (ships as the `ghidra-mcp-bridge` wheel; `bridge-mcp-ghidra` console script) that translates MCP protocol to HTTP calls (209 catalog entries)\n- **GhidraMCP.jar** — Ghidra plugin that exposes analysis capabilities via HTTP (205 endpoints)\n- **GhidraMCPHeadlessServer** — Standalone headless server — 190 endpoints, no GUI required\n- **ghidra_scripts/** — Collection of automation scripts for common tasks\n\n## 🔧 Development\n\n### Building from Source\n\n```bash\n# Recommended: direct Python-first workflow\npython -m tools.setup ensure-prereqs --ghidra-path \"C:\\ghidra_12.1.4_PUBLIC\"\npython -m tools.setup build\npython -m tools.setup deploy --ghidra-path \"C:\\ghidra_12.1.4_PUBLIC\"\n\n# Version bump (updates all maintained version references atomically)\npython -m tools.setup bump-version --new X.Y.Z\n```\n\nBoth Java backends are maintained. Gradle (`./gradlew`, wrapper committed) is the default for local work and writes to `build/`; CI builds and gates with Maven, which writes to `target/`. `tools.setup` routes through Maven unless `TOOLS_SETUP_BACKEND=gradle`. Three things exist only under Maven: regenerating `tests/endpoints.json` (`mvn test -Dtest=RegenerateEndpointsJson -Dregenerate=true`), the JaCoCo coverage gate, and the `headless`/`docker` build profiles.\n\n### Command Reference\n\n| Command | What it does |\n| --------- | ------------- |\n| `ensure-prereqs` | Install Python deps + Ghidra Maven JARs in one shot. Start here on a new machine. |\n| `preflight` | Validate Python, build tool, Ghidra path, and JAR availability without making changes. Add `--strict` to also check network reachability. |\n| `build` | Build the plugin JAR and extension ZIP via Maven (or Gradle when `TOOLS_SETUP_BACKEND=gradle`). |\n| `deploy` | Copy the built extension into the Ghidra profile and patch `FrontEndTool.xml` for auto-activation. |\n| `start-ghidra` | Launch the configured Ghidra installation. |\n| `clean` | Remove the selected backend's build output (`target/`, or `build/` under Gradle). |\n| `clean-all` | Remove build outputs plus local cache artifacts (`.m2` Ghidra JARs, etc.). |\n| `install-ghidra-deps` | Install only the Ghidra JARs into `~/.m2`. Useful when the build environment changes. |\n| `install-python-deps` | Install the Python dependency groups via `uv sync`. |\n| `run-tests` | Run the backend's whole Java `test` task (`mvn test`, or `gradlew test` under Gradle). The integration classes in it need a live Ghidra on port 8089. |\n| `verify-version` | Check `pom.xml`'s Ghidra version against the `--ghidra-path` installation (same major.minor series passes). |\n| `bump-version --new X.Y.Z` | Atomically update all version references. Pass `--tag` to create a git tag. |\n\nCommon flags accepted by most commands:\n\n| Flag | Description |\n| ------ | ------------- |\n| `--ghidra-path PATH` | Ghidra installation directory. Defaults to `GHIDRA_PATH` from `.env`. |\n| `--dry-run` | Print actions without executing them. |\n| `--force` | Reinstall Ghidra JARs even if already present (`install-ghidra-deps`, `ensure-prereqs`). |\n| `--with-debugger` | Force-install debugger Python requirements (Windows only). |\n| `--use-debugger-toggle` | Read `INSTALL_DEBUGGER_DEPS` from `.env` to decide whether to install debugger deps. |\n| `--test TIER` | (`deploy` only) Opt into live deploy regression tiers such as `release` or `debugger-live`. |\n| `--strict` | (`preflight` only) Also check network reachability for Maven Central and PyPI. |\n\nDeploy test tiers are opt-in because benchmark tiers can import/reset\n`Benchmark.dll` and `BenchmarkDebug.exe` in the active Ghidra project. Use\n`--test release` before cutting releases, or set\n`GHIDRA_MCP_DEPLOY_TESTS=release` in a local `.env` when you want every deploy\non your machine to run the live benchmark regression. The value is validated\nagainst the same tier list `--test` accepts, and an unknown tier is refused\nrather than skipped. See [Testing and Release Regression](docs/TESTING.md).\n\n```text\n# Standard first-time setup and deploy\npython -m tools.setup ensure-prereqs --ghidra-path \"C:\\ghidra_12.1.4_PUBLIC\"\npython -m tools.setup build\npython -m tools.setup deploy --ghidra-path \"C:\\ghidra_12.1.4_PUBLIC\"\n\n# Preflight check before deploying\npython -m tools.setup preflight --strict --ghidra-path \"C:\\ghidra_12.1.4_PUBLIC\"\n\n# Version bump and tag\npython -m tools.setup bump-version --new X.Y.Z --tag\n\n# Run the Java test suite (integration classes need a live Ghidra)\npython -m tools.setup run-tests\n\n# Show full help\npython -m tools.setup --help\n```\n\n### Project Structure\n\n```text\nghidra-mcp/\n├── pyproject.toml           # uv project (ghidra-mcp-bridge wheel + dependency groups)\n├── python/bridge_mcp_ghidra/ # MCP server package (Python, 209 catalog entries)\n├── src/main/java/           # Ghidra plugin + headless server (Java)\n│   └── com/xebyte/\n│       ├── GhidraMCPPlugin.java         # GUI plugin (205 endpoints)\n│       ├── headless/                    # Headless server (190 endpoints)\n│       └── core/                        # Shared service layer (`*Service.java`, `@McpTool`-annotated)\n├── ghidra_scripts/          # Automation scripts for batch workflows\n├── tests/                   # Python unit tests + endpoint catalog\n│   ├── unit/               # Catalog consistency, schema, tool function tests\n│   └── endpoints.json      # Endpoint catalog (the authoritative tool list)\n├── docs/                    # Documentation\n│   ├── prompts/            # AI workflow prompts (V5 documentation workflows)\n│   ├── releases/           # Version release notes\n│   └── project-management/ # Contributor planning docs (Gradle migration, etc.)\n├── tools/setup/             # Build and deployment CLI (python -m tools.setup)\n├── docker/                  # Headless server + bridge containers\n└── .github/workflows/      # CI/CD pipelines\n```\n\n### Library Dependencies\n\nUnder the Maven backend, Ghidra JARs must be installed into your local Maven repository (`~/.m2/repository`) before compilation.\nThis is a one-time setup per machine, and again when your Ghidra version changes.\n`ensure-prereqs` does it for you; Gradle needs no such step, because it reads the jars from the installation.\n\nThe tool enforces version consistency between:\n\n- `pom.xml` (`ghidra.version`)\n- `--ghidra-path` version segment (e.g., `ghidra_12.1.4_PUBLIC`)\n\nIf they are not in the same major.minor series, deployment fails fast with a clear error (a different patch release of the same series is accepted).\n\n### Troubleshooting: Version Mismatch\n\nIf you see a version mismatch error, align both values:\n\n1. `pom.xml` → `ghidra.version`\n2. `--ghidra-path` version segment (`ghidra_X.Y.Z_PUBLIC`)\n\nThen rerun:\n\n```text\npython -m tools.setup preflight --ghidra-path \"C:\\ghidra_12.1.4_PUBLIC\"\n```\n\n```text\n# Windows\npython -m tools.setup install-ghidra-deps --ghidra-path \"C:\\path\\to\\ghidra_12.1.4_PUBLIC\"\n```\n\n**Required Libraries (18 JARs, as listed in `tools/setup/ghidra.py`):**\n\n| Library | Source Path | Purpose |\n| --------- | ------------ | --------- |\n| **Base.jar** | `Features/Base/lib/` | Core Ghidra functionality |\n| **Decompiler.jar** | `Features/Decompiler/lib/` | Decompilation engine |\n| **PDB.jar** | `Features/PDB/lib/` | Microsoft PDB symbol support |\n| **FunctionID.jar** | `Features/FunctionID/lib/` | Function identification |\n| **SoftwareModeling.jar** | `Framework/SoftwareModeling/lib/` | Program model API |\n| **Project.jar** | `Framework/Project/lib/` | Project management |\n| **Docking.jar** | `Framework/Docking/lib/` | UI docking framework |\n| **Generic.jar** | `Framework/Generic/lib/` | Generic utilities |\n| **Utility.jar** | `Framework/Utility/lib/` | Core utilities |\n| **Gui.jar** | `Framework/Gui/lib/` | GUI components |\n| **FileSystem.jar** | `Framework/FileSystem/lib/` | File system support |\n| **Graph.jar** | `Framework/Graph/lib/` | Graph/call graph analysis |\n| **DB.jar** | `Framework/DB/lib/` | Database operations |\n| **Emulation.jar** | `Framework/Emulation/lib/` | P-code emulation |\n| **Help.jar** | `Framework/Help/lib/` | Help system |\n| **Debugger-api.jar** | `Debug/Debugger-api/lib/` | Debugger service API |\n| **Framework-TraceModeling.jar** | `Debug/Framework-TraceModeling/lib/` | Debug trace model |\n| **Debugger-rmi-trace.jar** | `Debug/Debugger-rmi-trace/lib/` | Trace RMI debugger connection |\n\n> **Note**: Libraries are NOT included in the repository (see `.gitignore`). You must install them from your Ghidra installation before building.\n\n<!-- two separate call-outs, not one blockquote (MD028) -->\n\n> **Automation entry point**:\n>\n> - `python -m tools.setup` is the supported setup/build/deploy/versioning interface\n> - use `ensure-prereqs`, `build`, `deploy`, `preflight`, `clean-all`, and `bump-version` directly\n> - these commands use Maven unless `TOOLS_SETUP_BACKEND=gradle` is set\n\n### Development Features\n\n- **Automated Deployment**: Version-aware deployment script\n- **Batch Operations**: Reduces API calls by 93%\n- **Atomic Transactions**: All-or-nothing semantics\n- **Comprehensive Logging**: Debug and trace capabilities\n\n## 📚 Documentation\n\n### Core Documentation\n\n- [Documentation Index](docs/README.md) - Complete documentation navigation\n- [Project Structure](docs/PROJECT_STRUCTURE.md) - Project organization guide\n- [Testing and Release Regression](docs/TESTING.md) - Local tests, CI, live Ghidra regression, and release gates\n- [Naming Conventions](docs/NAMING_CONVENTIONS.md) - Code naming standards\n- [Hungarian Notation](docs/HUNGARIAN_NOTATION.md) - Variable naming guide\n\n### AI Workflow Prompts\n\n- [Function Documentation V5](docs/prompts/FUNCTION_DOC_WORKFLOW_V5.md) — Primary workflow: 7-step process with Hungarian notation, type auditing, and verification scoring\n- [Batch Documentation V5](docs/prompts/FUNCTION_DOC_WORKFLOW_V5_BATCH.md) — Parallel subagent dispatch for multi-function processing\n- [Orphaned Code Discovery](docs/prompts/ORPHANED_CODE_DISCOVERY_WORKFLOW.md) — Automated scanner for undiscovered functions\n- [Data Type Investigation](docs/prompts/DATA_TYPE_INVESTIGATION_QUICK.md) — Structure discovery and field analysis\n- [Global Data Analysis](docs/prompts/GLOBAL_DATA_ANALYSIS_WORKFLOW.md) — Global naming and analysis\n- [Quick Start Prompt](docs/prompts/QUICK_START_PROMPT.md) — Simplified beginner workflow\n- [All Prompts](docs/prompts/README.md) — Complete prompt index\n\n### Release History\n\n- [Complete Changelog](CHANGELOG.md) - All version release notes\n- [Release Notes](docs/releases/) - Detailed release documentation\n\n## 🐳 Headless Server (Docker)\n\nGhidraMCP includes a headless server mode for automated analysis without the Ghidra GUI.\n\n### Quick Start with Docker\n\n```bash\n# Build and run (the compose files live in docker/). The token is required:\n# the container binds 0.0.0.0, and the server refuses a non-loopback bind\n# without one.\ncd docker\nexport GHIDRA_MCP_AUTH_TOKEN=$(openssl rand -hex 32)\ndocker compose up -d --build\n\n# Test connection (/check_connection and /mcp/health need no token)\ncurl http://localhost:8089/check_connection\n# {\"status\": \"ok\", \"server_kind\": \"headless\", \"version\": \"7.0.0\"}\n```\n\nThis starts the headless server on `:8089` and the MCP bridge on `:8081`\n(streamable-http at `/mcp`). See [docker/README.md](docker/README.md) for the\nfull deployment guide.\n\n### Headless API Workflow\n\n```bash\nAUTH=\"Authorization: Bearer $GHIDRA_MCP_AUTH_TOKEN\"\n\n# 1. Import a binary into the open project (auto-analysis runs by default)\ncurl -X POST -H \"$AUTH\" -H 'Content-Type: application/json' \\\n     -d '{\"file_path\": \"/data/program.exe\"}' http://localhost:8089/import_file\n\n# 2. Re-run auto-analysis later if needed\ncurl -X POST -H \"$AUTH\" http://localhost:8089/run_analysis\n\n# 3. List discovered functions\ncurl -H \"$AUTH\" \"http://localhost:8089/find_functions?limit=20\"\n\n# 4. Decompile a function\ncurl -H \"$AUTH\" \"http://localhost:8089/get_functions?function=0x401000&fields=decompiled_code\"\n\n# 5. Get metadata\ncurl -H \"$AUTH\" http://localhost:8089/get_metadata\n```\n\n### Key Headless Endpoints\n\n| Endpoint | Method | Description |\n| ---------- | -------- | ------------- |\n| `/import_file` | POST | Import a binary into the project and open it |\n| `/open_program` | POST | Open a program already in the project (any `program=` also opens on demand) |\n| `/run_analysis` | POST | Run Ghidra auto-analysis |\n| `/find_functions` | GET | List or filter discovered functions |\n| `/list_program_items` | GET | `kind=imports`, `exports`, `segments`, `classes`, `namespaces`, `data_items` or `external_locations` |\n| `/get_functions` | GET | Decompiled code, signature, callers, callees and more for one or many functions (`fields=` picks) |\n| `/create_function` | POST | Create function at address |\n| `/get_metadata` | GET | Get program metadata |\n| `/create_project` | POST | Create a Ghidra project (headless only) |\n| `/list_analyzers` | GET | List available analyzers |\n| `/server/status` | GET | Check Ghidra Server connection |\n\n### Configuration\n\nEnvironment variables for Docker:\n\n- `GHIDRA_MCP_AUTH_TOKEN` - Bearer token, **required** by the compose files (see above)\n- `GHIDRA_MCP_PORT` - Server port (default: 8089)\n- `GHIDRA_MCP_BIND_ADDRESS` - Bind address (default: 0.0.0.0 in Docker)\n- `JAVA_OPTS` - JVM options (default: -Xmx4g -XX:+UseG1GC)\n\n## 🤝 Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for detailed contribution guidelines.\n\n### Quick Start\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Build and test your changes (`./gradlew buildExtension -PGHIDRA_INSTALL_DIR=/path/to/ghidra`, or `mvn clean package assembly:single -DskipTests` under the Maven backend)\n4. Update documentation as needed\n5. Commit your changes (`git commit -m 'Add amazing feature'`)\n6. Push to the branch (`git push origin feature/amazing-feature`)\n7. Open a Pull Request\n\n## 📄 License\n\nThis project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.\n\n## 🏆 Production Status\n\n| Metric | Value |\n| -------- | ------- |\n| **Version** | 7.0.0 |\n| **MCP Tools** | 209 fully implemented |\n| **GUI Endpoints** | 205 (GhidraMCPPlugin) |\n| **Headless Endpoints** | 190 (GhidraMCPHeadlessServer) |\n| **Compilation** | ✅ 100% success |\n| **Batch Efficiency** | 93% API call reduction |\n| **AI Workflows** | 7 proven documentation workflows |\n| **Ghidra Scripts** | Automation scripts included |\n| **Documentation** | Comprehensive with AI prompts |\n\nSee [CHANGELOG.md](CHANGELOG.md) for version history and release notes.\n\n## 🙏 Acknowledgments\n\nThis project was originally derived from [LaurieWired/GhidraMCP](https://github.com/LaurieWired/GhidraMCP) in August 2025 and has since been substantially rewritten and extended. We acknowledge LaurieWired's original work as the starting point. See [NOTICE](NOTICE) for license attribution.\n\n### Powered by\n\n[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSource)\n\nTooling provided by JetBrains through their [Open Source Support Program](https://jb.gg/OpenSource).\n\n## 👥 Contributors\n\nThis project has benefited from the work of dedicated contributors:\n\n### Core Contributors\n\n**[@heeen](https://github.com/heeen)** — Significant contributions including:\n\n- Fuzzy function matching and structured diff for cross-binary comparison (#13)\n- Script execution improvements and bug fixes (#12)\n- New API endpoints: `save_program`, `exit_ghidra`, `delete_function`, `create_memory_block`, `run_script_inline` (#11)\n- Architectural vision: annotation-driven design, UDS transport, Python bridge optimization proposals\n\n**[@huehuehuehueing](https://github.com/huehuehuehueing)** — Significant contributions including:\n\n- Address-space prefix support — added `<space>:<hex>` syntax (e.g., `mem:1000`, `code:ff00`) to address parsing across the entire endpoint surface, unlocking multi-space targets like embedded firmware (#84, closes #65)\n- Optional `program` parameter + required-param schema fixes — made `program` optional on every endpoint with a sane currentProgram fallback, and fixed several required-vs-optional schema bugs the catalog had inherited (#92)\n- Seeded #44 (data-type / enum tools) — the issue that motivated the v5.0 enum + struct enforcement layer\n\n- **Ghidra Team** - For the incredible reverse engineering platform\n- **Model Context Protocol** - For the standardized AI integration framework\n- **Contributors** - For testing, feedback, and improvements\n\n---\n\n## 🔗 Related Projects\n\n- [re-universe](https://github.com/bethington/re-universe) — Ghidra BSim PostgreSQL platform for large-scale binary similarity analysis. Pairs perfectly with GhidraMCP for AI-driven reverse engineering workflows.\n- [cheat-engine-server-python](https://github.com/bethington/cheat-engine-server-python) — MCP server for dynamic memory analysis and debugging.\n\n---\n\n**Ready for production deployment with enterprise-grade reliability and comprehensive binary analysis capabilities.**\n"}