Skip to main content

Cross-platform manager for compatible Ghidra and GhidraMCP installations

Project description

Ghidra Manager logo

Ghidra Manager

Ghidra Manager is a cross-platform CLI for installing Ghidra and compiling a curated set of extensions for that exact release. It tracks stable GitHub releases, requires GitHub-published SHA-256 digests for release assets, builds plugins from immutable release commits, and retains the active installation plus one complete rollback pair.

The ghidra-manager Python command is the only supported user-facing entrypoint on Windows, Linux, and macOS.

For Codex workflows, this repository also includes the ghidra-manager skill. Invoke $ghidra-manager to inspect managed state, choose reviewed plugins, launch and verify projects, connect the bridge, target exact programs inside a project, compare binaries, or work on the manager itself. The skill follows the same safety rules as the CLI: use instances as runtime truth, identify programs explicitly when more than one is open, and review comparison results before a write. The skill is maintained in the source repository and is not installed into Codex by the PyPI package.

Command Summary

The examples below abbreviate paths, process IDs, and versions with angle brackets. Running ghidra-manager without a subcommand is equivalent to ghidra-manager sync.

Command Options Sample output
ghidra-manager help Global: -h, --help usage: ghidra-manager ... {help,sync,status,...}
ghidra-manager --version None ghidra-manager 0.1.0
ghidra-manager sync --dry-run resolves without changing state Active pair: Ghidra <version>; plugins ghidra-lx-loader, mcp.
ghidra-manager status None Active Ghidra: <version>
ghidra-manager doctor [PROJECT] --program PROGRAM, --json, --base-port PORT Doctor result: ready
ghidra-manager rollback None Rolled back to Ghidra <version>; plugins mcp.
ghidra-manager projects None Projects known to Ghidra <version>:
ghidra-manager plugins discover --json mcp is reported as selected or available
ghidra-manager plugins list --json Plugins for Ghidra <version>:
ghidra-manager plugins install PLUGIN mcp or ghidra-lx-loader Installed plugin mcp <version> for Ghidra <version>.
ghidra-manager plugins remove PLUGIN mcp or ghidra-lx-loader Removed plugin mcp from Ghidra <version>.
ghidra-manager open PROJECT --program PROGRAM, --timeout SECONDS, --base-port PORT GhidraMCP instance ready:
ghidra-manager launch [GHIDRA_ARGS...] Remaining arguments pass through to Ghidra Launching Ghidra <version> with JDK 21...
ghidra-manager launch-multi [PROJECT.gpr ...] --count N, --timeout SECONDS, --base-port PORT GhidraMCP instances ready:
ghidra-manager bridge [BRIDGE_ARGS...] Remaining arguments pass through to the bridge <stdio bridge starts and waits for MCP traffic>
ghidra-manager compare SOURCE_PROJECT TARGET_PROJECT --base-port PORT Plan saved: <manager-home>/compare-plans/<plan>.json
ghidra-manager compare --apply PLAN No source or target arguments Applied <count> operations to <target>.
ghidra-manager instances --base-port PORT MCP port 8089

launch and bridge deliberately pass remaining arguments through rather than interpreting them. Environment variables can also set the manager home, MCP base port, launch timeout, and GitHub authentication; those are described in the detailed sections below.

Install

Install uv and a 64-bit JDK 21. The CLI uses uv to provision Python 3.13, so a system Python installation is not required.

Install the published CLI in an isolated tool environment:

uv tool install ghidra-manager
ghidra-manager help

Upgrade it independently of managed Ghidra installations and plugins:

uv tool upgrade ghidra-manager

For development from a checkout:

uv sync --locked --group dev
uv run ghidra-manager help

JDK discovery checks JAVA_HOME and PATH on every platform. macOS also uses /usr/libexec/java_home and Homebrew when available.

Install, Update, And Roll Back

Resolve and install the newest stable Ghidra release. A fresh installation has no plugins; later syncs rebuild the active pair's selected plugins for the new Ghidra release:

ghidra-manager sync

Preview release selection without changing managed state:

ghidra-manager sync --dry-run

Show active, retained, and newest upstream versions:

ghidra-manager status

Representative output:

Active Ghidra:      <ghidra-version>
Active plugin:      ghidra-lx-loader <plugin-version> (installed)
Active plugin:      mcp <plugin-version> (installed)
Previous pair:      Ghidra <previous-version>; plugins mcp
Upstream Ghidra:    <ghidra-version>

Check local reverse-engineering readiness without resolving upstream releases:

ghidra-manager doctor
ghidra-manager doctor ripper --program RIPPER.LE
ghidra-manager doctor ripper --program RIPPER.LE --json

doctor verifies the active pair, managed installation and MCP plugin, JDK 21, recorded project, responding MCP identity and versions, expected open program, endpoint catalog, and analysis status. Errors produce a nonzero exit code; warnings remain successful so partial environments can be inspected.

