refactor: Rename project from ghydramcp to mcghidra
Some checks are pending
Build Ghidra Plugin / build (push) Waiting to run
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:
parent
d1750cb339
commit
1143489924
@ -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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
22
CHANGELOG.md
22
CHANGELOG.md
@ -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
|
||||||
@ -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!
|
||||||
@ -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",
|
||||||
|
|||||||
36
Makefile
36
Makefile
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
34
README.md
34
README.md
@ -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
|
||||||
|
|||||||
14
TESTING.md
14
TESTING.md
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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()
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"]
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
20
pom.xml
@ -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>
|
||||||
|
|||||||
@ -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"]
|
||||||
|
|||||||
10
run_tests.py
10
run_tests.py
@ -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()
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
"""GhydraMCP package entry point.
|
|
||||||
|
|
||||||
Allows running with: python -m ghydramcp
|
|
||||||
"""
|
|
||||||
|
|
||||||
from .server import main
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@ -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();
|
||||||
@ -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);
|
||||||
|
|||||||
@ -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
9
src/mcghidra/__main__.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
"""MCGhidra package entry point.
|
||||||
|
|
||||||
|
Allows running with: python -m mcghidra
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .server import main
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -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
|
||||||
@ -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.
|
||||||
"""
|
"""
|
||||||
@ -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.
|
||||||
@ -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)
|
||||||
@ -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",
|
||||||
@ -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:
|
||||||
@ -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.
|
||||||
@ -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:
|
||||||
@ -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.
|
||||||
@ -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:
|
||||||
@ -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:
|
||||||
@ -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)
|
||||||
@ -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:
|
||||||
@ -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:
|
||||||
@ -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:
|
||||||
@ -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:
|
||||||
@ -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:
|
||||||
@ -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:
|
||||||
@ -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:
|
||||||
@ -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:
|
||||||
@ -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:
|
||||||
@ -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()
|
||||||
|
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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."""
|
||||||
|
|||||||
@ -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
38
uv.lock
generated
@ -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"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user