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

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

View File

@ -2,11 +2,11 @@
## Summary
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
- GhydraMCP Docker image: `ghydramcp:latest`
- MCGhidra Docker image: `mcghidra:latest`
- Ghidra Version: 11.4.2
- 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:
```bash
docker build -t ghydramcp:latest -f docker/Dockerfile .
docker build -t mcghidra:latest -f docker/Dockerfile .
```
2. Run with a binary:
```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:
@ -37,9 +37,9 @@ Analysis completes but the script fails to load:
```
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:
/opt/ghidra/scripts/GhydraMCPServer.java (HeadlessAnalyzer)
/opt/ghidra/scripts/MCGhidraServer.java (HeadlessAnalyzer)
```
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
`GhydraMCPServer.java` (lines 22-24) imports Gson:
`MCGhidraServer.java` (lines 22-24) imports Gson:
```java
import com.google.gson.Gson;
@ -61,14 +61,14 @@ import com.google.gson.JsonParser;
However:
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
## Verification
```bash
# 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
# 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)
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
- This ensures the script works without any external dependencies
### 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
@ -106,7 +106,7 @@ Compile `GhydraMCPServer.java` with Gson into a JAR and place it in the extensio
## Additional Context
The main GhydraMCP plugin works fine in GUI mode because the extension's lib dependencies are loaded. This only affects the headless Docker workflow where scripts are loaded separately from the extension.
The main MCGhidra plugin works fine in GUI mode because the extension's lib dependencies are loaded. This only affects the headless Docker workflow where scripts are loaded separately from the extension.
---

View File

@ -75,14 +75,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- **Silent Exception Handling:** Added debug logging to PortPool exception handlers and analysis fallback paths.
- **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.
- **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.
- **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.
- **Import Path:** Fixed `logging.py` to import `Context` from `fastmcp` (not deprecated `mcp.server.fastmcp` path).
### 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
@ -239,7 +239,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [1.1] - 2025-03-30
### Added
- Initial release of GhydraMCP bridge
- Initial release of MCGhidra bridge
- Basic Ghidra instance management tools
- Function analysis 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
- Basic MCP bridge functionality
[unreleased]: https://github.com/teal-bauer/GhydraMCP/compare/v2025.12.1...HEAD
[2025.12.1]: https://github.com/teal-bauer/GhydraMCP/compare/v2.0.0...v2025.12.1
[2.0.0]: https://github.com/teal-bauer/GhydraMCP/compare/v1.4.0...v2.0.0
[1.4.0]: https://github.com/teal-bauer/GhydraMCP/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/teal-bauer/GhydraMCP/compare/v1.2...v1.3.0
[1.2]: https://github.com/teal-bauer/GhydraMCP/compare/v1.1...v1.2
[1.1]: https://github.com/teal-bauer/GhydraMCP/compare/1.0...v1.1
[1.0]: https://github.com/teal-bauer/GhydraMCP/releases/tag/1.0
[unreleased]: https://github.com/teal-bauer/MCGhidra/compare/v2025.12.1...HEAD
[2025.12.1]: https://github.com/teal-bauer/MCGhidra/compare/v2.0.0...v2025.12.1
[2.0.0]: https://github.com/teal-bauer/MCGhidra/compare/v1.4.0...v2.0.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/MCGhidra/compare/v1.2...v1.3.0
[1.2]: https://github.com/teal-bauer/MCGhidra/compare/v1.1...v1.2
[1.1]: https://github.com/teal-bauer/MCGhidra/compare/1.0...v1.1
[1.0]: https://github.com/teal-bauer/MCGhidra/releases/tag/1.0

View File

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

View File

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

View File

@ -1,4 +1,4 @@
# GhydraMCP Makefile
# MCGhidra Makefile
# Convenient commands for Docker and development operations
.PHONY: help build build-dev up up-dev down down-dev logs logs-dev \
@ -6,7 +6,7 @@
# Default target
help:
@echo "GhydraMCP Docker Management"
@echo "MCGhidra Docker Management"
@echo "============================"
@echo ""
@echo "Build commands:"
@ -44,10 +44,10 @@ help:
# =============================================================================
build:
docker compose build ghydramcp
docker compose build mcghidra
build-dev:
docker compose build ghydramcp-dev
docker compose build mcghidra-dev
build-all: build build-dev
@ -56,14 +56,14 @@ build-all: build build-dev
# =============================================================================
up:
docker compose --profile prod up -d ghydramcp
@echo "GhydraMCP starting... checking health in 30 seconds"
docker compose --profile prod up -d mcghidra
@echo "MCGhidra starting... checking health in 30 seconds"
@sleep 30
@$(MAKE) health || echo "Server may still be starting up..."
up-dev:
docker compose --profile dev up -d ghydramcp-dev
@echo "GhydraMCP (dev) starting..."
docker compose --profile dev up -d mcghidra-dev
@echo "MCGhidra (dev) starting..."
down:
docker compose --profile prod down
@ -90,7 +90,7 @@ ifndef FILE
@exit 1
endif
@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-bg:
@ -99,20 +99,20 @@ ifndef FILE
@exit 1
endif
@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
# =============================================================================
shell:
docker compose --profile debug run --rm ghydramcp-shell
docker compose --profile debug run --rm mcghidra-shell
logs:
docker compose logs -f ghydramcp
docker compose logs -f mcghidra
logs-dev:
docker compose logs -f ghydramcp-dev
docker compose logs -f mcghidra-dev
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"
health:
@echo "Checking GhydraMCP API health..."
@curl -sf http://localhost:$${GHYDRA_PORT:-8192}/ | python3 -m json.tool 2>/dev/null \
@echo "Checking MCGhidra API health..."
@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)"
# =============================================================================
@ -135,7 +135,7 @@ clean:
@echo "Containers and volumes removed"
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"
prune:
@ -147,10 +147,10 @@ prune:
# =============================================================================
mcp:
uv run python -m ghydramcp
uv run python -m mcghidra
mcp-dev:
uv run python -m ghydramcp --verbose
uv run python -m mcghidra --verbose
# =============================================================================
# Development Commands

View File

@ -1,8 +1,8 @@
# GhydraMCP Quick Start Guide
# MCGhidra Quick Start Guide
## What is GhydraMCP?
## What is MCGhidra?
GhydraMCP is a complete reverse engineering platform that combines:
MCGhidra is a complete reverse engineering platform that combines:
- **Ghidra** - NSA's powerful binary analysis tool
- **Docker** - Containerized, reproducible analysis environment
- **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)
```bash
cd /home/rpm/claude/ghydramcp/GhydraMCP
cd /home/rpm/claude/mcghidra/MCGhidra
# 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
docker run -d --name my-analysis \
-p 8192:8192 \
-v $(pwd)/binaries:/binaries \
ghydramcp:latest \
mcghidra:latest \
/binaries/your-binary
# 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 \
-p 8192:8192 \
-v $(pwd)/binaries:/binaries \
ghydramcp:latest \
mcghidra:latest \
/binaries/your-firmware.elf
```
@ -53,11 +53,11 @@ docker run -d --name arm-firmware \
```bash
# The MCP server is located at:
cd /home/rpm/claude/ghydramcp/GhydraMCP
cd /home/rpm/claude/mcghidra/MCGhidra
./launch.sh
# Or with uv:
cd GhydraMCP && uv run ghydramcp
cd MCGhidra && uv run mcghidra
```
## HTTP API Overview
@ -176,7 +176,7 @@ curl "http://localhost:8192/functions/$ENTRY/decompile" | jq -r '.result'
### List Running Containers
```bash
docker ps | grep ghydramcp
docker ps | grep mcghidra
```
### View Logs
@ -201,7 +201,7 @@ docker run -d --name persistent \
-v $(pwd)/projects:/projects \
-v $(pwd)/binaries:/binaries \
-e PROJECT_NAME=MyProject \
ghydramcp:latest \
mcghidra:latest \
/binaries/my-binary
# 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 \
-p 8193:8192 \
-v $(pwd)/binaries:/binaries \
ghydramcp:latest \
mcghidra:latest \
/binaries/binary
# Access at http://localhost:8193/
@ -263,7 +263,7 @@ gcc -o binaries/test test.c
docker run -d --name test-analysis \
-p 8192:8192 \
-v $(pwd)/binaries:/binaries \
ghydramcp:latest \
mcghidra:latest \
/binaries/test
# Find hidden function
@ -284,7 +284,7 @@ python3 docker/arm_firmware_prep.py \
docker run -d --name cisco \
-p 8192:8192 \
-v $(pwd)/binaries:/binaries \
ghydramcp:latest \
mcghidra:latest \
/binaries/cisco.elf
# Explore
@ -303,15 +303,15 @@ curl http://localhost:8192/data/strings | jq '.strings[] | select(.value | test(
## Project Structure
```
GhydraMCP/
MCGhidra/
├── docker/
│ ├── Dockerfile # Main Docker image
│ ├── 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
│ ├── arm_firmware_prep.py # ELF wrapper tool ⭐
│ └── README*.md # Documentation
├── src/ghydramcp/ # MCP server implementation
├── src/mcghidra/ # MCP server implementation
│ ├── __init__.py
│ ├── server.py # FastMCP server
│ └── mixins/ # Modular functionality