Representative ready output:

OK      active-pair: Ghidra <version>; plugins ghidra-lx-loader, mcp
OK      project: /projects/ripper.gpr
OK      instance: PID <pid> on http://127.0.0.1:8089
OK      program: RIPPER.EXE is open
OK      analysis: RIPPER.EXE analyzed; 847 functions
Doctor result: ready

Return to the retained previous pair:

ghidra-manager rollback

Close managed Ghidra processes before syncing or rolling back. Rollback reinstalls the retained pair's complete plugin set before changing active state, including when both pairs share one Ghidra release.

GH_TOKEN or GITHUB_TOKEN may authenticate GitHub API requests.

Manage Plugins

List the reviewed plugins available from the manager's bundled registry:

ghidra-manager plugins discover
ghidra-manager plugins discover --json
ghidra-manager plugins list
ghidra-manager plugins list --json

The bundled registry is src/ghidra_manager/plugin_registry.json, not a remote marketplace. Its initial reviewed catalog contains mcp and ghidra-lx-loader. Discovery is read-only and works before Ghidra is installed. Plugin versions are resolved from stable GitHub releases when a plugin is installed or updated rather than being pinned in the registry.

discover shows registry membership and whether each plugin is selected in the active pair:

mcp | GhidraMCP | selected | bethington/ghidra-mcp
ghidra-lx-loader | Ghidra LX Loader | selected | yetmorecode/ghidra-lx-loader

list shows the resolved artifacts for the active Ghidra release:

Plugins for Ghidra <ghidra-version>:
ghidra-lx-loader | <plugin-version> | <release-tag> | <source-commit>
mcp | <plugin-version> | <release-tag> | <source-commit>

Install or update one plugin for the active Ghidra release:

ghidra-manager sync
ghidra-manager plugins install mcp
ghidra-manager plugins install ghidra-lx-loader
ghidra-manager plugins remove ghidra-lx-loader

Plugin installation requires an active Ghidra installation and refuses to replace extensions while a manager-owned Ghidra process is running. The manager resolves the latest stable plugin release tag to its immutable commit, uses the target Ghidra distribution's Gradle wrapper, validates the generated extension ZIP, then activates the complete selected set transactionally. Removal is idempotent and creates a rollback pair that retains the removed artifact until it is no longer referenced.

Install mcp before using bridge, instances, open, launch-multi, or compare. Install ghidra-lx-loader before importing LE/LX binaries such as DOS/4GW or OS/2 Linear Executables.

Managed State

Fresh installations use the native user-data directory:

  • Windows: %LOCALAPPDATA%\ghidra-manager
  • Linux: ${XDG_DATA_HOME:-~/.local/share}/ghidra-manager
  • macOS: ~/Library/Application Support/ghidra-manager

Set GHIDRA_MANAGER_HOME to override this location.

<manager-home>/
├── ghidra/<version>/
├── plugins/<plugin>/<ghidra-version>/<source-commit>/
├── pairs/<ghidra-and-plugin-manifest>/metadata.json
├── state.json
├── gradle-cache/
├── launch-logs/
├── compare-plans/
├── python/
└── uv-cache/

state.json records the current and previous pair IDs without requiring symlinks. Each pair records its exact Ghidra release and sorted plugin manifest. Downloads, builds, and extension installation are validated before an atomic state replacement. Failed extension replacement restores every affected plugin directory.

When first run from a checkout containing the former .managed/ layout, the CLI adopts that directory in place and converts valid current and previous pair metadata into versioned JSON. Legacy symlinks are not required afterward. Existing GhidraMCP pairs migrate with mcp selected and retain their legacy artifact paths until neither current nor previous references them.

Run Ghidra And GhidraMCP

Install the MCP plugin first if it is not already selected:

ghidra-manager plugins install mcp

List recorded projects to resolve the exact project path:

ghidra-manager projects

Representative output:

Projects known to Ghidra <version>:
ripper | ready | last-opened | active MCP port 8089 (PID <pid>) | /projects/ripper.gpr
harvester | ready | recent | inactive | /projects/harvester.gpr
2 recorded projects from <ghidra-settings>/preferences

Open a recorded project by name or .gpr path and wait for its GhidraMCP endpoint:

ghidra-manager open ripper
ghidra-manager open /path/to/ripper.gpr --program RIPPER.LE

open retains a startup log, rejects an already-active project, and reports success only after a new endpoint identifies the expected project and optional program. Use --timeout or --base-port to override discovery defaults.

Representative output:

Opening /projects/ripper.gpr with Ghidra <version> and JDK 21...
Launcher PID <launcher-pid> | log <manager-home>/launch-logs/<launch>.log
Waiting up to 180 seconds for project ripper on ports 8089-8104...
GhidraMCP instance ready:
MCP port 8089 | PID <pid> | project ripper | http://127.0.0.1:8089
Open programs: RIPPER.LE

Launch the active distribution, optionally opening a project by its .gpr path:

ghidra-manager launch
ghidra-manager launch /path/to/project.gpr

launch passes all remaining arguments directly to Ghidra. It does not resolve a recorded name such as ripper to its project path, and launch --help is therefore forwarded to Ghidra rather than handled as CLI help. Use ghidra-manager help for the manager command summary.

On the first launch for a new Ghidra settings version:

  1. Open or create a project and launch CodeBrowser.
  2. Select File > Configure > Configure All Plugins and enable GhidraMCP.
  3. Select Tools > GhidraMCP > Start MCP Server.

The plugin listens on 127.0.0.1:8089 by default and may fall back through port 8104. Verify the launch by listing responding instances; the launcher banner or exit code alone does not prove that Ghidra stayed running:

ghidra-manager instances

Representative output:

MCP port 8089 | PID <pid> | project ripper | http://127.0.0.1:8089

projects reads Ghidra's settings registry, while instances reports live GhidraMCP endpoints. A running Project Window does not appear in instances until CodeBrowser is open and the plugin server is active.

Launch one process per project and wait for new MCP endpoints:

ghidra-manager launch-multi \
  /path/to/first-project.gpr \
  /path/to/second-project.gpr

Without paths, launch-multi opens two instances by default. Use --count, --timeout, or --base-port to override launch behavior. Projects and project names must be distinct, and already-active projects are rejected. Detached startup logs are retained under launch-logs/.

Register the managed stdio bridge with Codex:

codex mcp add ghidra -- ghidra-manager bridge

The bridge uses uv-managed Python 3.13 and the upstream retained script:

ghidra-manager bridge --help

GHIDRA_MCP_BASE_PORT changes the discovery base port. GHIDRA_MCP_LAUNCH_TIMEOUT changes the multi-launch timeout.

Use With Codex

The bundled ghidra-manager skill teaches Codex the manager's command boundaries, plugin lifecycle, launch checks, and comparison safeguards. The repository directory is the source of truth; install or link skills/ghidra-manager as $CODEX_HOME/skills/ghidra-manager and start a new Codex session to make $ghidra-manager available. Once the skill and bridge are available, a request can name the skill explicitly:

Use $ghidra-manager to inspect the active Ghidra pair, discover the reviewed
plugins, open the ripper project, and confirm its MCP program before making any
changes.

The CLI identifies a live project; GhidraMCP identifies programs inside it. When multiple programs are open, use their full Ghidra project paths on MCP calls instead of relying on the active tab:

/RIPPER.LE
/v1.05/RIPPER.EXE

--program is optional on open and doctor. Supply it when readiness depends on one exact open program. Within MCP calls, always supply program when more than one program is open.

End-to-End Example: Compare RIPPER With v1.05

This example starts with an analyzed /RIPPER.LE in a recorded project named ripper and a patched DOS/4GW executable at /path/to/Ripper/RIPPER/RIPPER.EXE.

1. Install the reviewed runtime

Install Ghidra, inspect the bundled registry, and add both plugins before launching Ghidra:

ghidra-manager sync
ghidra-manager plugins discover
ghidra-manager plugins install mcp
ghidra-manager plugins install ghidra-lx-loader
ghidra-manager plugins list

Plugin installation compiles each stable release for the active Ghidra version. Close manager-owned Ghidra processes before installing or updating a plugin.

2. Register the bridge and verify the project

codex mcp add ghidra -- ghidra-manager bridge
ghidra-manager projects
ghidra-manager open ripper --program RIPPER.LE
ghidra-manager instances
ghidra-manager doctor ripper --program RIPPER.LE

At this point the manager has proven which Ghidra, plugin set, JDK, project, process, port, and program are in use.

3. Import the patched executable through MCP

The manager does not duplicate Ghidra's import API. Ask Codex to use GhidraMCP after the verified launch:

Use $ghidra-manager. In the ripper project, create the virtual folder /v1.05
and import /path/to/Ripper/RIPPER/RIPPER.EXE there with the LX loader. Keep
/RIPPER.LE unchanged. Run analysis, save the new program, and verify its format,
project path, and function count through MCP.

The important verification is the loader result, not just a successful import:

name:              RIPPER.EXE
project path:      /v1.05/RIPPER.EXE
executable format: Linear Executable (LE-Style DOS)
language:          x86:LE:32:default
analysis:          complete

4. Compare and synchronize conservatively

Both binaries can share one MCP instance while remaining unambiguous:

/RIPPER.LE            original documentation source
/v1.05/RIPPER.EXE     patched v1.05 target

