refactor: Rename project from ghydramcp to mcghidra
Some checks are pending
Build Ghidra Plugin / build (push) Waiting to run

- Rename src/ghydramcp → src/mcghidra
- Rename GhydraMCPPlugin.java → MCGhidraPlugin.java
- Update all imports, class names, and references
- Update pyproject.toml package name and script entry
- Update Docker image names and container prefixes
- Update environment variables: GHYDRA_* → MCGHIDRA_*
- Update all documentation references
This commit is contained in:
Ryan Malloy 2026-02-07 02:13:53 -07:00
parent d1750cb339
commit 1143489924
54 changed files with 436 additions and 436 deletions

View File

@ -2,11 +2,11 @@
## Summary ## Summary
The GhydraMCP Docker container fails to start the HTTP API server because `GhydraMCPServer.java` imports Gson, but Gson is not available in Ghidra's headless script classpath. The MCGhidra Docker container fails to start the HTTP API server because `MCGhidraServer.java` imports Gson, but Gson is not available in Ghidra's headless script classpath.
## Environment ## Environment
- GhydraMCP Docker image: `ghydramcp:latest` - MCGhidra Docker image: `mcghidra:latest`
- Ghidra Version: 11.4.2 - Ghidra Version: 11.4.2
- Build Date: 2025-08-26 - Build Date: 2025-08-26
@ -14,12 +14,12 @@ The GhydraMCP Docker container fails to start the HTTP API server because `Ghydr
1. Build the Docker image: 1. Build the Docker image:
```bash ```bash
docker build -t ghydramcp:latest -f docker/Dockerfile . docker build -t mcghidra:latest -f docker/Dockerfile .
``` ```
2. Run with a binary: 2. Run with a binary:
```bash ```bash
docker run -p 8192:8192 -v /path/to/binary:/binaries/test ghydramcp:latest /binaries/test docker run -p 8192:8192 -v /path/to/binary:/binaries/test mcghidra:latest /binaries/test
``` ```
3. Check logs: 3. Check logs:
@ -37,9 +37,9 @@ Analysis completes but the script fails to load:
``` ```
INFO REPORT: Analysis succeeded for file: file:///binaries/cardv (HeadlessAnalyzer) INFO REPORT: Analysis succeeded for file: file:///binaries/cardv (HeadlessAnalyzer)
ERROR REPORT SCRIPT ERROR: GhydraMCPServer.java : The class could not be found. ERROR REPORT SCRIPT ERROR: MCGhidraServer.java : The class could not be found.
It must be the public class of the .java file: Failed to get OSGi bundle containing script: It must be the public class of the .java file: Failed to get OSGi bundle containing script:
/opt/ghidra/scripts/GhydraMCPServer.java (HeadlessAnalyzer) /opt/ghidra/scripts/MCGhidraServer.java (HeadlessAnalyzer)
``` ```
The health check fails because the HTTP server never starts: The health check fails because the HTTP server never starts:
@ -50,7 +50,7 @@ The health check fails because the HTTP server never starts:
## Root Cause Analysis ## Root Cause Analysis
`GhydraMCPServer.java` (lines 22-24) imports Gson: `MCGhidraServer.java` (lines 22-24) imports Gson:
```java ```java
import com.google.gson.Gson; import com.google.gson.Gson;
@ -61,14 +61,14 @@ import com.google.gson.JsonParser;
However: However:
1. Gson is **not** bundled with Ghidra 1. Gson is **not** bundled with Ghidra
2. The GhydraMCP extension JAR includes Gson, but headless scripts run in a **separate OSGi classloader** without access to extension lib dependencies 2. The MCGhidra extension JAR includes Gson, but headless scripts run in a **separate OSGi classloader** without access to extension lib dependencies
3. The Dockerfile doesn't copy Gson to Ghidra's script classpath 3. The Dockerfile doesn't copy Gson to Ghidra's script classpath
## Verification ## Verification
```bash ```bash
# Check if Gson is in the built extension # Check if Gson is in the built extension
unzip -l target/GhydraMCP-*.zip | grep -i gson unzip -l target/MCGhidra-*.zip | grep -i gson
# Result: No matches # Result: No matches
# Check Ghidra's lib directories # Check Ghidra's lib directories
@ -90,13 +90,13 @@ RUN curl -fsSL "https://repo1.maven.org/maven2/com/google/gson/gson/2.10.1/gson-
### Option 2: Use Built-in JSON (No External Dependencies) ### Option 2: Use Built-in JSON (No External Dependencies)
Rewrite `GhydraMCPServer.java` to use only JDK classes: Rewrite `MCGhidraServer.java` to use only JDK classes:
- Replace Gson with `javax.json` or manual JSON string building - Replace Gson with `javax.json` or manual JSON string building
- This ensures the script works without any external dependencies - This ensures the script works without any external dependencies
### Option 3: Pre-compiled Script JAR ### Option 3: Pre-compiled Script JAR
Compile `GhydraMCPServer.java` with Gson into a JAR and place it in the extension, then reference it differently in headless mode. Compile `MCGhidraServer.java` with Gson into a JAR and place it in the extension, then reference it differently in headless mode.
## Impact ## Impact
@ -106,7 +106,7 @@ Compile `GhydraMCPServer.java` with Gson into a JAR and place it in the extensio
## Additional Context ## Additional Context
The main GhydraMCP plugin works fine in GUI mode because the extension's lib dependencies are loaded. This only affects the headless Docker workflow where scripts are loaded separately from the extension. The main MCGhidra plugin works fine in GUI mode because the extension's lib dependencies are loaded. This only affects the headless Docker workflow where scripts are loaded separately from the extension.
--- ---

View File

@ -75,14 +75,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- **Silent Exception Handling:** Added debug logging to PortPool exception handlers and analysis fallback paths. - **Silent Exception Handling:** Added debug logging to PortPool exception handlers and analysis fallback paths.
- **File Descriptor Leak:** Fixed potential leak in `PortPool._try_acquire_port()` if write operations fail after lock acquisition. - **File Descriptor Leak:** Fixed potential leak in `PortPool._try_acquire_port()` if write operations fail after lock acquisition.
- **Hash Algorithm Consistency:** Changed query hash from MD5 to SHA-256 in pagination module for consistency with cursor ID generation. - **Hash Algorithm Consistency:** Changed query hash from MD5 to SHA-256 in pagination module for consistency with cursor ID generation.
- **Lazy PortPool Initialization:** `PortPool` now created on first use, avoiding `/tmp/ghydramcp-ports` directory creation when Docker tools are never used. - **Lazy PortPool Initialization:** `PortPool` now created on first use, avoiding `/tmp/mcghidra-ports` directory creation when Docker tools are never used.
- **Logging Configuration:** `configure_logging()` now called during server startup — debug messages actually work now. - **Logging Configuration:** `configure_logging()` now called during server startup — debug messages actually work now.
- **Type Hint Consistency:** Aligned `filtering.py` to use `List[T]` from typing module like rest of codebase. - **Type Hint Consistency:** Aligned `filtering.py` to use `List[T]` from typing module like rest of codebase.
- **Parameter Naming:** Renamed `project_fields` to `fields` in `structs_get()` for consistency with other tools. - **Parameter Naming:** Renamed `project_fields` to `fields` in `structs_get()` for consistency with other tools.
- **Import Path:** Fixed `logging.py` to import `Context` from `fastmcp` (not deprecated `mcp.server.fastmcp` path). - **Import Path:** Fixed `logging.py` to import `Context` from `fastmcp` (not deprecated `mcp.server.fastmcp` path).
### Added ### Added
- **Debug Logging Environment Variable:** Set `GHYDRAMCP_DEBUG=1` to enable DEBUG-level logging for troubleshooting. - **Debug Logging Environment Variable:** Set `MCGHIDRA_DEBUG=1` to enable DEBUG-level logging for troubleshooting.
## [2025.12.1] - 2025-12-01 ## [2025.12.1] - 2025-12-01
@ -239,7 +239,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [1.1] - 2025-03-30 ## [1.1] - 2025-03-30
### Added ### Added
- Initial release of GhydraMCP bridge - Initial release of MCGhidra bridge
- Basic Ghidra instance management tools - Basic Ghidra instance management tools
- Function analysis tools - Function analysis tools
- Variable manipulation tools - Variable manipulation tools
@ -250,11 +250,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- Initial project setup - Initial project setup
- Basic MCP bridge functionality - Basic MCP bridge functionality
[unreleased]: https://github.com/teal-bauer/GhydraMCP/compare/v2025.12.1...HEAD [unreleased]: https://github.com/teal-bauer/MCGhidra/compare/v2025.12.1...HEAD
[2025.12.1]: https://github.com/teal-bauer/GhydraMCP/compare/v2.0.0...v2025.12.1 [2025.12.1]: https://github.com/teal-bauer/MCGhidra/compare/v2.0.0...v2025.12.1
[2.0.0]: https://github.com/teal-bauer/GhydraMCP/compare/v1.4.0...v2.0.0 [2.0.0]: https://github.com/teal-bauer/MCGhidra/compare/v1.4.0...v2.0.0
[1.4.0]: https://github.com/teal-bauer/GhydraMCP/compare/v1.3.0...v1.4.0 [1.4.0]: https://github.com/teal-bauer/MCGhidra/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/teal-bauer/GhydraMCP/compare/v1.2...v1.3.0 [1.3.0]: https://github.com/teal-bauer/MCGhidra/compare/v1.2...v1.3.0
[1.2]: https://github.com/teal-bauer/GhydraMCP/compare/v1.1...v1.2 [1.2]: https://github.com/teal-bauer/MCGhidra/compare/v1.1...v1.2
[1.1]: https://github.com/teal-bauer/GhydraMCP/compare/1.0...v1.1 [1.1]: https://github.com/teal-bauer/MCGhidra/compare/1.0...v1.1
[1.0]: https://github.com/teal-bauer/GhydraMCP/releases/tag/1.0 [1.0]: https://github.com/teal-bauer/MCGhidra/releases/tag/1.0

View File

@ -1,6 +1,6 @@
# Contributing to GhydraMCP # Contributing to MCGhidra
Thank you for your interest in contributing to GhydraMCP! This document provides guidelines and information for contributors. Thank you for your interest in contributing to MCGhidra! This document provides guidelines and information for contributors.
## Table of Contents ## Table of Contents
@ -13,10 +13,10 @@ Thank you for your interest in contributing to GhydraMCP! This document provides
## Project Structure ## Project Structure
GhydraMCP consists of two main components: MCGhidra consists of two main components:
1. **Java Plugin for Ghidra** (`src/main/java/eu/starsong/ghidra/`): 1. **Java Plugin for Ghidra** (`src/main/java/eu/starsong/ghidra/`):
- Main class: `GhydraMCPPlugin.java` - Main class: `MCGhidraPlugin.java`
- API constants: `api/ApiConstants.java` - API constants: `api/ApiConstants.java`
- Endpoints: `endpoints/` directory - Endpoints: `endpoints/` directory
- Data models: `model/` directory - Data models: `model/` directory
@ -39,23 +39,23 @@ GhydraMCP consists of two main components:
```bash ```bash
# Clone the repository # Clone the repository
git clone https://github.com/starsong-consulting/GhydraMCP.git git clone https://github.com/starsong-consulting/MCGhidra.git
cd GhydraMCP cd MCGhidra
# Build the project # Build the project
mvn clean package mvn clean package
``` ```
This creates: This creates:
- `target/GhydraMCP-[version].zip` - The Ghidra plugin only - `target/MCGhidra-[version].zip` - The Ghidra plugin only
- `target/GhydraMCP-Complete-[version].zip` - Complete package with plugin and bridge script - `target/MCGhidra-Complete-[version].zip` - Complete package with plugin and bridge script
### Installing for Development ### Installing for Development
1. Build the project as described above 1. Build the project as described above
2. In Ghidra, go to `File` -> `Install Extensions` 2. In Ghidra, go to `File` -> `Install Extensions`
3. Click the `+` button 3. Click the `+` button
4. Select the `GhydraMCP-[version].zip` file 4. Select the `MCGhidra-[version].zip` file
5. Restart Ghidra 5. Restart Ghidra
6. Enable the plugin in `File` -> `Configure` -> `Developer` 6. Enable the plugin in `File` -> `Configure` -> `Developer`
@ -75,7 +75,7 @@ uv pip install mcp==1.6.0 requests==2.32.3
## Versioning ## Versioning
GhydraMCP follows semantic versioning (SemVer) and uses explicit API versions: MCGhidra follows semantic versioning (SemVer) and uses explicit API versions:
### Version Numbers ### Version Numbers
@ -244,4 +244,4 @@ If you have questions or need help, please:
2. Check existing documentation 2. Check existing documentation
3. Reach out to the maintainers directly 3. Reach out to the maintainers directly
Thank you for contributing to GhydraMCP! Thank you for contributing to MCGhidra!

View File

@ -1,4 +1,4 @@
# GhydraMCP Ghidra Plugin HTTP API v2 # MCGhidra Ghidra Plugin HTTP API v2
## Overview ## Overview
@ -159,7 +159,7 @@ Returns information about the current plugin instance, including details about t
``` ```
### `GET /instances` ### `GET /instances`
Returns information about all active GhydraMCP plugin instances. Returns information about all active MCGhidra plugin instances.
```json ```json
{ {
"id": "req-instances", "id": "req-instances",

View File

@ -1,4 +1,4 @@
# GhydraMCP Makefile # MCGhidra Makefile
# Convenient commands for Docker and development operations # Convenient commands for Docker and development operations
.PHONY: help build build-dev up up-dev down down-dev logs logs-dev \ .PHONY: help build build-dev up up-dev down down-dev logs logs-dev \
@ -6,7 +6,7 @@
# Default target # Default target
help: help:
@echo "GhydraMCP Docker Management" @echo "MCGhidra Docker Management"
@echo "============================" @echo "============================"
@echo "" @echo ""
@echo "Build commands:" @echo "Build commands:"
@ -44,10 +44,10 @@ help:
# ============================================================================= # =============================================================================
build: build:
docker compose build ghydramcp docker compose build mcghidra
build-dev: build-dev:
docker compose build ghydramcp-dev docker compose build mcghidra-dev
build-all: build build-dev build-all: build build-dev
@ -56,14 +56,14 @@ build-all: build build-dev
# ============================================================================= # =============================================================================
up: up:
docker compose --profile prod up -d ghydramcp docker compose --profile prod up -d mcghidra
@echo "GhydraMCP starting... checking health in 30 seconds" @echo "MCGhidra starting... checking health in 30 seconds"
@sleep 30 @sleep 30
@$(MAKE) health || echo "Server may still be starting up..." @$(MAKE) health || echo "Server may still be starting up..."
up-dev: up-dev:
docker compose --profile dev up -d ghydramcp-dev docker compose --profile dev up -d mcghidra-dev
@echo "GhydraMCP (dev) starting..." @echo "MCGhidra (dev) starting..."
down: down:
docker compose --profile prod down docker compose --profile prod down
@ -90,7 +90,7 @@ ifndef FILE
@exit 1 @exit 1
endif endif
@echo "Analyzing: $(FILE)" @echo "Analyzing: $(FILE)"
docker compose run --rm -v "$(dir $(FILE)):/binaries:ro" ghydramcp /binaries/$(notdir $(FILE)) docker compose run --rm -v "$(dir $(FILE)):/binaries:ro" mcghidra /binaries/$(notdir $(FILE))
# Analyze in background (detached) # Analyze in background (detached)
analyze-bg: analyze-bg:
@ -99,20 +99,20 @@ ifndef FILE
@exit 1 @exit 1
endif endif
@echo "Starting background analysis of: $(FILE)" @echo "Starting background analysis of: $(FILE)"
docker compose run -d -v "$(dir $(FILE)):/binaries:ro" ghydramcp /binaries/$(notdir $(FILE)) docker compose run -d -v "$(dir $(FILE)):/binaries:ro" mcghidra /binaries/$(notdir $(FILE))
# ============================================================================= # =============================================================================
# Utility Commands # Utility Commands
# ============================================================================= # =============================================================================
shell: shell:
docker compose --profile debug run --rm ghydramcp-shell docker compose --profile debug run --rm mcghidra-shell
logs: logs:
docker compose logs -f ghydramcp docker compose logs -f mcghidra
logs-dev: logs-dev:
docker compose logs -f ghydramcp-dev docker compose logs -f mcghidra-dev
status: status:
@echo "=== Container Status ===" @echo "=== Container Status ==="
@ -122,8 +122,8 @@ status:
@docker stats --no-stream $$(docker compose ps -q 2>/dev/null) 2>/dev/null || echo "No containers running" @docker stats --no-stream $$(docker compose ps -q 2>/dev/null) 2>/dev/null || echo "No containers running"
health: health:
@echo "Checking GhydraMCP API health..." @echo "Checking MCGhidra API health..."
@curl -sf http://localhost:$${GHYDRA_PORT:-8192}/ | python3 -m json.tool 2>/dev/null \ @curl -sf http://localhost:$${MCGHIDRA_PORT:-8192}/ | python3 -m json.tool 2>/dev/null \
|| echo "API not responding (server may be starting or binary being analyzed)" || echo "API not responding (server may be starting or binary being analyzed)"
# ============================================================================= # =============================================================================
@ -135,7 +135,7 @@ clean:
@echo "Containers and volumes removed" @echo "Containers and volumes removed"
clean-all: clean clean-all: clean
docker rmi ghydramcp:latest ghydramcp:dev 2>/dev/null || true docker rmi mcghidra:latest mcghidra:dev 2>/dev/null || true
@echo "Images removed" @echo "Images removed"
prune: prune:
@ -147,10 +147,10 @@ prune:
# ============================================================================= # =============================================================================
mcp: mcp:
uv run python -m ghydramcp uv run python -m mcghidra
mcp-dev: mcp-dev:
uv run python -m ghydramcp --verbose uv run python -m mcghidra --verbose
# ============================================================================= # =============================================================================
# Development Commands # Development Commands

View File

@ -1,8 +1,8 @@
# GhydraMCP Quick Start Guide # MCGhidra Quick Start Guide
## What is GhydraMCP? ## What is MCGhidra?
GhydraMCP is a complete reverse engineering platform that combines: MCGhidra is a complete reverse engineering platform that combines:
- **Ghidra** - NSA's powerful binary analysis tool - **Ghidra** - NSA's powerful binary analysis tool
- **Docker** - Containerized, reproducible analysis environment - **Docker** - Containerized, reproducible analysis environment
- **HTTP REST API** - HATEOAS-compliant REST interface - **HTTP REST API** - HATEOAS-compliant REST interface
@ -14,16 +14,16 @@ GhydraMCP is a complete reverse engineering platform that combines:
### 1. Analyze a Standard Binary (ELF/PE/Mach-O) ### 1. Analyze a Standard Binary (ELF/PE/Mach-O)
```bash ```bash
cd /home/rpm/claude/ghydramcp/GhydraMCP cd /home/rpm/claude/mcghidra/MCGhidra
# Build the Docker image (one time) # Build the Docker image (one time)
docker build -t ghydramcp:latest -f docker/Dockerfile . docker build -t mcghidra:latest -f docker/Dockerfile .
# Analyze any standard binary # Analyze any standard binary
docker run -d --name my-analysis \ docker run -d --name my-analysis \
-p 8192:8192 \ -p 8192:8192 \
-v $(pwd)/binaries:/binaries \ -v $(pwd)/binaries:/binaries \
ghydramcp:latest \ mcghidra:latest \
/binaries/your-binary /binaries/your-binary
# Wait ~20 seconds for analysis, then access HTTP API # Wait ~20 seconds for analysis, then access HTTP API
@ -45,7 +45,7 @@ python3 docker/arm_firmware_prep.py \
docker run -d --name arm-firmware \ docker run -d --name arm-firmware \
-p 8192:8192 \ -p 8192:8192 \
-v $(pwd)/binaries:/binaries \ -v $(pwd)/binaries:/binaries \
ghydramcp:latest \ mcghidra:latest \
/binaries/your-firmware.elf /binaries/your-firmware.elf
``` ```
@ -53,11 +53,11 @@ docker run -d --name arm-firmware \
```bash ```bash
# The MCP server is located at: # The MCP server is located at:
cd /home/rpm/claude/ghydramcp/GhydraMCP cd /home/rpm/claude/mcghidra/MCGhidra
./launch.sh ./launch.sh
# Or with uv: # Or with uv:
cd GhydraMCP && uv run ghydramcp cd MCGhidra && uv run mcghidra
``` ```
## HTTP API Overview ## HTTP API Overview
@ -176,7 +176,7 @@ curl "http://localhost:8192/functions/$ENTRY/decompile" | jq -r '.result'
### List Running Containers ### List Running Containers
```bash ```bash
docker ps | grep ghydramcp docker ps | grep mcghidra
``` ```
### View Logs ### View Logs
@ -201,7 +201,7 @@ docker run -d --name persistent \
-v $(pwd)/projects:/projects \ -v $(pwd)/projects:/projects \
-v $(pwd)/binaries:/binaries \ -v $(pwd)/binaries:/binaries \
-e PROJECT_NAME=MyProject \ -e PROJECT_NAME=MyProject \
ghydramcp:latest \ mcghidra:latest \
/binaries/my-binary /binaries/my-binary
# Projects are saved in ./projects/MyProject/ # Projects are saved in ./projects/MyProject/
@ -238,7 +238,7 @@ docker exec my-analysis sh -c 'chmod 644 /opt/ghidra/scripts/*.java'
docker run -d --name analysis2 \ docker run -d --name analysis2 \
-p 8193:8192 \ -p 8193:8192 \
-v $(pwd)/binaries:/binaries \ -v $(pwd)/binaries:/binaries \
ghydramcp:latest \ mcghidra:latest \
/binaries/binary /binaries/binary
# Access at http://localhost:8193/ # Access at http://localhost:8193/
@ -263,7 +263,7 @@ gcc -o binaries/test test.c
docker run -d --name test-analysis \ docker run -d --name test-analysis \
-p 8192:8192 \ -p 8192:8192 \
-v $(pwd)/binaries:/binaries \ -v $(pwd)/binaries:/binaries \
ghydramcp:latest \ mcghidra:latest \
/binaries/test /binaries/test
# Find hidden function # Find hidden function
@ -284,7 +284,7 @@ python3 docker/arm_firmware_prep.py \
docker run -d --name cisco \ docker run -d --name cisco \
-p 8192:8192 \ -p 8192:8192 \
-v $(pwd)/binaries:/binaries \ -v $(pwd)/binaries:/binaries \
ghydramcp:latest \ mcghidra:latest \
/binaries/cisco.elf /binaries/cisco.elf
# Explore # Explore
@ -303,15 +303,15 @@ curl http://localhost:8192/data/strings | jq '.strings[] | select(.value | test(
## Project Structure ## Project Structure
``` ```
GhydraMCP/ MCGhidra/
├── docker/ ├── docker/
│ ├── Dockerfile # Main Docker image │ ├── Dockerfile # Main Docker image
│ ├── entrypoint.sh # Container entry point │ ├── entrypoint.sh # Container entry point
│ ├── GhydraMCPServer.java # HTTP API server (1724 lines) │ ├── MCGhidraServer.java # HTTP API server (1724 lines)
│ ├── ImportRawARM.java # Raw binary import script │ ├── ImportRawARM.java # Raw binary import script
│ ├── arm_firmware_prep.py # ELF wrapper tool ⭐ │ ├── arm_firmware_prep.py # ELF wrapper tool ⭐
│ └── README*.md # Documentation │ └── README*.md # Documentation
├── src/ghydramcp/ # MCP server implementation ├── src/mcghidra/ # MCP server implementation
│ ├── __init__.py │ ├── __init__.py
│ ├── server.py # FastMCP server │ ├── server.py # FastMCP server
│ └── mixins/ # Modular functionality │ └── mixins/ # Modular functionality

View File

@ -1,4 +1,4 @@
# GhydraMCP # MCGhidra
**AI-native reverse engineering.** Give Claude (or any MCP client) direct access to Ghidra's analysis engine. **AI-native reverse engineering.** Give Claude (or any MCP client) direct access to Ghidra's analysis engine.
@ -60,10 +60,10 @@ No Ghidra installation needed. Analyze binaries in isolated containers.
```bash ```bash
# Build the image (once) # Build the image (once)
cd GhydraMCP && docker build -t ghydramcp:latest -f docker/Dockerfile . cd MCGhidra && docker build -t mcghidra:latest -f docker/Dockerfile .
# Add to your MCP config # Add to your MCP config
claude mcp add ghydramcp -- uv run --directory /path/to/GhydraMCP ghydramcp claude mcp add mcghidra -- uv run --directory /path/to/MCGhidra mcghidra
``` ```
Then in Claude: Then in Claude:
@ -76,14 +76,14 @@ Claude will auto-start a container, wait for analysis, and begin work.
### Option 2: Native Ghidra ### Option 2: Native Ghidra
1. **Install the Ghidra plugin:** 1. **Install the Ghidra plugin:**
- Download latest [release](https://github.com/starsong-consulting/GhydraMCP/releases) - Download latest [release](https://github.com/starsong-consulting/MCGhidra/releases)
- In Ghidra: `File → Install Extensions → +` → select the `.zip` - In Ghidra: `File → Install Extensions → +` → select the `.zip`
- Restart Ghidra - Restart Ghidra
- Enable in `File → Configure → Developer → GhydraMCPPlugin` - Enable in `File → Configure → Developer → MCGhidraPlugin`
2. **Add MCP server:** 2. **Add MCP server:**
```bash ```bash
claude mcp add ghydramcp -- uv run --directory /path/to/GhydraMCP ghydramcp claude mcp add mcghidra -- uv run --directory /path/to/MCGhidra mcghidra
``` ```
3. **Open a binary in Ghidra**, then ask Claude to analyze it. 3. **Open a binary in Ghidra**, then ask Claude to analyze it.
@ -94,14 +94,14 @@ Claude will auto-start a container, wait for analysis, and begin work.
``` ```
┌──────────────┐ MCP ┌──────────────┐ HTTP ┌──────────────┐ ┌──────────────┐ MCP ┌──────────────┐ HTTP ┌──────────────┐
│ Claude │◄────────────►│ GhydraMCP │◄────────────►│ Ghidra │ │ Claude │◄────────────►│ MCGhidra │◄────────────►│ Ghidra │
│ (or other │ stdio │ (Python) │ REST API │ Plugin │ │ (or other │ stdio │ (Python) │ REST API │ Plugin │
│ MCP client) │ │ │ │ (Java) │ │ MCP client) │ │ │ │ (Java) │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
``` ```
- **Ghidra Plugin**: Exposes Ghidra's analysis via HTTP REST API (HATEOAS) - **Ghidra Plugin**: Exposes Ghidra's analysis via HTTP REST API (HATEOAS)
- **GhydraMCP Server**: Translates MCP tool calls to API requests - **MCGhidra Server**: Translates MCP tool calls to API requests
- **Multi-instance**: Analyze multiple binaries simultaneously on different ports - **Multi-instance**: Analyze multiple binaries simultaneously on different ports
- **Session isolation**: Docker containers get unique ports, preventing conflicts - **Session isolation**: Docker containers get unique ports, preventing conflicts
@ -181,9 +181,9 @@ These guide Claude through systematic analysis with progress reporting.
```json ```json
{ {
"mcpServers": { "mcpServers": {
"ghydramcp": { "mcghidra": {
"command": "uv", "command": "uv",
"args": ["run", "--directory", "/path/to/GhydraMCP", "ghydramcp"] "args": ["run", "--directory", "/path/to/MCGhidra", "mcghidra"]
} }
} }
} }
@ -191,7 +191,7 @@ These guide Claude through systematic analysis with progress reporting.
**Claude Code**: **Claude Code**:
```bash ```bash
claude mcp add ghydramcp -- uv run --directory /path/to/GhydraMCP ghydramcp claude mcp add mcghidra -- uv run --directory /path/to/MCGhidra mcghidra
``` ```
--- ---
@ -267,25 +267,25 @@ See `--help` or the [API docs](GHIDRA_HTTP_API.md) for full parameter details.
```bash ```bash
# Clone # Clone
git clone https://github.com/starsong-consulting/GhydraMCP git clone https://github.com/starsong-consulting/MCGhidra
cd GhydraMCP cd MCGhidra
# Build Ghidra plugin # Build Ghidra plugin
mvn clean package mvn clean package
# → target/GhydraMCP-[version].zip # → target/MCGhidra-[version].zip
# Build Docker image # Build Docker image
docker build -t ghydramcp:latest -f docker/Dockerfile . docker build -t mcghidra:latest -f docker/Dockerfile .
# Run MCP server (for development) # Run MCP server (for development)
uv run ghydramcp uv run mcghidra
``` ```
--- ---
## Architecture ## Architecture
GhydraMCP is designed for AI agents: MCGhidra is designed for AI agents:
- **Lazy registration**: `instances_use` doesn't block — validates on first real call - **Lazy registration**: `instances_use` doesn't block — validates on first real call
- **Non-blocking I/O**: All Docker/HTTP operations run in thread executors - **Non-blocking I/O**: All Docker/HTTP operations run in thread executors

View File

@ -1,11 +1,11 @@
# Testing GhydraMCP # Testing MCGhidra
This document describes how to test the GhydraMCP plugin and bridge. This document describes how to test the MCGhidra plugin and bridge.
## Prerequisites ## Prerequisites
- Python 3.11 or higher - Python 3.11 or higher
- Ghidra with the GhydraMCP plugin installed and running - Ghidra with the MCGhidra plugin installed and running
- The `requests` Python package (`pip install requests`) - The `requests` Python package (`pip install requests`)
## Running All Tests ## Running All Tests
@ -34,7 +34,7 @@ The `test_http_api.py` script tests the HTTP API exposed by the Java plugin. It
### Running the HTTP API Tests ### Running the HTTP API Tests
1. Make sure Ghidra is running with the GhydraMCP plugin loaded 1. Make sure Ghidra is running with the MCGhidra plugin loaded
2. Run the tests: 2. Run the tests:
```bash ```bash
@ -57,7 +57,7 @@ The `test_mcp_client.py` script tests the MCP bridge functionality using the MCP
### Running the MCP Bridge Tests ### Running the MCP Bridge Tests
1. Make sure Ghidra is running with the GhydraMCP plugin loaded 1. Make sure Ghidra is running with the MCGhidra plugin loaded
2. Run the tests: 2. Run the tests:
```bash ```bash
@ -89,7 +89,7 @@ The test script will:
### HTTP API Tests ### HTTP API Tests
- If tests are skipped with "Ghidra server not running or not accessible", make sure Ghidra is running and the GhydraMCP plugin is loaded. - If tests are skipped with "Ghidra server not running or not accessible", make sure Ghidra is running and the MCGhidra plugin is loaded.
- If tests fail with connection errors, check that the plugin is listening on the expected port (default: 8192). - If tests fail with connection errors, check that the plugin is listening on the expected port (default: 8192).
### MCP Bridge Tests ### MCP Bridge Tests
@ -103,7 +103,7 @@ The test script will:
To add a new test for an HTTP endpoint: To add a new test for an HTTP endpoint:
1. Add a new test method to the `GhydraMCPHttpApiTests` class 1. Add a new test method to the `MCGhidraHttpApiTests` class
2. Use the `requests` library to make HTTP requests to the endpoint 2. Use the `requests` library to make HTTP requests to the endpoint
3. Verify the response using assertions 3. Verify the response using assertions

View File

@ -5,7 +5,7 @@
# "requests>=2.32.3", # "requests>=2.32.3",
# ] # ]
# /// # ///
# GhydraMCP Bridge for Ghidra HATEOAS API - Optimized for MCP integration # MCGhidra Bridge for Ghidra HATEOAS API - Optimized for MCP integration
# Provides namespaced tools for interacting with Ghidra's reverse engineering capabilities # Provides namespaced tools for interacting with Ghidra's reverse engineering capabilities
# Features: Cursor-based pagination, grep filtering, session isolation # Features: Cursor-based pagination, grep filtering, session isolation
import os import os
@ -699,7 +699,7 @@ def paginate_response(data: List[Any], query_params: dict,
# ================= End Cursor System ================= # ================= End Cursor System =================
instructions = """ instructions = """
GhydraMCP allows interacting with multiple Ghidra SRE instances. Ghidra SRE is a tool for reverse engineering and analyzing binaries, e.g. malware. MCGhidra allows interacting with multiple Ghidra SRE instances. Ghidra SRE is a tool for reverse engineering and analyzing binaries, e.g. malware.
First, run `instances_list()` to see all available Ghidra instances (automatically discovers instances on the default host). First, run `instances_list()` to see all available Ghidra instances (automatically discovers instances on the default host).
Then use `instances_use(port)` to set your working instance. Then use `instances_use(port)` to set your working instance.
@ -742,7 +742,7 @@ Use `cursor_list()` to see active cursors.
Use `cursor_delete(cursor_id)` to clean up cursors. Use `cursor_delete(cursor_id)` to clean up cursors.
""" """
mcp = FastMCP("GhydraMCP", instructions=instructions) mcp = FastMCP("MCGhidra", instructions=instructions)
ghidra_host = os.environ.get("GHIDRA_HYDRA_HOST", DEFAULT_GHIDRA_HOST) ghidra_host = os.environ.get("GHIDRA_HYDRA_HOST", DEFAULT_GHIDRA_HOST)
@ -1162,7 +1162,7 @@ def _discover_instances(port_range, host=None, timeout=0.5) -> dict:
timeout=timeout) timeout=timeout)
if response.ok: if response.ok:
# Further validate it's a GhydraMCP instance by checking response format # Further validate it's a MCGhidra instance by checking response format
try: try:
json_data = response.json() json_data = response.json()
if "success" in json_data and json_data["success"] and "result" in json_data: if "success" in json_data and json_data["success"] and "result" in json_data:
@ -2200,7 +2200,7 @@ def reverse_engineer_binary_prompt(port: int = None):
- Security checks - Security checks
- Data transformation - Data transformation
Remember to use the available GhydraMCP tools: Remember to use the available MCGhidra tools:
- Use functions_list to find functions matching patterns - Use functions_list to find functions matching patterns
- Use xrefs_list to find cross-references - Use xrefs_list to find cross-references
- Use functions_decompile for C-like representations - Use functions_decompile for C-like representations
@ -6862,7 +6862,7 @@ def main():
discovery_thread = threading.Thread( discovery_thread = threading.Thread(
target=periodic_discovery, target=periodic_discovery,
daemon=True, daemon=True,
name="GhydraMCP-Discovery" name="MCGhidra-Discovery"
) )
discovery_thread.start() discovery_thread.start()

View File

@ -1,9 +1,9 @@
# GhydraMCP Docker Compose Configuration # MCGhidra Docker Compose Configuration
# Provides both development and production modes for Ghidra + GhydraMCP # Provides both development and production modes for Ghidra + MCGhidra
# #
# Usage: # Usage:
# Development: docker compose up ghydramcp-dev # Development: docker compose up mcghidra-dev
# Production: docker compose up ghydramcp # Production: docker compose up mcghidra
# #
# Set MODE in .env file to switch between dev/prod behaviors # Set MODE in .env file to switch between dev/prod behaviors
@ -11,28 +11,28 @@ services:
# ============================================================================= # =============================================================================
# Production Service - Optimized for stability and security # Production Service - Optimized for stability and security
# ============================================================================= # =============================================================================
ghydramcp: mcghidra:
build: build:
context: . context: .
dockerfile: docker/Dockerfile dockerfile: docker/Dockerfile
args: args:
GHIDRA_VERSION: ${GHIDRA_VERSION:-11.4.2} GHIDRA_VERSION: ${GHIDRA_VERSION:-11.4.2}
GHIDRA_DATE: ${GHIDRA_DATE:-20250826} GHIDRA_DATE: ${GHIDRA_DATE:-20250826}
image: ghydramcp:${GHYDRAMCP_VERSION:-latest} image: mcghidra:${MCGHIDRAMCP_VERSION:-latest}
container_name: ${COMPOSE_PROJECT_NAME:-ghydramcp}-server container_name: ${COMPOSE_PROJECT_NAME:-mcghidra}-server
restart: unless-stopped restart: unless-stopped
ports: ports:
- "${GHYDRA_PORT:-8192}:8192" - "${MCGHIDRA_PORT:-8192}:8192"
volumes: volumes:
# Mount binaries to analyze (read-only in prod) # Mount binaries to analyze (read-only in prod)
- ${BINARIES_PATH:-./binaries}:/binaries:ro - ${BINARIES_PATH:-./binaries}:/binaries:ro
# Persist Ghidra projects between runs # Persist Ghidra projects between runs
- ghydra-projects:/projects - mcghidra-projects:/projects
environment: environment:
- GHYDRA_MODE=${GHYDRA_MODE:-headless} - MCGHIDRA_MODE=${MCGHIDRA_MODE:-headless}
- GHYDRA_PORT=8192 - MCGHIDRA_PORT=8192
- GHYDRA_MAXMEM=${GHYDRA_MAXMEM:-2G} - MCGHIDRA_MAXMEM=${MCGHIDRA_MAXMEM:-2G}
- PROJECT_NAME=${PROJECT_NAME:-GhydraMCP} - PROJECT_NAME=${PROJECT_NAME:-MCGhidra}
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8192/"] test: ["CMD", "curl", "-f", "http://localhost:8192/"]
interval: 30s interval: 30s
@ -42,7 +42,7 @@ services:
deploy: deploy:
resources: resources:
limits: limits:
memory: ${GHYDRA_MAXMEM:-2G} memory: ${MCGHIDRA_MAXMEM:-2G}
profiles: profiles:
- prod - prod
- default - default
@ -50,17 +50,17 @@ services:
# ============================================================================= # =============================================================================
# Development Service - Hot-reload and debugging friendly # Development Service - Hot-reload and debugging friendly
# ============================================================================= # =============================================================================
ghydramcp-dev: mcghidra-dev:
build: build:
context: . context: .
dockerfile: docker/Dockerfile dockerfile: docker/Dockerfile
args: args:
GHIDRA_VERSION: ${GHIDRA_VERSION:-11.4.2} GHIDRA_VERSION: ${GHIDRA_VERSION:-11.4.2}
GHIDRA_DATE: ${GHIDRA_DATE:-20250826} GHIDRA_DATE: ${GHIDRA_DATE:-20250826}
image: ghydramcp:dev image: mcghidra:dev
container_name: ${COMPOSE_PROJECT_NAME:-ghydramcp}-dev container_name: ${COMPOSE_PROJECT_NAME:-mcghidra}-dev
ports: ports:
- "${GHYDRA_PORT:-8192}:8192" - "${MCGHIDRA_PORT:-8192}:8192"
# Additional ports for debugging/multiple instances # Additional ports for debugging/multiple instances
- "8193:8193" - "8193:8193"
- "8194:8194" - "8194:8194"
@ -68,15 +68,15 @@ services:
# Mount binaries (read-write in dev) # Mount binaries (read-write in dev)
- ${BINARIES_PATH:-./binaries}:/binaries:rw - ${BINARIES_PATH:-./binaries}:/binaries:rw
# Persist projects # Persist projects
- ghydra-projects-dev:/projects - mcghidra-projects-dev:/projects
# Mount scripts for live editing (development only) # Mount scripts for live editing (development only)
- ./docker/GhydraMCPServer.java:/opt/ghidra/scripts/GhydraMCPServer.java:ro - ./docker/MCGhidraServer.java:/opt/ghidra/scripts/MCGhidraServer.java:ro
- ./docker/entrypoint.sh:/entrypoint.sh:ro - ./docker/entrypoint.sh:/entrypoint.sh:ro
environment: environment:
- GHYDRA_MODE=${GHYDRA_MODE:-headless} - MCGHIDRA_MODE=${MCGHIDRA_MODE:-headless}
- GHYDRA_PORT=8192 - MCGHIDRA_PORT=8192
- GHYDRA_MAXMEM=${GHYDRA_MAXMEM:-4G} - MCGHIDRA_MAXMEM=${MCGHIDRA_MAXMEM:-4G}
- PROJECT_NAME=${PROJECT_NAME:-GhydraMCP-Dev} - PROJECT_NAME=${PROJECT_NAME:-MCGhidra-Dev}
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8192/"] test: ["CMD", "curl", "-f", "http://localhost:8192/"]
interval: 15s interval: 15s
@ -89,28 +89,28 @@ services:
# ============================================================================= # =============================================================================
# Shell Service - Interactive debugging container # Shell Service - Interactive debugging container
# ============================================================================= # =============================================================================
ghydramcp-shell: mcghidra-shell:
build: build:
context: . context: .
dockerfile: docker/Dockerfile dockerfile: docker/Dockerfile
image: ghydramcp:${GHYDRAMCP_VERSION:-latest} image: mcghidra:${MCGHIDRAMCP_VERSION:-latest}
container_name: ${COMPOSE_PROJECT_NAME:-ghydramcp}-shell container_name: ${COMPOSE_PROJECT_NAME:-mcghidra}-shell
stdin_open: true stdin_open: true
tty: true tty: true
volumes: volumes:
- ${BINARIES_PATH:-./binaries}:/binaries:rw - ${BINARIES_PATH:-./binaries}:/binaries:rw
- ghydra-projects-dev:/projects - mcghidra-projects-dev:/projects
environment: environment:
- GHYDRA_MODE=shell - MCGHIDRA_MODE=shell
profiles: profiles:
- debug - debug
volumes: volumes:
ghydra-projects: mcghidra-projects:
name: ${COMPOSE_PROJECT_NAME:-ghydramcp}-projects name: ${COMPOSE_PROJECT_NAME:-mcghidra}-projects
ghydra-projects-dev: mcghidra-projects-dev:
name: ${COMPOSE_PROJECT_NAME:-ghydramcp}-projects-dev name: ${COMPOSE_PROJECT_NAME:-mcghidra}-projects-dev
networks: networks:
default: default:
name: ${COMPOSE_PROJECT_NAME:-ghydramcp}-network name: ${COMPOSE_PROJECT_NAME:-mcghidra}-network

View File

@ -1,14 +1,14 @@
# GhydraMCP Docker Image # MCGhidra Docker Image
# Ghidra + GhydraMCP Plugin pre-installed for headless binary analysis # Ghidra + MCGhidra Plugin pre-installed for headless binary analysis
# #
# Build: docker build -t ghydramcp:latest -f docker/Dockerfile . # Build: docker build -t mcghidra:latest -f docker/Dockerfile .
# Run: docker run -p 8192:8192 -v /path/to/binaries:/binaries ghydramcp:latest # Run: docker run -p 8192:8192 -v /path/to/binaries:/binaries mcghidra:latest
ARG GHIDRA_VERSION=11.4.2 ARG GHIDRA_VERSION=11.4.2
ARG GHIDRA_DATE=20250826 ARG GHIDRA_DATE=20250826
# ============================================================================= # =============================================================================
# Stage 1: Build the GhydraMCP plugin # Stage 1: Build the MCGhidra plugin
# ============================================================================= # =============================================================================
FROM eclipse-temurin:21-jdk-jammy AS builder FROM eclipse-temurin:21-jdk-jammy AS builder
@ -33,7 +33,7 @@ RUN curl -fsSL "https://github.com/NationalSecurityAgency/ghidra/releases/downlo
ENV GHIDRA_HOME=/opt/ghidra ENV GHIDRA_HOME=/opt/ghidra
# Copy GhydraMCP source and build # Copy MCGhidra source and build
WORKDIR /build WORKDIR /build
# Copy pom.xml first and download dependencies (cached until pom.xml changes) # Copy pom.xml first and download dependencies (cached until pom.xml changes)
@ -63,7 +63,7 @@ RUN mvn package -P plugin-only -DskipTests \
-Dghidra.base.jar=${GHIDRA_HOME}/Ghidra/Features/Base/lib/Base.jar -Dghidra.base.jar=${GHIDRA_HOME}/Ghidra/Features/Base/lib/Base.jar
# ============================================================================= # =============================================================================
# Stage 2: Runtime image with Ghidra + GhydraMCP # Stage 2: Runtime image with Ghidra + MCGhidra
# ============================================================================= # =============================================================================
# NOTE: Ghidra requires JDK (not JRE) - it checks for javac in LaunchSupport # NOTE: Ghidra requires JDK (not JRE) - it checks for javac in LaunchSupport
FROM eclipse-temurin:21-jdk-jammy AS runtime FROM eclipse-temurin:21-jdk-jammy AS runtime
@ -71,9 +71,9 @@ FROM eclipse-temurin:21-jdk-jammy AS runtime
ARG GHIDRA_VERSION ARG GHIDRA_VERSION
ARG GHIDRA_DATE ARG GHIDRA_DATE
LABEL org.opencontainers.image.title="ghydramcp" \ LABEL org.opencontainers.image.title="mcghidra" \
org.opencontainers.image.description="Ghidra + GhydraMCP Plugin for AI-assisted reverse engineering" \ org.opencontainers.image.description="Ghidra + MCGhidra Plugin for AI-assisted reverse engineering" \
org.opencontainers.image.source="https://github.com/starsong-consulting/GhydraMCP" \ org.opencontainers.image.source="https://github.com/starsong-consulting/MCGhidra" \
org.opencontainers.image.licenses="Apache-2.0" org.opencontainers.image.licenses="Apache-2.0"
# Install runtime dependencies # Install runtime dependencies
@ -99,21 +99,21 @@ RUN curl -fsSL "https://github.com/NationalSecurityAgency/ghidra/releases/downlo
ENV GHIDRA_HOME=/opt/ghidra ENV GHIDRA_HOME=/opt/ghidra
ENV PATH="${GHIDRA_HOME}:${PATH}" ENV PATH="${GHIDRA_HOME}:${PATH}"
# Install the GhydraMCP plugin # Install the MCGhidra plugin
COPY --from=builder /build/target/GhydraMCP-*.zip /tmp/ COPY --from=builder /build/target/MCGhidra-*.zip /tmp/
RUN mkdir -p /opt/ghidra/Ghidra/Extensions \ RUN mkdir -p /opt/ghidra/Ghidra/Extensions \
&& unzip -q /tmp/GhydraMCP-*.zip -d /opt/ghidra/Ghidra/Extensions/ \ && unzip -q /tmp/MCGhidra-*.zip -d /opt/ghidra/Ghidra/Extensions/ \
&& rm /tmp/GhydraMCP-*.zip \ && rm /tmp/MCGhidra-*.zip \
&& chown -R ghidra:ghidra /opt/ghidra/Ghidra/Extensions/ && chown -R ghidra:ghidra /opt/ghidra/Ghidra/Extensions/
# Create directories for projects and binaries # Create directories for projects and binaries
RUN mkdir -p /projects /binaries /home/ghidra/.ghidra \ RUN mkdir -p /projects /binaries /home/ghidra/.ghidra \
&& chown -R ghidra:ghidra /projects /binaries /home/ghidra && chown -R ghidra:ghidra /projects /binaries /home/ghidra
# Copy GhydraMCP Python scripts to user scripts directory # Copy MCGhidra Python scripts to user scripts directory
# Python/Jython scripts don't require OSGi bundle registration - they work without issue # Python/Jython scripts don't require OSGi bundle registration - they work without issue
RUN mkdir -p /home/ghidra/ghidra_scripts RUN mkdir -p /home/ghidra/ghidra_scripts
COPY docker/GhydraMCPServer.py /home/ghidra/ghidra_scripts/ COPY docker/MCGhidraServer.py /home/ghidra/ghidra_scripts/
COPY docker/ImportRawARM.java /home/ghidra/ghidra_scripts/ COPY docker/ImportRawARM.java /home/ghidra/ghidra_scripts/
# Set proper ownership and permissions # Set proper ownership and permissions
@ -129,16 +129,16 @@ RUN chmod 755 /entrypoint.sh
USER ghidra USER ghidra
WORKDIR /home/ghidra WORKDIR /home/ghidra
# Expose the GhydraMCP HTTP API port (and additional ports for multiple instances) # Expose the MCGhidra HTTP API port (and additional ports for multiple instances)
EXPOSE 8192 8193 8194 8195 EXPOSE 8192 8193 8194 8195
# Default environment # Default environment
ENV GHYDRA_MODE=headless ENV MCGHIDRA_MODE=headless
ENV GHYDRA_PORT=8192 ENV MCGHIDRA_PORT=8192
ENV GHYDRA_MAXMEM=2G ENV MCGHIDRA_MAXMEM=2G
# Healthcheck # Healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:${GHYDRA_PORT}/ || exit 1 CMD curl -f http://localhost:${MCGHIDRA_PORT}/ || exit 1
ENTRYPOINT ["/entrypoint.sh"] ENTRYPOINT ["/entrypoint.sh"]

View File

@ -1,10 +1,10 @@
# GhydraMCPServer.py - Headless Ghidra script for GhydraMCP HTTP API # MCGhidraServer.py - Headless Ghidra script for MCGhidra HTTP API
# Full API parity with the Java plugin implementation. # Full API parity with the Java plugin implementation.
# Python 2 / Jython compatible (no f-strings, no readAllBytes). # Python 2 / Jython compatible (no f-strings, no readAllBytes).
# #
# Usage: analyzeHeadless <project> <name> -import <binary> -postScript GhydraMCPServer.py [port] # Usage: analyzeHeadless <project> <name> -import <binary> -postScript MCGhidraServer.py [port]
# #
#@category GhydraMCP #@category MCGhidra
#@keybinding #@keybinding
#@menupath #@menupath
#@toolbar #@toolbar
@ -366,7 +366,7 @@ ROUTES = [
# HTTP Handler # HTTP Handler
# ======================================================================== # ========================================================================
class GhydraMCPHandler(HttpHandler): class MCGhidraHandler(HttpHandler):
def __init__(self, program, decompiler): def __init__(self, program, decompiler):
self.program = program self.program = program
@ -641,7 +641,7 @@ class GhydraMCPHandler(HttpHandler):
"success": True, "success": True,
"api_version": API_VERSION, "api_version": API_VERSION,
"api_version_string": API_VERSION_STRING, "api_version_string": API_VERSION_STRING,
"message": "GhydraMCP Headless API", "message": "MCGhidra Headless API",
"mode": "headless", "mode": "headless",
} }
if self.program: if self.program:
@ -2748,10 +2748,10 @@ class GhydraMCPHandler(HttpHandler):
def run_server(port, program, decompiler): def run_server(port, program, decompiler):
"""Start the HTTP server with a single catch-all handler.""" """Start the HTTP server with a single catch-all handler."""
server = HttpServer.create(InetSocketAddress(port), 0) server = HttpServer.create(InetSocketAddress(port), 0)
server.createContext("/", GhydraMCPHandler(program, decompiler)) server.createContext("/", MCGhidraHandler(program, decompiler))
server.setExecutor(Executors.newCachedThreadPool()) server.setExecutor(Executors.newCachedThreadPool())
server.start() server.start()
println("[GhydraMCP] HTTP server started on port %d" % port) println("[MCGhidra] HTTP server started on port %d" % port)
return server return server
@ -2775,7 +2775,7 @@ def main():
decompiler.openProgram(currentProgram) decompiler.openProgram(currentProgram)
println("=========================================") println("=========================================")
println(" GhydraMCP Headless HTTP Server") println(" MCGhidra Headless HTTP Server")
println("=========================================") println("=========================================")
println(" API Version: %s (compat: %d)" % (API_VERSION_STRING, API_VERSION)) println(" API Version: %s (compat: %d)" % (API_VERSION_STRING, API_VERSION))
println(" Port: %d" % port) println(" Port: %d" % port)
@ -2787,7 +2787,7 @@ def main():
server = run_server(port, currentProgram, decompiler) server = run_server(port, currentProgram, decompiler)
println("") println("")
println("GhydraMCP Server running. Press Ctrl+C to stop.") println("MCGhidra Server running. Press Ctrl+C to stop.")
println("API available at: http://localhost:%d/" % port) println("API available at: http://localhost:%d/" % port)
# Keep the script running # Keep the script running
@ -2796,7 +2796,7 @@ def main():
time.sleep(1) time.sleep(1)
except KeyboardInterrupt: except KeyboardInterrupt:
server.stop(0) server.stop(0)
println("[GhydraMCP] Server stopped.") println("[MCGhidra] Server stopped.")
# Run # Run

View File

@ -1,6 +1,6 @@
// Import and analyze raw ARM firmware binary // Import and analyze raw ARM firmware binary
// This script imports a raw binary file with specified ARM processor and load address // This script imports a raw binary file with specified ARM processor and load address
// @author GhydraMCP // @author MCGhidra
// @category Binary.Import // @category Binary.Import
// @keybinding // @keybinding
// @menupath // @menupath

View File

@ -1,15 +1,15 @@
# GhydraMCP Docker Setup # MCGhidra Docker Setup
This directory contains Docker configuration for running GhydraMCP in headless mode. This directory contains Docker configuration for running MCGhidra in headless mode.
## Quick Start ## Quick Start
```bash ```bash
# Build the image # Build the image
docker build -t ghydramcp:latest -f docker/Dockerfile . docker build -t mcghidra:latest -f docker/Dockerfile .
# Analyze a binary # Analyze a binary
docker run -p 8192:8192 -v /path/to/binaries:/binaries ghydramcp /binaries/sample.exe docker run -p 8192:8192 -v /path/to/binaries:/binaries mcghidra /binaries/sample.exe
# Check API health # Check API health
curl http://localhost:8192/ curl http://localhost:8192/
@ -20,17 +20,17 @@ curl http://localhost:8192/
The Docker container includes: The Docker container includes:
1. **Ghidra 11.4.2** - Full headless installation 1. **Ghidra 11.4.2** - Full headless installation
2. **GhydraMCP Extension** - The Java plugin (installed in Extensions/) 2. **MCGhidra Extension** - The Java plugin (installed in Extensions/)
3. **GhydraMCPServer.py** - Headless HTTP server (Jython, full API parity) 3. **MCGhidraServer.py** - Headless HTTP server (Jython, full API parity)
### Why Two HTTP Servers? ### Why Two HTTP Servers?
The GhydraMCP plugin (`GhydraMCPPlugin.java`) is a full Ghidra GUI plugin that requires: The MCGhidra plugin (`MCGhidraPlugin.java`) is a full Ghidra GUI plugin that requires:
- Ghidra's `PluginTool` framework - Ghidra's `PluginTool` framework
- `ProgramManager` service for program access - `ProgramManager` service for program access
- GUI event handling - GUI event handling
These GUI services don't exist in headless mode. Instead, the container uses `GhydraMCPServer.py`, a Jython script that: These GUI services don't exist in headless mode. Instead, the container uses `MCGhidraServer.py`, a Jython script that:
- Runs via `analyzeHeadless -postScript` - Runs via `analyzeHeadless -postScript`
- Has direct access to `currentProgram` from the script context - Has direct access to `currentProgram` from the script context
- Provides **full API parity** with the GUI plugin (45 routes) - Provides **full API parity** with the GUI plugin (45 routes)
@ -38,7 +38,7 @@ These GUI services don't exist in headless mode. Instead, the container uses `Gh
### Available Endpoints (Headless Mode) ### Available Endpoints (Headless Mode)
The headless server implements the complete GhydraMCP HTTP API: The headless server implements the complete MCGhidra HTTP API:
| Category | Endpoints | Description | | Category | Endpoints | Description |
|----------|-----------|-------------| |----------|-----------|-------------|
@ -65,7 +65,7 @@ Imports a binary, analyzes it, and starts the HTTP API server:
```bash ```bash
docker run -p 8192:8192 \ docker run -p 8192:8192 \
-v ./samples:/binaries \ -v ./samples:/binaries \
ghydramcp /binaries/sample.exe mcghidra /binaries/sample.exe
``` ```
### Server Mode ### Server Mode
@ -74,9 +74,9 @@ Opens an existing project and program:
```bash ```bash
docker run -p 8192:8192 \ docker run -p 8192:8192 \
-e GHYDRA_MODE=server \ -e MCGHIDRA_MODE=server \
-v ./projects:/projects \ -v ./projects:/projects \
ghydramcp program_name mcghidra program_name
``` ```
### Analyze Mode ### Analyze Mode
@ -85,10 +85,10 @@ Imports and analyzes without starting HTTP server:
```bash ```bash
docker run \ docker run \
-e GHYDRA_MODE=analyze \ -e MCGHIDRA_MODE=analyze \
-v ./samples:/binaries \ -v ./samples:/binaries \
-v ./projects:/projects \ -v ./projects:/projects \
ghydramcp /binaries/sample.exe mcghidra /binaries/sample.exe
``` ```
### Shell Mode ### Shell Mode
@ -97,19 +97,19 @@ Interactive debugging:
```bash ```bash
docker run -it \ docker run -it \
-e GHYDRA_MODE=shell \ -e MCGHIDRA_MODE=shell \
ghydramcp mcghidra
``` ```
## Environment Variables ## Environment Variables
| Variable | Default | Description | | Variable | Default | Description |
|----------|---------|-------------| |----------|---------|-------------|
| `GHYDRA_MODE` | `headless` | Container mode (headless, server, analyze, shell) | | `MCGHIDRA_MODE` | `headless` | Container mode (headless, server, analyze, shell) |
| `GHYDRA_PORT` | `8192` | HTTP API port | | `MCGHIDRA_PORT` | `8192` | HTTP API port |
| `GHYDRA_MAXMEM` | `2G` | JVM heap memory | | `MCGHIDRA_MAXMEM` | `2G` | JVM heap memory |
| `PROJECT_DIR` | `/projects` | Ghidra project directory | | `PROJECT_DIR` | `/projects` | Ghidra project directory |
| `PROJECT_NAME` | `GhydraMCP` | Ghidra project name | | `PROJECT_NAME` | `MCGhidra` | Ghidra project name |
## Docker Compose ## Docker Compose
@ -117,18 +117,18 @@ Use docker-compose for easier management:
```bash ```bash
# Development mode (hot-reload scripts) # Development mode (hot-reload scripts)
docker compose --profile dev up ghydramcp-dev docker compose --profile dev up mcghidra-dev
# Production mode # Production mode
docker compose --profile prod up ghydramcp docker compose --profile prod up mcghidra
# Interactive shell # Interactive shell
docker compose --profile debug run --rm ghydramcp-shell docker compose --profile debug run --rm mcghidra-shell
``` ```
## MCP Integration ## MCP Integration
The GhydraMCP Python server includes Docker management tools: The MCGhidra Python server includes Docker management tools:
```python ```python
# Check Docker status # Check Docker status
@ -144,10 +144,10 @@ await docker_wait(port=8192, timeout=300)
await docker_auto_start(binary_path="/path/to/binary.exe") await docker_auto_start(binary_path="/path/to/binary.exe")
# Get container logs # Get container logs
await docker_logs("ghydramcp-server") await docker_logs("mcghidra-server")
# Stop container # Stop container
await docker_stop("ghydramcp-server") await docker_stop("mcghidra-server")
``` ```
## Building ## Building
@ -157,10 +157,10 @@ await docker_stop("ghydramcp-server")
make build make build
# Using Docker directly # Using Docker directly
docker build -t ghydramcp:latest -f docker/Dockerfile . docker build -t mcghidra:latest -f docker/Dockerfile .
# Build with specific Ghidra version # Build with specific Ghidra version
docker build -t ghydramcp:latest \ docker build -t mcghidra:latest \
--build-arg GHIDRA_VERSION=11.4.2 \ --build-arg GHIDRA_VERSION=11.4.2 \
--build-arg GHIDRA_DATE=20250826 \ --build-arg GHIDRA_DATE=20250826 \
-f docker/Dockerfile . -f docker/Dockerfile .
@ -172,21 +172,21 @@ docker build -t ghydramcp:latest \
Analysis takes time. Monitor progress with: Analysis takes time. Monitor progress with:
```bash ```bash
docker logs -f ghydramcp-server docker logs -f mcghidra-server
``` ```
### Port already in use ### Port already in use
Stop existing containers: Stop existing containers:
```bash ```bash
docker stop $(docker ps -q --filter "name=ghydramcp") docker stop $(docker ps -q --filter "name=mcghidra")
``` ```
### Memory issues with large binaries ### Memory issues with large binaries
Increase JVM heap: Increase JVM heap:
```bash ```bash
docker run -e GHYDRA_MAXMEM=4G -p 8192:8192 ghydramcp /binaries/large.exe docker run -e MCGHIDRA_MAXMEM=4G -p 8192:8192 mcghidra /binaries/large.exe
``` ```
### Permission denied on volumes ### Permission denied on volumes