View File

@ -1,4 +1,4 @@
# GhydraMCP
# MCGhidra
**AI-native reverse engineering.** Give Claude (or any MCP client) direct access to Ghidra's analysis engine.
@ -60,10 +60,10 @@ No Ghidra installation needed. Analyze binaries in isolated containers.
```bash
# 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
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:
@ -76,14 +76,14 @@ Claude will auto-start a container, wait for analysis, and begin work.
### Option 2: Native Ghidra
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`
- Restart Ghidra
- Enable in `File → Configure → Developer → GhydraMCPPlugin`
- Enable in `File → Configure → Developer → MCGhidraPlugin`
2. **Add MCP server:**
```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.
@ -94,14 +94,14 @@ Claude will auto-start a container, wait for analysis, and begin work.
```
┌──────────────┐ MCP ┌──────────────┐ HTTP ┌──────────────┐
│ Claude │◄────────────►│ GhydraMCP │◄────────────►│ Ghidra │
│ Claude │◄────────────►│ MCGhidra │◄────────────►│ Ghidra │
│ (or other │ stdio │ (Python) │ REST API │ Plugin │
│ MCP client) │ │ │ │ (Java) │
└──────────────┘ └──────────────┘ └──────────────┘
```
- **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
- **Session isolation**: Docker containers get unique ports, preventing conflicts
@ -181,9 +181,9 @@ These guide Claude through systematic analysis with progress reporting.
```json
{
"mcpServers": {
"ghydramcp": {
"mcghidra": {
"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**:
```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
# Clone
git clone https://github.com/starsong-consulting/GhydraMCP
cd GhydraMCP
git clone https://github.com/starsong-consulting/MCGhidra
cd MCGhidra
# Build Ghidra plugin
mvn clean package
# → target/GhydraMCP-[version].zip
# → target/MCGhidra-[version].zip
# Build Docker image
docker build -t ghydramcp:latest -f docker/Dockerfile .
docker build -t mcghidra:latest -f docker/Dockerfile .
# Run MCP server (for development)
uv run ghydramcp
uv run mcghidra
```
---
## 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
- **Non-blocking I/O**: All Docker/HTTP operations run in thread executors

View File

@ -1,11 +1,11 @@
# Testing GhydraMCP
# Testing MCGhidra
This document describes how to test the GhydraMCP plugin and bridge.
This document describes how to test the MCGhidra plugin and bridge.
## Prerequisites
- 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`)
## 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
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:
```bash
@ -57,7 +57,7 @@ The `test_mcp_client.py` script tests the MCP bridge functionality using the MCP
### 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:
```bash
@ -89,7 +89,7 @@ The test script will:
### 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).
### MCP Bridge Tests
@ -103,7 +103,7 @@ The test script will:
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
3. Verify the response using assertions

View File

@ -5,7 +5,7 @@
# "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
# Features: Cursor-based pagination, grep filtering, session isolation
import os
@ -699,7 +699,7 @@ def paginate_response(data: List[Any], query_params: dict,
# ================= End Cursor System =================
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).
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.
"""
mcp = FastMCP("GhydraMCP", instructions=instructions)
mcp = FastMCP("MCGhidra", instructions=instructions)
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)
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:
json_data = response.json()
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
- 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 xrefs_list to find cross-references
- Use functions_decompile for C-like representations
@ -6862,7 +6862,7 @@ def main():
discovery_thread = threading.Thread(
target=periodic_discovery,
daemon=True,
name="GhydraMCP-Discovery"
name="MCGhidra-Discovery"
)
discovery_thread.start()

View File

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

View File

@ -1,14 +1,14 @@
# GhydraMCP Docker Image
# Ghidra + GhydraMCP Plugin pre-installed for headless binary analysis
# MCGhidra Docker Image
# Ghidra + MCGhidra Plugin pre-installed for headless binary analysis
#
# Build: docker build -t ghydramcp:latest -f docker/Dockerfile .
# Run: docker run -p 8192:8192 -v /path/to/binaries:/binaries ghydramcp:latest
# Build: docker build -t mcghidra:latest -f docker/Dockerfile .
# Run: docker run -p 8192:8192 -v /path/to/binaries:/binaries mcghidra:latest
ARG GHIDRA_VERSION=11.4.2
ARG GHIDRA_DATE=20250826
# =============================================================================
# Stage 1: Build the GhydraMCP plugin
# Stage 1: Build the MCGhidra plugin
# =============================================================================
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
# Copy GhydraMCP source and build
# Copy MCGhidra source and build
WORKDIR /build
# 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
# =============================================================================
# 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
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_DATE
LABEL org.opencontainers.image.title="ghydramcp" \
org.opencontainers.image.description="Ghidra + GhydraMCP Plugin for AI-assisted reverse engineering" \
org.opencontainers.image.source="https://github.com/starsong-consulting/GhydraMCP" \
LABEL org.opencontainers.image.title="mcghidra" \
org.opencontainers.image.description="Ghidra + MCGhidra Plugin for AI-assisted reverse engineering" \
org.opencontainers.image.source="https://github.com/starsong-consulting/MCGhidra" \
org.opencontainers.image.licenses="Apache-2.0"
# Install runtime dependencies
@ -99,21 +99,21 @@ RUN curl -fsSL "https://github.com/NationalSecurityAgency/ghidra/releases/downlo
ENV GHIDRA_HOME=/opt/ghidra
ENV PATH="${GHIDRA_HOME}:${PATH}"
# Install the GhydraMCP plugin
COPY --from=builder /build/target/GhydraMCP-*.zip /tmp/
# Install the MCGhidra plugin
COPY --from=builder /build/target/MCGhidra-*.zip /tmp/
RUN mkdir -p /opt/ghidra/Ghidra/Extensions \
&& unzip -q /tmp/GhydraMCP-*.zip -d /opt/ghidra/Ghidra/Extensions/ \
&& rm /tmp/GhydraMCP-*.zip \
&& unzip -q /tmp/MCGhidra-*.zip -d /opt/ghidra/Ghidra/Extensions/ \
&& rm /tmp/MCGhidra-*.zip \
&& chown -R ghidra:ghidra /opt/ghidra/Ghidra/Extensions/
# Create directories for projects and binaries
RUN mkdir -p /projects /binaries /home/ghidra/.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
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/
# Set proper ownership and permissions
@ -129,16 +129,16 @@ RUN chmod 755 /entrypoint.sh
USER 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
# Default environment
ENV GHYDRA_MODE=headless
ENV GHYDRA_PORT=8192
ENV GHYDRA_MAXMEM=2G
ENV MCGHIDRA_MODE=headless
ENV MCGHIDRA_PORT=8192
ENV MCGHIDRA_MAXMEM=2G
# Healthcheck
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"]

View File

@ -1,10 +1,10 @@
# GhydraMCPServer.py - Headless Ghidra script for GhydraMCP HTTP API
# MCGhidraServer.py - Headless Ghidra script for MCGhidra HTTP API
# Full API parity with the Java plugin implementation.
# 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
#@menupath
#@toolbar
@ -366,7 +366,7 @@ ROUTES = [
# HTTP Handler
# ========================================================================
class GhydraMCPHandler(HttpHandler):
class MCGhidraHandler(HttpHandler):
def __init__(self, program, decompiler):
self.program = program
@ -641,7 +641,7 @@ class GhydraMCPHandler(HttpHandler):
"success": True,
"api_version": API_VERSION,
"api_version_string": API_VERSION_STRING,
"message": "GhydraMCP Headless API",
"message": "MCGhidra Headless API",
"mode": "headless",
}
if self.program:
@ -2748,10 +2748,10 @@ class GhydraMCPHandler(HttpHandler):
def run_server(port, program, decompiler):
"""Start the HTTP server with a single catch-all handler."""
server = HttpServer.create(InetSocketAddress(port), 0)
server.createContext("/", GhydraMCPHandler(program, decompiler))
server.createContext("/", MCGhidraHandler(program, decompiler))
server.setExecutor(Executors.newCachedThreadPool())
server.start()
println("[GhydraMCP] HTTP server started on port %d" % port)
println("[MCGhidra] HTTP server started on port %d" % port)
return server
@ -2775,7 +2775,7 @@ def main():
decompiler.openProgram(currentProgram)
println("=========================================")
println(" GhydraMCP Headless HTTP Server")
println(" MCGhidra Headless HTTP Server")
println("=========================================")
println(" API Version: %s (compat: %d)" % (API_VERSION_STRING, API_VERSION))
println(" Port: %d" % port)
@ -2787,7 +2787,7 @@ def main():
server = run_server(port, currentProgram, decompiler)
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)
# Keep the script running
@ -2796,7 +2796,7 @@ def main():
time.sleep(1)
except KeyboardInterrupt:
server.stop(0)
println("[GhydraMCP] Server stopped.")
println("[MCGhidra] Server stopped.")
# Run

View File

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

View File

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

View File

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

20
pom.xml
View File

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

View File

@ -1,5 +1,5 @@
[project]
name = "ghydramcp"
name = "mcghidra"
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"
readme = "README.md"
@ -15,14 +15,14 @@ dependencies = [
]
[project.scripts]
ghydramcp = "ghydramcp:main"
mcghidra = "mcghidra:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/ghydramcp"]
packages = ["src/mcghidra"]
[tool.hatch.build]
sources = ["src"]

View File

@ -1,6 +1,6 @@
#!/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.
"""
import os
@ -21,10 +21,10 @@ def run_http_api_tests():
# Import and run the tests
try:
from test_http_api import GhydraMCPHttpApiTests
from test_http_api import MCGhidraHttpApiTests
# Create a test suite with all tests from GhydraMCPHttpApiTests
suite = unittest.TestLoader().loadTestsFromTestCase(GhydraMCPHttpApiTests)
# Create a test suite with all tests from MCGhidraHttpApiTests
suite = unittest.TestLoader().loadTestsFromTestCase(MCGhidraHttpApiTests)
# Run the tests
result = unittest.TextTestRunner(verbosity=2).run(suite)
@ -118,7 +118,7 @@ def run_comment_tests():
def run_all_tests():
"""Run all tests"""
print_header("GhydraMCP Test Suite")
print_header("MCGhidra Test Suite")
# Run test suites
http_api_success = run_http_api_tests()

View File

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

View File

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

View File

@ -39,14 +39,14 @@ import ghidra.util.Msg;
status = PluginStatus.RELEASED,
packageName = ghidra.app.DeveloperPluginPackage.NAME,
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).",
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
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 HttpServer server;
@ -54,10 +54,10 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
private boolean isBaseInstance = false;
/**
* Constructor for GhydraMCP Plugin.
* Constructor for MCGhidra Plugin.
* @param tool The Ghidra PluginTool
*/
public GhydraMCPPlugin(PluginTool tool) {
public MCGhidraPlugin(PluginTool tool) {
super(tool);
this.port = findAvailablePort();
@ -70,8 +70,8 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
}
}
Msg.info(this, "GhydraMCPPlugin loaded on port " + port);
System.out.println("[GhydraMCP] Plugin loaded on port " + port);
Msg.info(this, "MCGhidraPlugin loaded on port " + port);
System.out.println("[MCGhidra] Plugin loaded on port " + port);
try {
startServer();
@ -111,9 +111,9 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
new Thread(() -> {
server.start();
Msg.info(this, "GhydraMCP HTTP server started on port " + port);
System.out.println("[GhydraMCP] HTTP server started on port " + port);
}, "GhydraMCP-HTTP-Server").start();
Msg.info(this, "MCGhidra HTTP server started on port " + port);
System.out.println("[MCGhidra] HTTP server started on port " + port);
}, "MCGhidra-HTTP-Server").start();
}
/**
@ -350,7 +350,7 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
}
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("isBaseInstance", isBaseInstance);
@ -449,8 +449,8 @@ public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
public void dispose() {
if (server != null) {
server.stop(0); // Stop immediately
Msg.info(this, "GhydraMCP HTTP server stopped on port " + port);
System.out.println("[GhydraMCP] HTTP server stopped on port " + port);
Msg.info(this, "MCGhidra HTTP server stopped on port " + port);
System.out.println("[MCGhidra] HTTP server stopped on port " + port);
}
activeInstances.remove(port);
super.dispose();

View File

@ -4,7 +4,7 @@ package eu.starsong.ghidra.endpoints;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
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.util.Msg;
@ -13,16 +13,16 @@ package eu.starsong.ghidra.endpoints;
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
// a different design, perhaps a dedicated manager class.
// 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.
// The constructor in AbstractEndpoint handles null program.
// 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
this.activeInstances = instances;
}
@ -46,7 +46,7 @@ package eu.starsong.ghidra.endpoints;
// Accessing the static map directly - requires it to be accessible
// 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<>();
int instancePort = entry.getKey();
instance.put("port", instancePort);

View File

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

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

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

View File

@ -1,4 +1,4 @@
"""Configuration management for GhydraMCP.
"""Configuration management for MCGhidra.
Handles environment variables, default settings, and runtime configuration.
"""
@ -14,18 +14,18 @@ class DockerConfig:
"""Docker-specific configuration."""
# Docker image settings
image_name: str = "ghydramcp"
image_tag: str = field(default_factory=lambda: os.environ.get("GHYDRAMCP_VERSION", "latest"))
image_name: str = "mcghidra"
image_tag: str = field(default_factory=lambda: os.environ.get("MCGHIDRAMCP_VERSION", "latest"))
# Default container settings
default_port: int = field(default_factory=lambda: int(os.environ.get("GHYDRA_PORT", "8192")))
default_memory: str = field(default_factory=lambda: os.environ.get("GHYDRA_MAXMEM", "2G"))
default_port: int = field(default_factory=lambda: int(os.environ.get("MCGHIDRA_PORT", "8192")))
default_memory: str = field(default_factory=lambda: os.environ.get("MCGHIDRA_MAXMEM", "2G"))
# Project directory (for building)
project_dir: Optional[Path] = None
# 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_timeout: float = 300.0
@ -49,8 +49,8 @@ def set_docker_config(config: DockerConfig) -> None:
@dataclass
class GhydraConfig:
"""Configuration for GhydraMCP server."""
class MCGhidraConfig:
"""Configuration for MCGhidra server."""
# Ghidra connection settings
ghidra_host: str = field(default_factory=lambda: os.environ.get("GHIDRA_HOST", "localhost"))
@ -81,12 +81,12 @@ class GhydraConfig:
# Feedback collection
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(
default_factory=lambda: os.environ.get(
"GHYDRA_FEEDBACK_DB",
str(Path.home() / ".ghydramcp" / "feedback.db"),
"MCGHIDRA_FEEDBACK_DB",
str(Path.home() / ".mcghidra" / "feedback.db"),
)
)
@ -114,18 +114,18 @@ class GhydraConfig:
# 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."""
global _config
if _config is None:
_config = GhydraConfig()
_config = MCGhidraConfig()
return _config
def set_config(config: GhydraConfig) -> None:
def set_config(config: MCGhidraConfig) -> None:
"""Set the global configuration instance."""
global _config
_config = config

View File

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

View File

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

View File

@ -11,7 +11,7 @@ if TYPE_CHECKING:
from fastmcp import Context
# Standard Python logger as fallback
logger = logging.getLogger("ghydramcp")
logger = logging.getLogger("mcghidra")
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:
"""Configure the standard logger for GhydraMCP.
"""Configure the standard logger for MCGhidra.
Args:
level: Logging level (default: INFO)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
"""Cursor management mixin for GhydraMCP.
"""Cursor management mixin for MCGhidra.
Provides tools for managing pagination cursors.
"""
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_tool
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.
Provides tools for navigating paginated results.

View File

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

View File

@ -1,4 +1,4 @@
"""Data types mixin for GhydraMCP.
"""Data types mixin for MCGhidra.
Provides tools for managing enum and typedef data types.
"""
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_tool
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.
Provides tools for:

View File

@ -1,4 +1,4 @@
"""Docker management mixin for GhydraMCP.
"""Docker management mixin for MCGhidra.
Provides tools for managing Ghidra Docker containers programmatically.
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.contrib.mcp_mixin import mcp_tool
from ghydramcp.core.logging import logger
from ghydramcp.mixins.base import GhydraMixinBase
from mcghidra.core.logging import logger
from mcghidra.mixins.base import MCGhidraMixinBase
# Port pool configuration (32 ports should handle many concurrent sessions)
PORT_POOL_START = 8192
PORT_POOL_END = 8223
PORT_LOCK_DIR = Path("/tmp/ghydramcp-ports")
PORT_LOCK_DIR = Path("/tmp/mcghidra-ports")
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
processes. Each allocated port gets a lock file that persists until
@ -209,11 +209,11 @@ class PortPool:
return cleaned
class DockerMixin(GhydraMixinBase):
"""Docker container management for GhydraMCP.
class DockerMixin(MCGhidraMixinBase):
"""Docker container management for MCGhidra.
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:
- Dynamic port allocation from a pool (8192-8223)
@ -231,8 +231,8 @@ class DockerMixin(GhydraMixinBase):
# Track containers started by this session
_session_containers: Dict[str, Dict[str, Any]] = {}
# Label prefix for GhydraMCP containers
LABEL_PREFIX = "com.ghydramcp"
# Label prefix for MCGhidra containers
LABEL_PREFIX = "com.mcghidra"
def __init__(self):
"""Initialize Docker mixin with session isolation."""
@ -252,7 +252,7 @@ class DockerMixin(GhydraMixinBase):
def port_pool(self) -> PortPool:
"""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.
"""
if self._port_pool is None:
@ -331,7 +331,7 @@ class DockerMixin(GhydraMixinBase):
env = os.environ.copy()
if project_dir:
env["COMPOSE_PROJECT_NAME"] = "ghydramcp"
env["COMPOSE_PROJECT_NAME"] = "mcghidra"
return subprocess.run(
cmd,
@ -345,7 +345,7 @@ class DockerMixin(GhydraMixinBase):
def _generate_container_name(self, binary_name: str) -> str:
"""Generate a unique container name for this session.
Format: ghydramcp-{session_id}-{binary_stem}
Format: mcghidra-{session_id}-{binary_stem}
Args:
binary_name: Name of the binary being analyzed
@ -356,7 +356,7 @@ class DockerMixin(GhydraMixinBase):
# Clean binary name for container naming
stem = Path(binary_name).stem.lower()
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]:
"""Generate Docker labels for a container.
@ -383,7 +383,7 @@ class DockerMixin(GhydraMixinBase):
label_filter: Optional[str] = None,
session_only: bool = False,
) -> List[Dict[str, Any]]:
"""Find GhydraMCP containers by label.
"""Find MCGhidra containers by label.
Args:
label_filter: Additional label filter (e.g., "port=8192")
@ -431,19 +431,19 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool(
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]:
"""Check Docker status and list running GhydraMCP containers.
"""Check Docker status and list running MCGhidra containers.
Returns:
Status information including:
- docker_available: Whether Docker is installed
- docker_running: Whether Docker daemon is running
- 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
- images: Available GhydraMCP images
- images: Available MCGhidra images
"""
result = {
"docker_available": False,
@ -479,7 +479,7 @@ class DockerMixin(GhydraMixinBase):
except subprocess.CalledProcessError:
pass
# List all GhydraMCP containers (from any session)
# List all MCGhidra containers (from any session)
result["containers"] = await self._find_containers_by_label()
# List containers from this session only
@ -495,7 +495,7 @@ class DockerMixin(GhydraMixinBase):
"ps",
"-a",
"--filter",
"name=ghydramcp",
"name=mcghidra",
"--format",
"{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}",
]
@ -517,13 +517,13 @@ class DockerMixin(GhydraMixinBase):
except subprocess.CalledProcessError:
pass
# List GhydraMCP images
# List MCGhidra images
try:
images_result = await self._run_docker_cmd(
[
"images",
"--filter",
"reference=ghydramcp*",
"reference=mcghidra*",
"--format",
"{{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedSince}}",
]
@ -546,7 +546,7 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool(
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(
self,
@ -555,9 +555,9 @@ class DockerMixin(GhydraMixinBase):
name: Optional[str] = None,
ctx: Optional[Context] = None,
) -> 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,
then the HTTP API will be available.
@ -635,9 +635,9 @@ class DockerMixin(GhydraMixinBase):
"-v",
f"{binary_file.parent}:/binaries:ro",
"-e",
f"GHYDRA_MAXMEM={memory}",
f"MCGHIDRA_MAXMEM={memory}",
*label_args,
"ghydramcp:latest",
"mcghidra:latest",
f"/binaries/{binary_file.name}",
]
)
@ -673,12 +673,12 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool(
name="docker_stop",
description="Stop a running GhydraMCP Docker container",
description="Stop a running MCGhidra Docker container",
)
async def docker_stop(
self, name_or_id: str, remove: bool = True, ctx: Optional[Context] = None
) -> 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
MCP session. Attempting to stop another session's container will fail
@ -752,7 +752,7 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool(
name="docker_logs",
description="Get logs from a GhydraMCP Docker container",
description="Get logs from a MCGhidra Docker container",
)
async def docker_logs(
self,
@ -761,7 +761,7 @@ class DockerMixin(GhydraMixinBase):
follow: bool = False,
ctx: Optional[Context] = None,
) -> Dict[str, Any]:
"""Get logs from a GhydraMCP container.
"""Get logs from a MCGhidra container.
Args:
name_or_id: Container name or ID
@ -792,7 +792,7 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool(
name="docker_build",
description="Build the GhydraMCP Docker image from source",
description="Build the MCGhidra Docker image from source",
)
async def docker_build(
self,
@ -801,12 +801,12 @@ class DockerMixin(GhydraMixinBase):
project_dir: Optional[str] = None,
ctx: Optional[Context] = None,
) -> Dict[str, Any]:
"""Build the GhydraMCP Docker image.
"""Build the MCGhidra Docker image.
Args:
tag: Image tag (default: 'latest')
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:
Build status
@ -824,7 +824,7 @@ class DockerMixin(GhydraMixinBase):
proj_path = module_dir
else:
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"
@ -835,7 +835,7 @@ class DockerMixin(GhydraMixinBase):
args = [
"build",
"-t",
f"ghydramcp:{tag}",
f"mcghidra:{tag}",
"-f",
str(dockerfile),
]
@ -848,8 +848,8 @@ class DockerMixin(GhydraMixinBase):
return {
"success": True,
"image": f"ghydramcp:{tag}",
"message": f"Successfully built ghydramcp:{tag}",
"image": f"mcghidra:{tag}",
"message": f"Successfully built mcghidra:{tag}",
"output": result.stdout[-2000:] if len(result.stdout) > 2000 else result.stdout,
}
@ -899,12 +899,12 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool(
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(
self, port: Optional[int] = None, timeout: float = 5.0, ctx: Optional[Context] = None
) -> Dict[str, Any]:
"""Check if a GhydraMCP container's API is healthy.
"""Check if a MCGhidra container's API is healthy.
Args:
port: API port to check (uses current instance if not specified)
@ -920,7 +920,7 @@ class DockerMixin(GhydraMixinBase):
@mcp_tool(
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(
self,
@ -978,10 +978,10 @@ class DockerMixin(GhydraMixinBase):
}
# 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 {
"error": (
"GhydraMCP Docker image not found. "
"MCGhidra Docker image not found. "
"Build it with docker_build() or 'make build' first."
)
}
@ -1017,14 +1017,14 @@ class DockerMixin(GhydraMixinBase):
dry_run: bool = False,
ctx: Optional[Context] = None,
) -> 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
port locks behind.
By default, only cleans containers from the current session to prevent
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:
session_only: Only clean up containers from this session (default: True for safety)

View File

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

View File

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

View File

@ -1,4 +1,4 @@
"""Memory mixin for GhydraMCP.
"""Memory mixin for MCGhidra.
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 .base import GhydraMixinBase
from .base import MCGhidraMixinBase
class MemoryMixin(GhydraMixinBase):
class MemoryMixin(MCGhidraMixinBase):
"""Mixin for memory operations.
Provides tools for:

View File

@ -1,4 +1,4 @@
"""Namespaces mixin for GhydraMCP.
"""Namespaces mixin for MCGhidra.
Provides tools for querying namespaces and class definitions.
"""
@ -9,10 +9,10 @@ from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool
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.
Provides tools for:

View File

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

View File

@ -1,4 +1,4 @@
"""Structs mixin for GhydraMCP.
"""Structs mixin for MCGhidra.
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 ..config import get_config
from .base import GhydraMixinBase
from .base import MCGhidraMixinBase
class StructsMixin(GhydraMixinBase):
class StructsMixin(MCGhidraMixinBase):
"""Mixin for struct operations.
Provides tools for:

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
#!/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
different types of comments in Ghidra, including plate, pre, post, EOL,

View File

@ -1,6 +1,6 @@
#!/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:
1. Creating data items with different types

View File

@ -1,6 +1,6 @@
#!/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.
"""
import json
@ -14,9 +14,9 @@ import sys
DEFAULT_PORT = 8192
# Get host from environment variable or default to localhost
GHYDRAMCP_TEST_HOST = os.getenv('GHYDRAMCP_TEST_HOST')
if GHYDRAMCP_TEST_HOST and GHYDRAMCP_TEST_HOST.strip():
BASE_URL = f"http://{GHYDRAMCP_TEST_HOST}:{DEFAULT_PORT}"
MCGHIDRA_TEST_HOST = os.getenv('MCGHIDRA_TEST_HOST')
if MCGHIDRA_TEST_HOST and MCGHIDRA_TEST_HOST.strip():
BASE_URL = f"http://{MCGHIDRA_TEST_HOST}:{DEFAULT_PORT}"
else:
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.
"""
class GhydraMCPHttpApiTests(unittest.TestCase):
"""Test cases for the GhydraMCP HTTP API"""
class MCGhidraHttpApiTests(unittest.TestCase):
"""Test cases for the MCGhidra HTTP API"""
def assertStandardSuccessResponse(self, data):
"""Helper to assert the standard success response structure for HATEOAS API."""

View File

@ -1,6 +1,6 @@
#!/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.
"""
import json
@ -14,8 +14,8 @@ from mcp.client.session import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client
# Get host and port from environment variables or use defaults
GHYDRAMCP_TEST_HOST = os.getenv('GHYDRAMCP_TEST_HOST', 'localhost')
GHYDRAMCP_TEST_PORT = int(os.getenv('GHYDRAMCP_TEST_PORT', '8192'))
MCGHIDRA_TEST_HOST = os.getenv('MCGHIDRA_TEST_HOST', 'localhost')
MCGHIDRA_TEST_PORT = int(os.getenv('MCGHIDRA_TEST_PORT', '8192'))
# Set up logging
logging.basicConfig(level=logging.INFO)
@ -95,8 +95,8 @@ async def test_bridge():
logger.info(f"List instances result: {list_instances_result}")
# Set the current instance to use for subsequent calls
logger.info(f"Setting current instance to port {GHYDRAMCP_TEST_PORT}...")
use_instance_result = await session.call_tool("instances_use", arguments={"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": MCGHIDRA_TEST_PORT})
logger.info(f"Use instance result: {use_instance_result}")
# Call the functions_list tool (no port needed now)

38
uv.lock generated
View File

@ -414,25 +414,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/db/ee/327a3f6c7ac5cde56c7c9449dbc6c0ab78b15c06a51ad5645ab880240120/fastmcp_feedback-2026.1.12.1-py3-none-any.whl", hash = "sha256:6a3dec71f3d3eae4eb0102eb0a86aa7853fb0419fb506a5a13d17deaf842c53c", size = 29789, upload-time = "2026-01-16T02:09:11.831Z" },
]
[[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]]
name = "greenlet"
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" },
]
[[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]]
name = "mcp"
version = "1.23.1"