For programs in one project, ask Codex to query GhidraMCP with those full paths. Transfer function documentation only for unique exact normalized-code matches, preserve target conflicts, and review changed functions separately. Portable datatype graphs such as structures, enums, arrays, pointers, and function signatures can be copied source-to-target after equivalence checks; do not copy address-derived switchD_* namespaces between rebuilt binaries.

The RIPPER run that informed this tutorial produced:

Original /RIPPER.LE:        1,682 functions, 561 data types
Target /v1.05/RIPPER.EXE:     847 functions, 567 data types
Unique exact matches:          675 functions
Target documentation:          679/847 functions (80.2%)
Copied source data types:       561/561 equivalent

Those counts are specific to this sample. The reusable value is the guarded workflow: reproducible plugins, verified runtime identity, explicit program paths, exact-match transfer, conflict preservation, and post-save health checks.

Finish with:

ghidra-manager doctor ripper --program RIPPER.EXE
ghidra-manager instances

Compare Binaries Across Instances

Treat the first responding project as the documentation source and the second as the target:

ghidra-manager compare source-project target-project

Comparison is read-only. It summarizes inventories first, then matches functions only when normalized opcode hash and instruction count uniquely identify one function in each program. It reports missing function metadata, structures, and enumerations without overwriting conflicting target state.

Each comparison writes a private versioned plan under compare-plans/ and retains the newest ten. Apply a reviewed plan explicitly:

ghidra-manager compare --apply \
  /path/to/compare-plans/<timestamp>-source-to-target.json

Apply rechecks target project, PID, function hashes, documentation, and type state. It stops on the first rejected operation and deliberately leaves changes unsaved in Ghidra for review or undo.

compare requires two distinct responding project instances. For two programs inside one project, use the full-path MCP workflow in the RIPPER example instead of trying to launch the same project twice.

Validation

uv sync --locked --group dev
uv run pytest
uv run ruff check .
uv run mypy
uv build
uv run ghidra-manager sync --dry-run
git diff --check

Pull-request CI runs on Ubuntu x64, Windows x64, macOS ARM64, and macOS x64. The manually triggered real-integration workflow performs a real sync, idempotent resync, status check, and bridge smoke test on the same matrix.

Troubleshooting

  • JDK 21 not found: set JAVA_HOME or put Java 21 on PATH.
  • GitHub rate limit: set GH_TOKEN or GITHUB_TOKEN.
  • MCP connection refused: open CodeBrowser, enable GhidraMCP, and start its server from Tools > GhidraMCP.
  • MCP plugin is not installed: close managed Ghidra processes, run ghidra-manager plugins install mcp, then relaunch Ghidra.
  • Plugin build failed: inspect the reported Gradle output and keep using the unchanged active pair; the failed build is never activated.
  • Invalid project: pass the .gpr path reported by the projects command, not only its display name.
  • Open timeout: finish opening CodeBrowser, enable GhidraMCP, and inspect the retained log path reported by open.
  • Doctor not ready: address each ERROR check, then rerun the same project and program selection before starting a write workflow.
  • Multi-launch timeout: finish opening CodeBrowser in each project, then run ghidra-manager instances.
  • Update refused: close all processes running from the managed Ghidra component directory.
  • Compare target changed: generate a fresh plan instead of applying stale operations.
  • No project registry: launch Ghidra once so it creates platform settings and records a project.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ghidra_manager-0.1.0.tar.gz (1.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ghidra_manager-0.1.0-py3-none-any.whl (46.7 kB view details)

Uploaded Python 3

File details

Details for the file ghidra_manager-0.1.0.tar.gz.

File metadata

  • Download URL: ghidra_manager-0.1.0.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ghidra_manager-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b7cb49df751a42d7bbd055ee64118d2941ed6df9cb38f00d48daf38859f3562c
MD5 e0fa0990656885f220ed283546655a92
BLAKE2b-256 053f6cd73cddfc33cd954ad5f9e3be560123bc8f98b18032b4e0a1324be925cd

See more details on using hashes here.

Provenance

The following attestation bundles were made for ghidra_manager-0.1.0.tar.gz:

Publisher: release.yml on alexbevi/ghidra-manager

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ghidra_manager-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ghidra_manager-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 46.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for ghidra_manager-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cd218e61d5a6eefa6b34f6e9e9fb4ef560297aa0c482c11a4f91b4dab59345b9
MD5 e1884d383af7271a2040de417c3b9e11
BLAKE2b-256 559b71d3b2f8aa2e1228d01a1b20e038639fc041a4f16ab7ba5b046c5c739e73

See more details on using hashes here.

Provenance

The following attestation bundles were made for ghidra_manager-0.1.0-py3-none-any.whl:

Publisher: release.yml on alexbevi/ghidra-manager

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page