View File

@ -1,26 +1,26 @@
#!/bin/bash #!/bin/bash
# GhydraMCP Docker Entrypoint # MCGhidra Docker Entrypoint
# Starts Ghidra in headless mode with HTTP API server # Starts Ghidra in headless mode with HTTP API server
set -e set -e
GHYDRA_MODE=${GHYDRA_MODE:-headless} MCGHIDRA_MODE=${MCGHIDRA_MODE:-headless}
GHYDRA_PORT=${GHYDRA_PORT:-8192} MCGHIDRA_PORT=${MCGHIDRA_PORT:-8192}
GHYDRA_MAXMEM=${GHYDRA_MAXMEM:-2G} MCGHIDRA_MAXMEM=${MCGHIDRA_MAXMEM:-2G}
GHIDRA_HOME=${GHIDRA_HOME:-/opt/ghidra} GHIDRA_HOME=${GHIDRA_HOME:-/opt/ghidra}
# User scripts directory - Python scripts don't need OSGi bundle registration # User scripts directory - Python scripts don't need OSGi bundle registration
SCRIPT_DIR=${SCRIPT_DIR:-/home/ghidra/ghidra_scripts} SCRIPT_DIR=${SCRIPT_DIR:-/home/ghidra/ghidra_scripts}
# Project settings # Project settings
PROJECT_DIR=${PROJECT_DIR:-/projects} PROJECT_DIR=${PROJECT_DIR:-/projects}
PROJECT_NAME=${PROJECT_NAME:-GhydraMCP} PROJECT_NAME=${PROJECT_NAME:-MCGhidra}
echo "==============================================" echo "=============================================="
echo " GhydraMCP Docker Container" echo " MCGhidra Docker Container"
echo "==============================================" echo "=============================================="
echo " Mode: ${GHYDRA_MODE}" echo " Mode: ${MCGHIDRA_MODE}"
echo " Port: ${GHYDRA_PORT}" echo " Port: ${MCGHIDRA_PORT}"
echo " Memory: ${GHYDRA_MAXMEM}" echo " Memory: ${MCGHIDRA_MAXMEM}"
echo " Project: ${PROJECT_DIR}/${PROJECT_NAME}" echo " Project: ${PROJECT_DIR}/${PROJECT_NAME}"
echo "==============================================" echo "=============================================="
@ -28,25 +28,25 @@ echo "=============================================="
mkdir -p "${PROJECT_DIR}" mkdir -p "${PROJECT_DIR}"
# Handle different modes # Handle different modes
case "${GHYDRA_MODE}" in case "${MCGHIDRA_MODE}" in
headless) headless)
# Headless mode: Import a binary and start HTTP server # Headless mode: Import a binary and start HTTP server
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
echo "" echo ""
echo "Usage: docker run ghydramcp:latest [binary_path] [options]" echo "Usage: docker run mcghidra:latest [binary_path] [options]"
echo "" echo ""
echo "Examples:" echo "Examples:"
echo " # Analyze a binary mounted at /binaries/sample.exe" echo " # Analyze a binary mounted at /binaries/sample.exe"
echo " docker run -p 8192:8192 -v ./samples:/binaries ghydramcp /binaries/sample.exe" echo " docker run -p 8192:8192 -v ./samples:/binaries mcghidra /binaries/sample.exe"
echo "" echo ""
echo " # With custom project name" echo " # With custom project name"
echo " docker run -p 8192:8192 -v ./samples:/binaries -e PROJECT_NAME=malware ghydramcp /binaries/sample.exe" echo " docker run -p 8192:8192 -v ./samples:/binaries -e PROJECT_NAME=malware mcghidra /binaries/sample.exe"
echo "" echo ""
echo "Environment variables:" echo "Environment variables:"
echo " GHYDRA_PORT - HTTP API port (default: 8192)" echo " MCGHIDRA_PORT - HTTP API port (default: 8192)"
echo " GHYDRA_MAXMEM - Max JVM heap (default: 2G)" echo " MCGHIDRA_MAXMEM - Max JVM heap (default: 2G)"
echo " PROJECT_NAME - Ghidra project name (default: GhydraMCP)" echo " PROJECT_NAME - Ghidra project name (default: MCGhidra)"
echo " PROJECT_DIR - Project directory (default: /projects)" echo " PROJECT_DIR - Project directory (default: /projects)"
echo "" echo ""
echo "Starting in wait mode..." echo "Starting in wait mode..."
@ -78,7 +78,7 @@ case "${GHYDRA_MODE}" in
-import "${BINARY_PATH}" -import "${BINARY_PATH}"
-max-cpu 2 -max-cpu 2
-scriptPath "${SCRIPT_DIR}" -scriptPath "${SCRIPT_DIR}"
-postScript "GhydraMCPServer.py" "${GHYDRA_PORT}" -postScript "MCGhidraServer.py" "${MCGHIDRA_PORT}"
) )
# Add any extra arguments passed # Add any extra arguments passed
@ -93,10 +93,10 @@ case "${GHYDRA_MODE}" in
server) server)
# Server mode: Open existing project with HTTP server # Server mode: Open existing project with HTTP server
echo "Starting GhydraMCP server on existing project..." echo "Starting MCGhidra server on existing project..."
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
echo "Usage: docker run -e GHYDRA_MODE=server ghydramcp [program_name]" echo "Usage: docker run -e MCGHIDRA_MODE=server mcghidra [program_name]"
echo "" echo ""
echo " program_name: Name of program in the project to open" echo " program_name: Name of program in the project to open"
exit 1 exit 1
@ -110,14 +110,14 @@ case "${GHYDRA_MODE}" in
-process "${PROGRAM_NAME}" \ -process "${PROGRAM_NAME}" \
-noanalysis \ -noanalysis \
-scriptPath "${SCRIPT_DIR}" \ -scriptPath "${SCRIPT_DIR}" \
-postScript "GhydraMCPServer.py" "${GHYDRA_PORT}" \ -postScript "MCGhidraServer.py" "${MCGHIDRA_PORT}" \
"$@" "$@"
;; ;;
analyze) analyze)
# Analyze mode: Import and analyze, then exit (no HTTP server) # Analyze mode: Import and analyze, then exit (no HTTP server)
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
echo "Usage: docker run -e GHYDRA_MODE=analyze ghydramcp [binary_path]" echo "Usage: docker run -e MCGHIDRA_MODE=analyze mcghidra [binary_path]"
exit 1 exit 1
fi fi
@ -138,7 +138,7 @@ case "${GHYDRA_MODE}" in
;; ;;
*) *)
echo "Unknown mode: ${GHYDRA_MODE}" echo "Unknown mode: ${MCGHIDRA_MODE}"
echo "Valid modes: headless, server, analyze, shell" echo "Valid modes: headless, server, analyze, shell"
exit 1 exit 1
;; ;;

20
pom.xml
View File

@ -4,11 +4,11 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>eu.starsong.ghidra</groupId> <groupId>eu.starsong.ghidra</groupId>
<artifactId>GhydraMCP</artifactId> <artifactId>MCGhidra</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>dev</version> <version>dev</version>
<name>GhydraMCP</name> <name>MCGhidra</name>
<url>https://github.com/starsong-consulting/GhydraMCP</url> <url>https://github.com/starsong-consulting/MCGhidra</url>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -25,7 +25,7 @@
<maven.install.skip>true</maven.install.skip> <maven.install.skip>true</maven.install.skip>
<maven.build.timestamp.format>yyyyMMdd-HHmmss</maven.build.timestamp.format> <maven.build.timestamp.format>yyyyMMdd-HHmmss</maven.build.timestamp.format>
<revision>dev</revision> <revision>dev</revision>
<inner.zip.filename>GhydraMCP-${git.commit.id.describe}-${maven.build.timestamp}.zip</inner.zip.filename> <inner.zip.filename>MCGhidra-${git.commit.id.describe}-${maven.build.timestamp}.zip</inner.zip.filename>
</properties> </properties>
<dependencies> <dependencies>
@ -153,16 +153,16 @@
<addDefaultImplementationEntries>false</addDefaultImplementationEntries> <addDefaultImplementationEntries>false</addDefaultImplementationEntries>
</manifest> </manifest>
<manifestEntries> <manifestEntries>
<Implementation-Title>GhydraMCP</Implementation-Title> <Implementation-Title>MCGhidra</Implementation-Title>
<Implementation-Version>${git.commit.id.abbrev}-${maven.build.timestamp}</Implementation-Version> <Implementation-Version>${git.commit.id.abbrev}-${maven.build.timestamp}</Implementation-Version>
<Plugin-Class>eu.starsong.ghidra.GhydraMCP</Plugin-Class> <Plugin-Class>eu.starsong.ghidra.MCGhidra</Plugin-Class>
<Plugin-Name>GhydraMCP</Plugin-Name> <Plugin-Name>MCGhidra</Plugin-Name>
<Plugin-Version>${git.commit.id.abbrev}-${maven.build.timestamp}</Plugin-Version> <Plugin-Version>${git.commit.id.abbrev}-${maven.build.timestamp}</Plugin-Version>
<Plugin-Author>LaurieWired, Teal Bauer</Plugin-Author> <Plugin-Author>LaurieWired, Teal Bauer</Plugin-Author>
<Plugin-Description>Expose multiple Ghidra tools to MCP servers with variable management</Plugin-Description> <Plugin-Description>Expose multiple Ghidra tools to MCP servers with variable management</Plugin-Description>
</manifestEntries> </manifestEntries>
</archive> </archive>
<finalName>GhydraMCP</finalName> <finalName>MCGhidra</finalName>
<excludes> <excludes>
<exclude>**/App.class</exclude> <exclude>**/App.class</exclude>
</excludes> </excludes>
@ -187,7 +187,7 @@
<descriptors> <descriptors>
<descriptor>src/assembly/ghidra-extension.xml</descriptor> <descriptor>src/assembly/ghidra-extension.xml</descriptor>
</descriptors> </descriptors>
<finalName>GhydraMCP-${git.commit.id.describe}-${maven.build.timestamp}</finalName> <finalName>MCGhidra-${git.commit.id.describe}-${maven.build.timestamp}</finalName>
<appendAssemblyId>false</appendAssemblyId> <appendAssemblyId>false</appendAssemblyId>
</configuration> </configuration>
</execution> </execution>
@ -203,7 +203,7 @@
<descriptors> <descriptors>
<descriptor>src/assembly/complete-package.xml</descriptor> <descriptor>src/assembly/complete-package.xml</descriptor>
</descriptors> </descriptors>
<finalName>GhydraMCP-Complete-${git.commit.id.describe}-${maven.build.timestamp}</finalName> <finalName>MCGhidra-Complete-${git.commit.id.describe}-${maven.build.timestamp}</finalName>
<appendAssemblyId>false</appendAssemblyId> <appendAssemblyId>false</appendAssemblyId>
</configuration> </configuration>
</execution> </execution>

View File

@ -1,5 +1,5 @@
[project] [project]
name = "ghydramcp" name = "mcghidra"
version = "2025.12.3" version = "2025.12.3"
description = "AI-assisted reverse engineering bridge: a multi-instance Ghidra plugin exposed via a HATEOAS REST API plus an MCP Python bridge for decompilation, analysis & binary manipulation" description = "AI-assisted reverse engineering bridge: a multi-instance Ghidra plugin exposed via a HATEOAS REST API plus an MCP Python bridge for decompilation, analysis & binary manipulation"
readme = "README.md" readme = "README.md"
@ -15,14 +15,14 @@ dependencies = [
] ]
[project.scripts] [project.scripts]
ghydramcp = "ghydramcp:main" mcghidra = "mcghidra:main"
[build-system] [build-system]
requires = ["hatchling"] requires = ["hatchling"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel] [tool.hatch.build.targets.wheel]
packages = ["src/ghydramcp"] packages = ["src/mcghidra"]
[tool.hatch.build] [tool.hatch.build]
sources = ["src"] sources = ["src"]

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Test runner for GhydraMCP tests. Test runner for MCGhidra tests.
This script runs both the HTTP API tests and the MCP bridge tests. This script runs both the HTTP API tests and the MCP bridge tests.
""" """
import os import os
@ -21,10 +21,10 @@ def run_http_api_tests():
# Import and run the tests # Import and run the tests
try: try:
from test_http_api import GhydraMCPHttpApiTests from test_http_api import MCGhidraHttpApiTests
# Create a test suite with all tests from GhydraMCPHttpApiTests # Create a test suite with all tests from MCGhidraHttpApiTests
suite = unittest.TestLoader().loadTestsFromTestCase(GhydraMCPHttpApiTests) suite = unittest.TestLoader().loadTestsFromTestCase(MCGhidraHttpApiTests)
# Run the tests # Run the tests
result = unittest.TextTestRunner(verbosity=2).run(suite) result = unittest.TextTestRunner(verbosity=2).run(suite)
@ -118,7 +118,7 @@ def run_comment_tests():
def run_all_tests(): def run_all_tests():
"""Run all tests""" """Run all tests"""
print_header("GhydraMCP Test Suite") print_header("MCGhidra Test Suite")
# Run test suites # Run test suites
http_api_success = run_http_api_tests() http_api_success = run_http_api_tests()

View File

@ -11,23 +11,23 @@
<includeBaseDirectory>false</includeBaseDirectory> <includeBaseDirectory>false</includeBaseDirectory>
<fileSets> <fileSets>
<!-- Copy extension files to GhydraMCP/ directory --> <!-- Copy extension files to MCGhidra/ directory -->
<fileSet> <fileSet>
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
<includes> <includes>
<include>extension.properties</include> <include>extension.properties</include>
<include>Module.manifest</include> <include>Module.manifest</include>
</includes> </includes>
<outputDirectory>GhydraMCP</outputDirectory> <outputDirectory>MCGhidra</outputDirectory>
</fileSet> </fileSet>
</fileSets> </fileSets>
<dependencySets> <dependencySets>
<!-- Include the main project JAR as GhydraMCP.jar --> <!-- Include the main project JAR as MCGhidra.jar -->
<dependencySet> <dependencySet>
<useProjectArtifact>true</useProjectArtifact> <useProjectArtifact>true</useProjectArtifact>
<outputDirectory>GhydraMCP/lib</outputDirectory> <outputDirectory>MCGhidra/lib</outputDirectory>
<outputFileNameMapping>GhydraMCP.jar</outputFileNameMapping> <outputFileNameMapping>MCGhidra.jar</outputFileNameMapping>
<unpack>false</unpack> <unpack>false</unpack>
</dependencySet> </dependencySet>
</dependencySets> </dependencySets>

View File

@ -1,9 +0,0 @@
"""GhydraMCP package entry point.
Allows running with: python -m ghydramcp
"""
from .server import main
if __name__ == "__main__":
main()

View File

@ -39,14 +39,14 @@ import ghidra.util.Msg;
status = PluginStatus.RELEASED, status = PluginStatus.RELEASED,
packageName = ghidra.app.DeveloperPluginPackage.NAME, packageName = ghidra.app.DeveloperPluginPackage.NAME,
category = PluginCategoryNames.ANALYSIS, category = PluginCategoryNames.ANALYSIS,
shortDescription = "GhydraMCP Plugin for AI Analysis", shortDescription = "MCGhidra Plugin for AI Analysis",
description = "Exposes program data via HATEOAS HTTP API for AI-assisted reverse engineering with MCP (Model Context Protocol).", description = "Exposes program data via HATEOAS HTTP API for AI-assisted reverse engineering with MCP (Model Context Protocol).",
servicesRequired = { ProgramManager.class } servicesRequired = { ProgramManager.class }
) )
public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin { public class MCGhidraPlugin extends Plugin implements ApplicationLevelPlugin {
// Made public static to be accessible by InstanceEndpoints // Made public static to be accessible by InstanceEndpoints
public static final Map<Integer, GhydraMCPPlugin> activeInstances = new ConcurrentHashMap<>(); public static final Map<Integer, MCGhidraPlugin> activeInstances = new ConcurrentHashMap<>();
private static final Object baseInstanceLock = new Object(); private static final Object baseInstanceLock = new Object();
private HttpServer server; private HttpServer server;
@ -54,10 +54,10 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
private boolean isBaseInstance = false; private boolean isBaseInstance = false;
/** /**
* Constructor for GhydraMCP Plugin. * Constructor for MCGhidra Plugin.
* @param tool The Ghidra PluginTool * @param tool The Ghidra PluginTool
*/ */
public GhydraMCPPlugin(PluginTool tool) { public MCGhidraPlugin(PluginTool tool) {
super(tool); super(tool);
this.port = findAvailablePort(); this.port = findAvailablePort();
@ -70,8 +70,8 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
} }
} }
Msg.info(this, "GhydraMCPPlugin loaded on port " + port); Msg.info(this, "MCGhidraPlugin loaded on port " + port);
System.out.println("[GhydraMCP] Plugin loaded on port " + port); System.out.println("[MCGhidra] Plugin loaded on port " + port);
try { try {
startServer(); startServer();
@ -111,9 +111,9 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
new Thread(() -> { new Thread(() -> {
server.start(); server.start();
Msg.info(this, "GhydraMCP HTTP server started on port " + port); Msg.info(this, "MCGhidra HTTP server started on port " + port);
System.out.println("[GhydraMCP] HTTP server started on port " + port); System.out.println("[MCGhidra] HTTP server started on port " + port);
}, "GhydraMCP-HTTP-Server").start(); }, "MCGhidra-HTTP-Server").start();
} }
/** /**
@ -350,7 +350,7 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
} }
Map<String, Object> rootData = new HashMap<>(); Map<String, Object> rootData = new HashMap<>();
rootData.put("message", "GhydraMCP API " + ApiConstants.API_VERSION); rootData.put("message", "MCGhidra API " + ApiConstants.API_VERSION);
rootData.put("documentation", "See GHIDRA_HTTP_API.md for full API documentation"); rootData.put("documentation", "See GHIDRA_HTTP_API.md for full API documentation");
rootData.put("isBaseInstance", isBaseInstance); rootData.put("isBaseInstance", isBaseInstance);
@ -449,8 +449,8 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
public void dispose() { public void dispose() {
if (server != null) { if (server != null) {
server.stop(0); // Stop immediately server.stop(0); // Stop immediately
Msg.info(this, "GhydraMCP HTTP server stopped on port " + port); Msg.info(this, "MCGhidra HTTP server stopped on port " + port);
System.out.println("[GhydraMCP] HTTP server stopped on port " + port); System.out.println("[MCGhidra] HTTP server stopped on port " + port);
} }
activeInstances.remove(port); activeInstances.remove(port);
super.dispose(); super.dispose();

View File

@ -4,7 +4,7 @@ package eu.starsong.ghidra.endpoints;
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpServer;
import eu.starsong.ghidra.api.ResponseBuilder; import eu.starsong.ghidra.api.ResponseBuilder;
import eu.starsong.ghidra.GhydraMCPPlugin; // Need access to activeInstances import eu.starsong.ghidra.MCGhidraPlugin; // Need access to activeInstances
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -13,16 +13,16 @@ package eu.starsong.ghidra.endpoints;
public class InstanceEndpoints extends AbstractEndpoint { public class InstanceEndpoints extends AbstractEndpoint {
// Need a way to access the static activeInstances map from GhydraMCPPlugin // Need a way to access the static activeInstances map from MCGhidraPlugin
// This is a bit awkward and suggests the instance management might need // This is a bit awkward and suggests the instance management might need
// a different design, perhaps a dedicated manager class. // a different design, perhaps a dedicated manager class.
// For now, we pass the map or use a static accessor if made public. // For now, we pass the map or use a static accessor if made public.
private final Map<Integer, GhydraMCPPlugin> activeInstances; private final Map<Integer, MCGhidraPlugin> activeInstances;
// Note: Passing currentProgram might be null here if no program is open. // Note: Passing currentProgram might be null here if no program is open.
// The constructor in AbstractEndpoint handles null program. // The constructor in AbstractEndpoint handles null program.
// Updated constructor to accept port // Updated constructor to accept port
public InstanceEndpoints(Program program, int port, Map<Integer, GhydraMCPPlugin> instances) { public InstanceEndpoints(Program program, int port, Map<Integer, MCGhidraPlugin> instances) {
super(program, port); // Call super constructor super(program, port); // Call super constructor
this.activeInstances = instances; this.activeInstances = instances;
} }
@ -46,7 +46,7 @@ package eu.starsong.ghidra.endpoints;
// Accessing the static map directly - requires it to be accessible // Accessing the static map directly - requires it to be accessible
// or passed in constructor. // or passed in constructor.
for (Map.Entry<Integer, GhydraMCPPlugin> entry : activeInstances.entrySet()) { for (Map.Entry<Integer, MCGhidraPlugin> entry : activeInstances.entrySet()) {
Map<String, Object> instance = new HashMap<>(); Map<String, Object> instance = new HashMap<>();
int instancePort = entry.getKey(); int instancePort = entry.getKey();
instance.put("port", instancePort); instance.put("port", instancePort);

View File

@ -1,4 +1,4 @@
"""GhydraMCP - AI-assisted reverse engineering bridge for Ghidra. """MCGhidra - AI-assisted reverse engineering bridge for Ghidra.
A multi-instance Ghidra plugin exposed via HATEOAS REST API plus an MCP A multi-instance Ghidra plugin exposed via HATEOAS REST API plus an MCP
Python bridge for decompilation, analysis & binary manipulation. Python bridge for decompilation, analysis & binary manipulation.
@ -6,7 +6,7 @@ Python bridge for decompilation, analysis & binary manipulation.
try: try:
from importlib.metadata import version from importlib.metadata import version
__version__ = version("ghydramcp") __version__ = version("mcghidra")
except Exception: except Exception:
__version__ = "2025.12.1" __version__ = "2025.12.1"

9
src/mcghidra/__main__.py Normal file
View File

@ -0,0 +1,9 @@
"""MCGhidra package entry point.
Allows running with: python -m mcghidra
"""
from .server import main
if __name__ == "__main__":
main()

View File

@ -1,4 +1,4 @@
"""Configuration management for GhydraMCP. """Configuration management for MCGhidra.
Handles environment variables, default settings, and runtime configuration. Handles environment variables, default settings, and runtime configuration.
""" """
@ -14,18 +14,18 @@ class DockerConfig:
"""Docker-specific configuration.""" """Docker-specific configuration."""
# Docker image settings # Docker image settings
image_name: str = "ghydramcp" image_name: str = "mcghidra"
image_tag: str = field(default_factory=lambda: os.environ.get("GHYDRAMCP_VERSION", "latest")) image_tag: str = field(default_factory=lambda: os.environ.get("MCGHIDRAMCP_VERSION", "latest"))
# Default container settings # Default container settings
default_port: int = field(default_factory=lambda: int(os.environ.get("GHYDRA_PORT", "8192"))) default_port: int = field(default_factory=lambda: int(os.environ.get("MCGHIDRA_PORT", "8192")))
default_memory: str = field(default_factory=lambda: os.environ.get("GHYDRA_MAXMEM", "2G")) default_memory: str = field(default_factory=lambda: os.environ.get("MCGHIDRA_MAXMEM", "2G"))
# Project directory (for building) # Project directory (for building)
project_dir: Optional[Path] = None project_dir: Optional[Path] = None
# Auto-start settings # Auto-start settings
auto_start_enabled: bool = field(default_factory=lambda: os.environ.get("GHYDRA_DOCKER_AUTO", "false").lower() == "true") auto_start_enabled: bool = field(default_factory=lambda: os.environ.get("MCGHIDRA_DOCKER_AUTO", "false").lower() == "true")
auto_start_wait: bool = True auto_start_wait: bool = True
auto_start_timeout: float = 300.0 auto_start_timeout: float = 300.0
@ -49,8 +49,8 @@ def set_docker_config(config: DockerConfig) -> None:
@dataclass @dataclass
class GhydraConfig: class MCGhidraConfig:
"""Configuration for GhydraMCP server.""" """Configuration for MCGhidra server."""
# Ghidra connection settings # Ghidra connection settings
ghidra_host: str = field(default_factory=lambda: os.environ.get("GHIDRA_HOST", "localhost")) ghidra_host: str = field(default_factory=lambda: os.environ.get("GHIDRA_HOST", "localhost"))
@ -81,12 +81,12 @@ class GhydraConfig:
# Feedback collection # Feedback collection
feedback_enabled: bool = field( feedback_enabled: bool = field(
default_factory=lambda: os.environ.get("GHYDRA_FEEDBACK", "true").lower() == "true" default_factory=lambda: os.environ.get("MCGHIDRA_FEEDBACK", "true").lower() == "true"
) )
feedback_db_path: str = field( feedback_db_path: str = field(
default_factory=lambda: os.environ.get( default_factory=lambda: os.environ.get(
"GHYDRA_FEEDBACK_DB", "MCGHIDRA_FEEDBACK_DB",
str(Path.home() / ".ghydramcp" / "feedback.db"), str(Path.home() / ".mcghidra" / "feedback.db"),
) )
) )
@ -114,18 +114,18 @@ class GhydraConfig:
# Global configuration instance (can be replaced for testing) # Global configuration instance (can be replaced for testing)
_config: Optional[GhydraConfig] = None _config: Optional[MCGhidraConfig] = None
def get_config() -> GhydraConfig: def get_config() -> MCGhidraConfig:
"""Get the global configuration instance.""" """Get the global configuration instance."""
global _config global _config
if _config is None: if _config is None:
_config = GhydraConfig() _config = MCGhidraConfig()
return _config return _config
def set_config(config: GhydraConfig) -> None: def set_config(config: MCGhidraConfig) -> None:
"""Set the global configuration instance.""" """Set the global configuration instance."""
global _config global _config
_config = config _config = config

View File

@ -1,4 +1,4 @@
"""Core infrastructure for GhydraMCP. """Core infrastructure for MCGhidra.
Contains HTTP client, pagination, progress reporting, and logging utilities. Contains HTTP client, pagination, progress reporting, and logging utilities.
""" """

View File

@ -1,4 +1,4 @@
"""Field projection and response size guard for GhydraMCP. """Field projection and response size guard for MCGhidra.
Provides jq-style field projection, grep filtering, and token budget Provides jq-style field projection, grep filtering, and token budget
enforcement to prevent oversized MCP tool results. enforcement to prevent oversized MCP tool results.

View File

@ -11,7 +11,7 @@ if TYPE_CHECKING:
from fastmcp import Context from fastmcp import Context
# Standard Python logger as fallback # Standard Python logger as fallback
logger = logging.getLogger("ghydramcp") logger = logging.getLogger("mcghidra")
async def log_debug(ctx: Optional["Context"], message: str) -> None: async def log_debug(ctx: Optional["Context"], message: str) -> None:
@ -75,7 +75,7 @@ async def log_error(ctx: Optional["Context"], message: str) -> None:
def configure_logging(level: int = logging.INFO) -> None: def configure_logging(level: int = logging.INFO) -> None:
"""Configure the standard logger for GhydraMCP. """Configure the standard logger for MCGhidra.
Args: Args:
level: Logging level (default: INFO) level: Logging level (default: INFO)

View File

@ -1,11 +1,11 @@
"""MCP Mixins for GhydraMCP. """MCP Mixins for MCGhidra.
Domain-specific mixins that organize tools, resources, and prompts by functionality. Domain-specific mixins that organize tools, resources, and prompts by functionality.
Uses FastMCP's contrib.mcp_mixin pattern for clean modular organization. Uses FastMCP's contrib.mcp_mixin pattern for clean modular organization.
""" """
from .analysis import AnalysisMixin from .analysis import AnalysisMixin
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
from .bookmarks import BookmarksMixin from .bookmarks import BookmarksMixin
from .cursors import CursorsMixin from .cursors import CursorsMixin
from .data import DataMixin from .data import DataMixin
@ -22,7 +22,7 @@ from .variables import VariablesMixin
from .xrefs import XrefsMixin from .xrefs import XrefsMixin
__all__ = [ __all__ = [
"GhydraMixinBase", "MCGhidraMixinBase",
"InstancesMixin", "InstancesMixin",
"FunctionsMixin", "FunctionsMixin",
"DataMixin", "DataMixin",

View File

@ -1,4 +1,4 @@
"""Analysis mixin for GhydraMCP. """Analysis mixin for MCGhidra.
Provides tools for program analysis operations. Provides tools for program analysis operations.
""" """
@ -10,10 +10,10 @@ from fastmcp.contrib.mcp_mixin import mcp_tool
from ..config import get_config from ..config import get_config
from ..core.logging import logger from ..core.logging import logger
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class AnalysisMixin(GhydraMixinBase): class AnalysisMixin(MCGhidraMixinBase):
"""Mixin for analysis operations. """Mixin for analysis operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Base mixin class for GhydraMCP domain mixins. """Base mixin class for MCGhidra domain mixins.
Provides shared state and utilities for all domain mixins. Provides shared state and utilities for all domain mixins.
""" """
@ -23,8 +23,8 @@ from ..core.logging import log_debug, log_error, log_info, log_warning
from ..core.pagination import paginate_response from ..core.pagination import paginate_response
class GhydraMixinBase(MCPMixin): class MCGhidraMixinBase(MCPMixin):
"""Base class for GhydraMCP domain mixins. """Base class for MCGhidra domain mixins.
Provides shared instance state and common utilities. Provides shared instance state and common utilities.
All domain mixins should inherit from this class. All domain mixins should inherit from this class.

View File

@ -1,4 +1,4 @@
"""Bookmarks mixin for GhydraMCP. """Bookmarks mixin for MCGhidra.
Provides tools for managing Ghidra bookmarks (annotations at addresses). Provides tools for managing Ghidra bookmarks (annotations at addresses).
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_tool from fastmcp.contrib.mcp_mixin import mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class BookmarksMixin(GhydraMixinBase): class BookmarksMixin(MCGhidraMixinBase):
"""Mixin for bookmark operations. """Mixin for bookmark operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Cursor management mixin for GhydraMCP. """Cursor management mixin for MCGhidra.
Provides tools for managing pagination cursors. Provides tools for managing pagination cursors.
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_tool from fastmcp.contrib.mcp_mixin import mcp_tool
from ..core.pagination import get_cursor_manager from ..core.pagination import get_cursor_manager
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class CursorsMixin(GhydraMixinBase): class CursorsMixin(MCGhidraMixinBase):
"""Mixin for cursor management. """Mixin for cursor management.
Provides tools for navigating paginated results. Provides tools for navigating paginated results.

View File

@ -1,4 +1,4 @@
"""Data mixin for GhydraMCP. """Data mixin for MCGhidra.
Provides tools for data items and strings operations. Provides tools for data items and strings operations.
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class DataMixin(GhydraMixinBase): class DataMixin(MCGhidraMixinBase):
"""Mixin for data operations. """Mixin for data operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Data types mixin for GhydraMCP. """Data types mixin for MCGhidra.
Provides tools for managing enum and typedef data types. Provides tools for managing enum and typedef data types.
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_tool from fastmcp.contrib.mcp_mixin import mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class DataTypesMixin(GhydraMixinBase): class DataTypesMixin(MCGhidraMixinBase):
"""Mixin for enum and typedef data type operations. """Mixin for enum and typedef data type operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Docker management mixin for GhydraMCP. """Docker management mixin for MCGhidra.
Provides tools for managing Ghidra Docker containers programmatically. Provides tools for managing Ghidra Docker containers programmatically.
Allows the MCP server to automatically start containers when Ghidra isn't available. Allows the MCP server to automatically start containers when Ghidra isn't available.
@ -21,17 +21,17 @@ from typing import Any, Dict, List, Optional
from fastmcp import Context from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_tool from fastmcp.contrib.mcp_mixin import mcp_tool
from ghydramcp.core.logging import logger from mcghidra.core.logging import logger
from ghydramcp.mixins.base import GhydraMixinBase from mcghidra.mixins.base import MCGhidraMixinBase
# Port pool configuration (32 ports should handle many concurrent sessions) # Port pool configuration (32 ports should handle many concurrent sessions)
PORT_POOL_START = 8192 PORT_POOL_START = 8192
PORT_POOL_END = 8223 PORT_POOL_END = 8223
PORT_LOCK_DIR = Path("/tmp/ghydramcp-ports") PORT_LOCK_DIR = Path("/tmp/mcghidra-ports")
class PortPool: class PortPool:
"""Manages a pool of ports for GhydraMCP containers. """Manages a pool of ports for MCGhidra containers.
Uses file-based locking to coordinate port allocation across multiple Uses file-based locking to coordinate port allocation across multiple
processes. Each allocated port gets a lock file that persists until processes. Each allocated port gets a lock file that persists until
@ -209,11 +209,11 @@ class PortPool:
return cleaned return cleaned
class DockerMixin(GhydraMixinBase): class DockerMixin(MCGhidraMixinBase):
"""Docker container management for GhydraMCP. """Docker container management for MCGhidra.
Provides tools to start, stop, and manage Ghidra containers Provides tools to start, stop, and manage Ghidra containers
with the GhydraMCP plugin pre-installed. with the MCGhidra plugin pre-installed.
Supports multi-process environments with: Supports multi-process environments with:
- Dynamic port allocation from a pool (8192-8223) - Dynamic port allocation from a pool (8192-8223)
@ -231,8 +231,8 @@ class DockerMixin(GhydraMixinBase):
# Track containers started by this session # Track containers started by this session
_session_containers: Dict[str, Dict[str, Any]] = {} _session_containers: Dict[str, Dict[str, Any]] = {}
# Label prefix for GhydraMCP containers # Label prefix for MCGhidra containers
LABEL_PREFIX = "com.ghydramcp" LABEL_PREFIX = "com.mcghidra"
def __init__(self): def __init__(self):
"""Initialize Docker mixin with session isolation.""" """Initialize Docker mixin with session isolation."""
@ -252,7 +252,7 @@ class DockerMixin(GhydraMixinBase):
def port_pool(self) -> PortPool: def port_pool(self) -> PortPool:
"""Get the port pool, creating it on first access. """Get the port pool, creating it on first access.
Lazy initialization avoids creating /tmp/ghydramcp-ports Lazy initialization avoids creating /tmp/mcghidra-ports
until Docker tools are actually used. until Docker tools are actually used.
""" """
if self._port_pool is None: if self._port_pool is None:
@ -331,7 +331,7 @@ class DockerMixin(GhydraMixinBase):
env = os.environ.copy() env = os.environ.copy()
if project_dir: if project_dir:
env["COMPOSE_PROJECT_NAME"] = "ghydramcp" env["COMPOSE_PROJECT_NAME"] = "mcghidra"
return subprocess.run( return subprocess.run(
cmd, cmd,
@ -345,7 +345,7 @@ class DockerMixin(GhydraMixinBase):
def _generate_container_name(self, binary_name: str) -> str: def _generate_container_name(self, binary_name: str) -> str:
"""Generate a unique container name for this session. """Generate a unique container name for this session.
Format: ghydramcp-{session_id}-{binary_stem} Format: mcghidra-{session_id}-{binary_stem}
Args: Args:
binary_name: Name of the binary being analyzed binary_name: Name of the binary being analyzed
@ -356,7 +356,7 @@ class DockerMixin(GhydraMixinBase):
# Clean binary name for container naming # Clean binary name for container naming
stem = Path(binary_name).stem.lower() stem = Path(binary_name).stem.lower()
clean_name = "".join(c if c.isalnum() else "-" for c in stem)[:20] clean_name = "".join(c if c.isalnum() else "-" for c in stem)[:20]
return f"ghydramcp-{self.session_id}-{clean_name}" return f"mcghidra-{self.session_id}-{clean_name}"
def _get_container_labels(self, binary_path: str, port: int) -> Dict[str, str]: def _get_container_labels(self, binary_path: str, port: int) -> Dict[str, str]:
"""Generate Docker labels for a container. """Generate Docker labels for a container.
@ -383,7 +383,7 @@ class DockerMixin(GhydraMixinBase):
label_filter: Optional[str] = None, label_filter: Optional[str] = None,
session_only: bool = False, session_only: bool = False,
) -> List[Dict[str, Any]]: ) -> List[Dict[str, Any]]:
"""Find GhydraMCP containers by label. """Find MCGhidra containers by label.
Args: Args:
label_filter: Additional label filter (e.g., "port=8192") label_filter: Additional label filter (e.g., "port=8192")
@ -431,19 +431,19 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool( @mcp_tool(
name="docker_status", name="docker_status",
description="Check Docker availability and running GhydraMCP containers", description="Check Docker availability and running MCGhidra containers",
) )
async def docker_status(self, ctx: Optional[Context] = None) -> Dict[str, Any]: async def docker_status(self, ctx: Optional[Context] = None) -> Dict[str, Any]:
"""Check Docker status and list running GhydraMCP containers. """Check Docker status and list running MCGhidra containers.
Returns: Returns:
Status information including: Status information including:
- docker_available: Whether Docker is installed - docker_available: Whether Docker is installed
- docker_running: Whether Docker daemon is running - docker_running: Whether Docker daemon is running
- session_id: This MCP instance's session ID - session_id: This MCP instance's session ID
- containers: List of GhydraMCP containers with their status - containers: List of MCGhidra containers with their status
- port_pool: Port allocation status - port_pool: Port allocation status
- images: Available GhydraMCP images - images: Available MCGhidra images
""" """
result = { result = {
"docker_available": False, "docker_available": False,
@ -479,7 +479,7 @@ class DockerMixin(GhydraMixinBase):
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
pass pass
# List all GhydraMCP containers (from any session) # List all MCGhidra containers (from any session)
result["containers"] = await self._find_containers_by_label() result["containers"] = await self._find_containers_by_label()
# List containers from this session only # List containers from this session only
@ -495,7 +495,7 @@ class DockerMixin(GhydraMixinBase):
"ps", "ps",
"-a", "-a",
"--filter", "--filter",
"name=ghydramcp", "name=mcghidra",
"--format", "--format",
"{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}", "{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}",
] ]
@ -517,13 +517,13 @@ class DockerMixin(GhydraMixinBase):
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
pass pass
# List GhydraMCP images # List MCGhidra images
try: try:
images_result = await self._run_docker_cmd( images_result = await self._run_docker_cmd(
[ [
"images", "images",
"--filter", "--filter",
"reference=ghydramcp*", "reference=mcghidra*",
"--format", "--format",
"{{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}", "{{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}",
] ]
@ -546,7 +546,7 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool( @mcp_tool(
name="docker_start", name="docker_start",
description="Start a GhydraMCP Docker container to analyze a binary (auto-assigns port from pool)", description="Start a MCGhidra Docker container to analyze a binary (auto-assigns port from pool)",
) )
async def docker_start( async def docker_start(
self, self,
@ -555,9 +555,9 @@ class DockerMixin(GhydraMixinBase):
name: Optional[str] = None, name: Optional[str] = None,
ctx: Optional[Context] = None, ctx: Optional[Context] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Start a GhydraMCP Docker container for binary analysis. """Start a MCGhidra Docker container for binary analysis.
This creates a new Ghidra instance in Docker with the GhydraMCP This creates a new Ghidra instance in Docker with the MCGhidra
plugin pre-installed. The binary will be imported and analyzed, plugin pre-installed. The binary will be imported and analyzed,
then the HTTP API will be available. then the HTTP API will be available.
@ -635,9 +635,9 @@ class DockerMixin(GhydraMixinBase):
"-v", "-v",
f"{binary_file.parent}:/binaries:ro", f"{binary_file.parent}:/binaries:ro",
"-e", "-e",
f"GHYDRA_MAXMEM={memory}", f"MCGHIDRA_MAXMEM={memory}",
*label_args, *label_args,
"ghydramcp:latest", "mcghidra:latest",
f"/binaries/{binary_file.name}", f"/binaries/{binary_file.name}",
] ]
) )
@ -673,12 +673,12 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool( @mcp_tool(
name="docker_stop", name="docker_stop",
description="Stop a running GhydraMCP Docker container", description="Stop a running MCGhidra Docker container",
) )
async def docker_stop( async def docker_stop(
self, name_or_id: str, remove: bool = True, ctx: Optional[Context] = None self, name_or_id: str, remove: bool = True, ctx: Optional[Context] = None
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Stop a GhydraMCP Docker container. """Stop a MCGhidra Docker container.
For safety, this will only stop containers that belong to the current For safety, this will only stop containers that belong to the current
MCP session. Attempting to stop another session's container will fail MCP session. Attempting to stop another session's container will fail
@ -752,7 +752,7 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool( @mcp_tool(
name="docker_logs", name="docker_logs",
description="Get logs from a GhydraMCP Docker container", description="Get logs from a MCGhidra Docker container",
) )
async def docker_logs( async def docker_logs(
self, self,
@ -761,7 +761,7 @@ class DockerMixin(GhydraMixinBase):
follow: bool = False, follow: bool = False,
ctx: Optional[Context] = None, ctx: Optional[Context] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Get logs from a GhydraMCP container. """Get logs from a MCGhidra container.
Args: Args:
name_or_id: Container name or ID name_or_id: Container name or ID
@ -792,7 +792,7 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool( @mcp_tool(
name="docker_build", name="docker_build",
description="Build the GhydraMCP Docker image from source", description="Build the MCGhidra Docker image from source",
) )
async def docker_build( async def docker_build(
self, self,
@ -801,12 +801,12 @@ class DockerMixin(GhydraMixinBase):
project_dir: Optional[str] = None, project_dir: Optional[str] = None,
ctx: Optional[Context] = None, ctx: Optional[Context] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Build the GhydraMCP Docker image. """Build the MCGhidra Docker image.
Args: Args:
tag: Image tag (default: 'latest') tag: Image tag (default: 'latest')
no_cache: Build without using cache no_cache: Build without using cache
project_dir: Path to GhydraMCP project (auto-detected if not specified) project_dir: Path to MCGhidra project (auto-detected if not specified)
Returns: Returns:
Build status Build status
@ -824,7 +824,7 @@ class DockerMixin(GhydraMixinBase):
proj_path = module_dir proj_path = module_dir
else: else:
return { return {
"error": "Could not find GhydraMCP project directory. Please specify project_dir." "error": "Could not find MCGhidra project directory. Please specify project_dir."
} }
dockerfile = proj_path / "docker" / "Dockerfile" dockerfile = proj_path / "docker" / "Dockerfile"
@ -835,7 +835,7 @@ class DockerMixin(GhydraMixinBase):
args = [ args = [
"build", "build",
"-t", "-t",
f"ghydramcp:{tag}", f"mcghidra:{tag}",
"-f", "-f",
str(dockerfile), str(dockerfile),
] ]
@ -848,8 +848,8 @@ class DockerMixin(GhydraMixinBase):
return { return {
"success": True, "success": True,
"image": f"ghydramcp:{tag}", "image": f"mcghidra:{tag}",
"message": f"Successfully built ghydramcp:{tag}", "message": f"Successfully built mcghidra:{tag}",
"output": result.stdout[-2000:] if len(result.stdout) > 2000 else result.stdout, "output": result.stdout[-2000:] if len(result.stdout) > 2000 else result.stdout,
} }
@ -899,12 +899,12 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool( @mcp_tool(
name="docker_health", name="docker_health",
description="Check if a GhydraMCP container's API is responding", description="Check if a MCGhidra container's API is responding",
) )
async def docker_health( async def docker_health(
self, port: Optional[int] = None, timeout: float = 5.0, ctx: Optional[Context] = None self, port: Optional[int] = None, timeout: float = 5.0, ctx: Optional[Context] = None
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Check if a GhydraMCP container's API is healthy. """Check if a MCGhidra container's API is healthy.
Args: Args:
port: API port to check (uses current instance if not specified) port: API port to check (uses current instance if not specified)
@ -920,7 +920,7 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool( @mcp_tool(
name="docker_auto_start", name="docker_auto_start",
description="Automatically start a GhydraMCP container with dynamic port allocation", description="Automatically start a MCGhidra container with dynamic port allocation",
) )
async def docker_auto_start( async def docker_auto_start(
self, self,
@ -978,10 +978,10 @@ class DockerMixin(GhydraMixinBase):
} }
# Check if we have the image # Check if we have the image
if not any("ghydramcp" in img.get("name", "") for img in status.get("images", [])): if not any("mcghidra" in img.get("name", "") for img in status.get("images", [])):
return { return {
"error": ( "error": (
"GhydraMCP Docker image not found. " "MCGhidra Docker image not found. "
"Build it with docker_build() or 'make build' first." "Build it with docker_build() or 'make build' first."
) )
} }
@ -1017,14 +1017,14 @@ class DockerMixin(GhydraMixinBase):
dry_run: bool = False, dry_run: bool = False,
ctx: Optional[Context] = None, ctx: Optional[Context] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Clean up orphaned GhydraMCP containers and stale port locks. """Clean up orphaned MCGhidra containers and stale port locks.
This helps recover from crashed processes that left containers or This helps recover from crashed processes that left containers or
port locks behind. port locks behind.
By default, only cleans containers from the current session to prevent By default, only cleans containers from the current session to prevent
accidentally removing another agent's work. Set session_only=False accidentally removing another agent's work. Set session_only=False
(with caution) to clean all GhydraMCP containers. (with caution) to clean all MCGhidra containers.
Args: Args:
session_only: Only clean up containers from this session (default: True for safety) session_only: Only clean up containers from this session (default: True for safety)

View File

@ -1,4 +1,4 @@
"""Functions mixin for GhydraMCP. """Functions mixin for MCGhidra.
Provides tools for function analysis, decompilation, and manipulation. Provides tools for function analysis, decompilation, and manipulation.
""" """
@ -10,10 +10,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class FunctionsMixin(GhydraMixinBase): class FunctionsMixin(MCGhidraMixinBase):
"""Mixin for function operations. """Mixin for function operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Instance management mixin for GhydraMCP. """Instance management mixin for MCGhidra.
Provides tools for discovering, registering, and managing Ghidra instances. Provides tools for discovering, registering, and managing Ghidra instances.
""" """
@ -9,10 +9,10 @@ from typing import Any, Dict, Optional
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class InstancesMixin(GhydraMixinBase): class InstancesMixin(MCGhidraMixinBase):
"""Mixin for Ghidra instance management. """Mixin for Ghidra instance management.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Memory mixin for GhydraMCP. """Memory mixin for MCGhidra.
Provides tools for memory read/write operations. Provides tools for memory read/write operations.
""" """
@ -7,10 +7,10 @@ from typing import Any, Dict, Optional
from fastmcp.contrib.mcp_mixin import mcp_tool from fastmcp.contrib.mcp_mixin import mcp_tool
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class MemoryMixin(GhydraMixinBase): class MemoryMixin(MCGhidraMixinBase):
"""Mixin for memory operations. """Mixin for memory operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Namespaces mixin for GhydraMCP. """Namespaces mixin for MCGhidra.
Provides tools for querying namespaces and class definitions. Provides tools for querying namespaces and class definitions.
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class NamespacesMixin(GhydraMixinBase): class NamespacesMixin(MCGhidraMixinBase):
"""Mixin for namespace and class operations. """Mixin for namespace and class operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Segments mixin for GhydraMCP. """Segments mixin for MCGhidra.
Provides tools for querying memory segments (sections) and their permissions. Provides tools for querying memory segments (sections) and their permissions.
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class SegmentsMixin(GhydraMixinBase): class SegmentsMixin(MCGhidraMixinBase):
"""Mixin for memory segment operations. """Mixin for memory segment operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Structs mixin for GhydraMCP. """Structs mixin for MCGhidra.
Provides tools for struct data type operations. Provides tools for struct data type operations.
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class StructsMixin(GhydraMixinBase): class StructsMixin(MCGhidraMixinBase):
"""Mixin for struct operations. """Mixin for struct operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Symbols mixin for GhydraMCP. """Symbols mixin for MCGhidra.
Provides tools for symbol table operations including labels, imports, and exports. Provides tools for symbol table operations including labels, imports, and exports.
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class SymbolsMixin(GhydraMixinBase): class SymbolsMixin(MCGhidraMixinBase):
"""Mixin for symbol table operations. """Mixin for symbol table operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Variables mixin for GhydraMCP. """Variables mixin for MCGhidra.
Provides tools for querying global and function-local variables. Provides tools for querying global and function-local variables.
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class VariablesMixin(GhydraMixinBase): class VariablesMixin(MCGhidraMixinBase):
"""Mixin for variable operations. """Mixin for variable operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""Cross-references mixin for GhydraMCP. """Cross-references mixin for MCGhidra.
Provides tools for cross-reference (xref) operations. Provides tools for cross-reference (xref) operations.
""" """
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
from ..config import get_config from ..config import get_config
from .base import GhydraMixinBase from .base import MCGhidraMixinBase
class XrefsMixin(GhydraMixinBase): class XrefsMixin(MCGhidraMixinBase):
"""Mixin for cross-reference operations. """Mixin for cross-reference operations.
Provides tools for: Provides tools for:

View File

@ -1,4 +1,4 @@
"""GhydraMCP Server - FastMCP server composing all mixins. """MCGhidra Server - FastMCP server composing all mixins.
This module creates and configures the FastMCP server by composing This module creates and configures the FastMCP server by composing
all domain-specific mixins into a single MCP server. all domain-specific mixins into a single MCP server.
@ -13,7 +13,7 @@ from typing import Optional
from fastmcp import FastMCP from fastmcp import FastMCP
from .config import GhydraConfig, get_config, set_config from .config import MCGhidraConfig, get_config, set_config
from .core.logging import configure_logging from .core.logging import configure_logging
from .mixins import ( from .mixins import (
AnalysisMixin, AnalysisMixin,
@ -35,10 +35,10 @@ from .mixins import (
def create_server( def create_server(
name: str = "GhydraMCP", name: str = "MCGhidra",
config: Optional[GhydraConfig] = None, config: Optional[MCGhidraConfig] = None,
) -> FastMCP: ) -> FastMCP:
"""Create and configure the GhydraMCP server. """Create and configure the MCGhidra server.
Args: Args:
name: Server name name: Server name
@ -114,7 +114,7 @@ def _periodic_discovery(interval: int = 30):
""" """
import requests as _requests import requests as _requests
from .mixins.base import GhydraMixinBase from .mixins.base import MCGhidraMixinBase
config = get_config() config = get_config()
@ -133,9 +133,9 @@ def _periodic_discovery(interval: int = 30):
if resp.ok: if resp.ok:
response = resp.json() response = resp.json()
if response.get("success", False): if response.get("success", False):
with GhydraMixinBase._instances_lock: with MCGhidraMixinBase._instances_lock:
if port not in GhydraMixinBase._instances: if port not in MCGhidraMixinBase._instances:
GhydraMixinBase._instances[port] = { MCGhidraMixinBase._instances[port] = {
"url": url.rstrip("/"), "url": url.rstrip("/"),
"project": response.get("project", ""), "project": response.get("project", ""),
"file": response.get("file", ""), "file": response.get("file", ""),
@ -149,27 +149,27 @@ def _periodic_discovery(interval: int = 30):
def _handle_sigint(signum, frame): def _handle_sigint(signum, frame):
"""Handle SIGINT gracefully.""" """Handle SIGINT gracefully."""
print("\nShutting down GhydraMCP...", file=sys.stderr) print("\nShutting down MCGhidra...", file=sys.stderr)
sys.exit(0) sys.exit(0)
def main(): def main():
"""Main entry point for the GhydraMCP server.""" """Main entry point for the MCGhidra server."""
import logging import logging
import os import os
import shutil import shutil
# Configure logging early (DEBUG if GHYDRAMCP_DEBUG is set) # Configure logging early (DEBUG if MCGHIDRAMCP_DEBUG is set)
log_level = logging.DEBUG if os.environ.get("GHYDRAMCP_DEBUG") else logging.INFO log_level = logging.DEBUG if os.environ.get("MCGHIDRAMCP_DEBUG") else logging.INFO
configure_logging(log_level) configure_logging(log_level)
try: try:
from importlib.metadata import version from importlib.metadata import version
package_version = version("ghydramcp") package_version = version("mcghidra")
except Exception: except Exception:
package_version = "2025.12.1" package_version = "2025.12.1"
print(f"🔬 GhydraMCP v{package_version}", file=sys.stderr) print(f"🔬 MCGhidra v{package_version}", file=sys.stderr)
print(" AI-assisted reverse engineering bridge for Ghidra", file=sys.stderr) print(" AI-assisted reverse engineering bridge for Ghidra", file=sys.stderr)
# Check Docker availability # Check Docker availability
@ -191,15 +191,15 @@ def main():
print(f" Discovering Ghidra instances on {config.ghidra_host}...", file=sys.stderr) print(f" Discovering Ghidra instances on {config.ghidra_host}...", file=sys.stderr)
from .core.http_client import safe_get from .core.http_client import safe_get
from .mixins.base import GhydraMixinBase from .mixins.base import MCGhidraMixinBase
found = 0 found = 0
for port in config.quick_discovery_range: for port in config.quick_discovery_range:
try: try:
response = safe_get(port, "") response = safe_get(port, "")
if response.get("success", False): if response.get("success", False):
with GhydraMixinBase._instances_lock: with MCGhidraMixinBase._instances_lock:
GhydraMixinBase._instances[port] = { MCGhidraMixinBase._instances[port] = {
"url": f"http://{config.ghidra_host}:{port}", "url": f"http://{config.ghidra_host}:{port}",
"project": response.get("project", ""), "project": response.get("project", ""),
"file": response.get("file", ""), "file": response.get("file", ""),
@ -219,7 +219,7 @@ def main():
discovery_thread = threading.Thread( discovery_thread = threading.Thread(
target=_periodic_discovery, target=_periodic_discovery,
daemon=True, daemon=True,
name="GhydraMCP-Discovery", name="MCGhidra-Discovery",
) )
discovery_thread.start() discovery_thread.start()

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Test script for the comment functionality in GhydraMCP. Test script for the comment functionality in MCGhidra.
Tests both HTTP API and MCP bridge interfaces for setting and retrieving Tests both HTTP API and MCP bridge interfaces for setting and retrieving
different types of comments in Ghidra, including plate, pre, post, EOL, different types of comments in Ghidra, including plate, pre, post, EOL,

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Comprehensive test script for data operations in GhydraMCP. Comprehensive test script for data operations in MCGhidra.
This script tests all data-related operations including: This script tests all data-related operations including:
1. Creating data items with different types 1. Creating data items with different types

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Test script for the GhydraMCP HTTP API. Test script for the MCGhidra HTTP API.
This script tests the HTTP endpoints of the Java plugin. This script tests the HTTP endpoints of the Java plugin.
""" """
import json import json
@ -14,9 +14,9 @@ import sys
DEFAULT_PORT = 8192 DEFAULT_PORT = 8192
# Get host from environment variable or default to localhost # Get host from environment variable or default to localhost
GHYDRAMCP_TEST_HOST = os.getenv('GHYDRAMCP_TEST_HOST') MCGHIDRA_TEST_HOST = os.getenv('MCGHIDRA_TEST_HOST')
if GHYDRAMCP_TEST_HOST and GHYDRAMCP_TEST_HOST.strip(): if MCGHIDRA_TEST_HOST and MCGHIDRA_TEST_HOST.strip():
BASE_URL = f"http://{GHYDRAMCP_TEST_HOST}:{DEFAULT_PORT}" BASE_URL = f"http://{MCGHIDRA_TEST_HOST}:{DEFAULT_PORT}"
else: else:
BASE_URL = f"http://localhost:{DEFAULT_PORT}" BASE_URL = f"http://localhost:{DEFAULT_PORT}"
@ -48,8 +48,8 @@ Endpoints requiring HATEOAS updates:
This test suite enforces strict HATEOAS compliance with no backward compatibility. This test suite enforces strict HATEOAS compliance with no backward compatibility.
""" """
class GhydraMCPHttpApiTests(unittest.TestCase): class MCGhidraHttpApiTests(unittest.TestCase):
"""Test cases for the GhydraMCP HTTP API""" """Test cases for the MCGhidra HTTP API"""
def assertStandardSuccessResponse(self, data): def assertStandardSuccessResponse(self, data):
"""Helper to assert the standard success response structure for HATEOAS API.""" """Helper to assert the standard success response structure for HATEOAS API."""

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Test script for the GhydraMCP bridge using the MCP client. Test script for the MCGhidra bridge using the MCP client.
This script tests the bridge by sending MCP requests and handling responses. This script tests the bridge by sending MCP requests and handling responses.
""" """
import json import json
@ -14,8 +14,8 @@ from mcp.client.session import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client from mcp.client.stdio import StdioServerParameters, stdio_client
# Get host and port from environment variables or use defaults # Get host and port from environment variables or use defaults
GHYDRAMCP_TEST_HOST = os.getenv('GHYDRAMCP_TEST_HOST', 'localhost') MCGHIDRA_TEST_HOST = os.getenv('MCGHIDRA_TEST_HOST', 'localhost')
GHYDRAMCP_TEST_PORT = int(os.getenv('GHYDRAMCP_TEST_PORT', '8192')) MCGHIDRA_TEST_PORT = int(os.getenv('MCGHIDRA_TEST_PORT', '8192'))
# Set up logging # Set up logging
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
@ -95,8 +95,8 @@ async def test_bridge():
logger.info(f"List instances result: {list_instances_result}") logger.info(f"List instances result: {list_instances_result}")
# Set the current instance to use for subsequent calls # Set the current instance to use for subsequent calls
logger.info(f"Setting current instance to port {GHYDRAMCP_TEST_PORT}...") logger.info(f"Setting current instance to port {MCGHIDRA_TEST_PORT}...")
use_instance_result = await session.call_tool("instances_use", arguments={"port": GHYDRAMCP_TEST_PORT}) use_instance_result = await session.call_tool("instances_use", arguments={"port": MCGHIDRA_TEST_PORT})
logger.info(f"Use instance result: {use_instance_result}") logger.info(f"Use instance result: {use_instance_result}")
# Call the functions_list tool (no port needed now) # Call the functions_list tool (no port needed now)

38
uv.lock generated
View File

@ -414,25 +414,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/db/ee/327a3f6c7ac5cde56c7c9449dbc6c0ab78b15c06a51ad5645ab880240120/fastmcp_feedback-2026.1.12.1-py3-none-any.whl", hash = "sha256:6a3dec71f3d3eae4eb0102eb0a86aa7853fb0419fb506a5a13d17deaf842c53c", size = 29789, upload-time = "2026-01-16T02:09:11.831Z" }, { url = "https://files.pythonhosted.org/packages/db/ee/327a3f6c7ac5cde56c7c9449dbc6c0ab78b15c06a51ad5645ab880240120/fastmcp_feedback-2026.1.12.1-py3-none-any.whl", hash = "sha256:6a3dec71f3d3eae4eb0102eb0a86aa7853fb0419fb506a5a13d17deaf842c53c", size = 29789, upload-time = "2026-01-16T02:09:11.831Z" },
] ]
[[package]]
name = "ghydramcp"
version = "2025.12.3"
source = { editable = "." }
dependencies = [
{ name = "fastmcp" },
{ name = "fastmcp-feedback" },
{ name = "mcp" },
{ name = "requests" },
]
[package.metadata]
requires-dist = [
{ name = "fastmcp", specifier = ">=2.0.0" },
{ name = "fastmcp-feedback", specifier = ">=1.0.0" },
{ name = "mcp", specifier = ">=1.22.0" },
{ name = "requests", specifier = ">=2.32.3" },
]
[[package]] [[package]]
name = "greenlet" name = "greenlet"
version = "3.3.1" version = "3.3.1"
@ -589,6 +570,25 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
] ]
[[package]]
name = "mcghidra"
version = "2025.12.3"
source = { editable = "." }
dependencies = [
{ name = "fastmcp" },
{ name = "fastmcp-feedback" },
{ name = "mcp" },
{ name = "requests" },
]
[package.metadata]
requires-dist = [
{ name = "fastmcp", specifier = ">=2.0.0" },
{ name = "fastmcp-feedback", specifier = ">=1.0.0" },
{ name = "mcp", specifier = ">=1.22.0" },
{ name = "requests", specifier = ">=2.32.3" },
]
[[package]] [[package]]
name = "mcp" name = "mcp"
version = "1.23.1" version = "1.23.1"