chore: prepare v2.0.0 release

- Update version to v2.0.0 in ApiConstants.java and bridge_mcp_hydra.py
- Create CHANGELOG v2.0.0 section with release date
- Fix Ghidra 11.3.2+ compatibility in TransactionHelper (endTransaction signature)
- Clarify instances_list vs instances_discover usage in documentation
- Remove commented-out code in pom.xml

Fixes #7
Closes #5
This commit is contained in:
Teal Bauer 2025-11-11 12:54:03 +01:00
parent 4379bea14f
commit bc1e137878
7 changed files with 53 additions and 76 deletions

View File

@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased] ## [Unreleased]
## [2.0.0] - 2025-11-11
### Added ### Added
- **MCP Integration Refactor:** Refactored the Python bridge for improved MCP integration. (337f89e) - **MCP Integration Refactor:** Refactored the Python bridge for improved MCP integration. (337f89e)
- Introduced MCP resources for loading context (e.g., instances, functions, disassembly). - Introduced MCP resources for loading context (e.g., instances, functions, disassembly).
@ -115,7 +117,8 @@ 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/v1.4.0...HEAD [unreleased]: https://github.com/teal-bauer/GhydraMCP/compare/v2.0.0...HEAD
[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.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.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.2]: https://github.com/teal-bauer/GhydraMCP/compare/v1.1...v1.2

View File

@ -83,12 +83,12 @@ When making changes, update version numbers in these locations:
1. **Plugin Version** in `src/main/java/eu/starsong/ghidra/api/ApiConstants.java`: 1. **Plugin Version** in `src/main/java/eu/starsong/ghidra/api/ApiConstants.java`:
```java ```java
public static final String PLUGIN_VERSION = "v2.0.0-beta.1"; public static final String PLUGIN_VERSION = "v2.0.0";
``` ```
2. **Bridge Version** in `bridge_mcp_hydra.py`: 2. **Bridge Version** in `bridge_mcp_hydra.py`:
```python ```python
BRIDGE_VERSION = "v2.0.0-beta.1" BRIDGE_VERSION = "v2.0.0"
``` ```
### API Versions ### API Versions

View File

@ -121,7 +121,7 @@ First, download the latest [release](https://github.com/teal-bauer/GhydraMCP/rel
1. Run Ghidra 1. Run Ghidra
2. Select `File` -> `Install Extensions` 2. Select `File` -> `Install Extensions`
3. Click the `+` button 3. Click the `+` button
4. Select the `GhydraMCP-2.0.0-beta.1.zip` (or your chosen version) from the downloaded release 4. Select the `GhydraMCP-[version].zip` file from the downloaded release
5. Restart Ghidra 5. Restart Ghidra
6. Make sure the GhydraMCPPlugin is enabled in `File` -> `Configure` -> `Developer` 6. Make sure the GhydraMCPPlugin is enabled in `File` -> `Configure` -> `Developer`
@ -177,10 +177,10 @@ Theoretically, any MCP client should work with GhydraMCP. Two examples are given
- `update_data`: Update both name and type (params: address, name, data_type) - `update_data`: Update both name and type (params: address, name, data_type)
**Instance Management**: **Instance Management**:
- `list_instances`: List active Ghidra instances (no params) - `list_instances`: List active Ghidra instances, automatically discovering new ones on default host (no params) - **use this first**
- `discover_instances`: Discover instances on a specific host (params: host [optional]) - **only use for non-default hosts**
- `register_instance`: Register new instance (params: port, url) - `register_instance`: Register new instance (params: port, url)
- `unregister_instance`: Remove instance (params: port) - `unregister_instance`: Remove instance (params: port)
- `discover_instances`: Auto-discover running instances (params: host [optional])
**Example Usage**: **Example Usage**:
```python ```python
@ -210,12 +210,10 @@ client.use_tool("ghydra", "update_data", {"address": "0x00401238", "name": "ptr_
client.use_tool("ghydra", "delete_data", {"address": "0x0040123C"}) client.use_tool("ghydra", "delete_data", {"address": "0x0040123C"})
# Instance management # Instance management
client.use_tool("ghydra", "list_instances") # Lists all instances (auto-discovers on default host)
client.use_tool("ghydra", "discover_instances", {"host": "192.168.1.10"}) # Only if scanning different host
client.use_tool("ghydra", "register_instance", {"port": 8192, "url": "http://localhost:8192/"}) client.use_tool("ghydra", "register_instance", {"port": 8192, "url": "http://localhost:8192/"})
client.use_tool("ghydra", "register_instance", {"port": 8193}) client.use_tool("ghydra", "register_instance", {"port": 8193})
# Auto-discover instances
client.use_tool("ghydra", "discover_instances") # Default host
client.use_tool("ghydra", "discover_instances", {"host": "192.168.1.10"}) # Custom host
``` ```
## Client Setup ## Client Setup
@ -256,24 +254,7 @@ Check which Ghidra instances are currently running.
**Assistant:** **Assistant:**
``` ```
I'll check which Ghidra instances are currently running by discovering any active instances. I'll check which Ghidra instances are currently running.
View result from discover_instances from ghydra (local)
{
"found": 2,
"instances": [
{
"port": 8192,
"url": "http://localhost:8192",
"result": "Registered instance on port 8192 at http://localhost:8192"
},
{
"port": 8193,
"url": "http://localhost:8193",
"result": "Registered instance on port 8193 at http://localhost:8193"
}
]
}
View result from list_instances from ghydra (local) View result from list_instances from ghydra (local)
{ {

View File

@ -31,7 +31,7 @@ DEFAULT_GHIDRA_HOST = "localhost"
QUICK_DISCOVERY_RANGE = range(DEFAULT_GHIDRA_PORT, DEFAULT_GHIDRA_PORT+10) QUICK_DISCOVERY_RANGE = range(DEFAULT_GHIDRA_PORT, DEFAULT_GHIDRA_PORT+10)
FULL_DISCOVERY_RANGE = range(DEFAULT_GHIDRA_PORT, DEFAULT_GHIDRA_PORT+20) FULL_DISCOVERY_RANGE = range(DEFAULT_GHIDRA_PORT, DEFAULT_GHIDRA_PORT+20)
BRIDGE_VERSION = "v2.0.0-beta.5" BRIDGE_VERSION = "v2.0.0"
REQUIRED_API_VERSION = 2005 REQUIRED_API_VERSION = 2005
current_instance_port = DEFAULT_GHIDRA_PORT current_instance_port = DEFAULT_GHIDRA_PORT
@ -39,7 +39,10 @@ current_instance_port = DEFAULT_GHIDRA_PORT
instructions = """ instructions = """
GhydraMCP allows interacting with multiple Ghidra SRE instances. Ghidra SRE is a tool for reverse engineering and analyzing binaries, e.g. malware. GhydraMCP allows interacting with multiple Ghidra SRE instances. Ghidra SRE is a tool for reverse engineering and analyzing binaries, e.g. malware.
First, run `instances_discover()` to find all available Ghidra instances (both already known and newly discovered). Then use `instances_use(port)` to set your working instance. 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.
Note: Use `instances_discover(host)` only if you need to scan a different host.
The API is organized into namespaces for different types of operations: The API is organized into namespaces for different types of operations:
- instances_* : For managing Ghidra instances - instances_* : For managing Ghidra instances
@ -1151,7 +1154,19 @@ def reverse_engineer_binary_prompt(port: int = None):
# Instance management tools # Instance management tools
@mcp.tool() @mcp.tool()
def instances_list() -> dict: def instances_list() -> dict:
"""List all active Ghidra instances""" """List all active Ghidra instances
This is the primary tool for working with instances. It automatically discovers
new instances on the default host before listing.
Use instances_discover(host) only if you need to scan a different host.
Returns:
dict: Contains 'instances' list with all available Ghidra instances
"""
# Auto-discover new instances before listing
_discover_instances(QUICK_DISCOVERY_RANGE, host=None, timeout=0.5)
with instances_lock: with instances_lock:
return { return {
"instances": [ "instances": [
@ -1167,44 +1182,32 @@ def instances_list() -> dict:
@mcp.tool() @mcp.tool()
def instances_discover(host: str = None) -> dict: def instances_discover(host: str = None) -> dict:
"""Discover available Ghidra instances by scanning ports """Discover Ghidra instances on a specific host
Use this ONLY when you need to discover instances on a different host.
For normal usage, just use instances_list() which auto-discovers on the default host.
Args: Args:
host: Optional host to scan (default: configured ghidra_host) host: Host to scan for Ghidra instances (default: configured ghidra_host)
Returns: Returns:
dict: Contains 'found' count, 'new_instances' count, and 'instances' list with all available instances dict: Contains 'instances' list with all available instances after discovery
""" """
# Get newly discovered instances # Discover instances on the specified host
discovery_result = _discover_instances(QUICK_DISCOVERY_RANGE, host=host, timeout=0.5) _discover_instances(QUICK_DISCOVERY_RANGE, host=host, timeout=0.5)
new_instances = discovery_result.get("instances", [])
new_count = len(new_instances)
# Get all currently known instances (including ones that were already registered) # Return all instances (same format as instances_list for consistency)
all_instances = []
with instances_lock: with instances_lock:
for port, info in active_instances.items(): return {
instance_info = { "instances": [
{
"port": port, "port": port,
"url": info["url"], "url": info["url"],
"project": info.get("project", ""), "project": info.get("project", ""),
"file": info.get("file", ""), "file": info.get("file", "")
"plugin_version": info.get("plugin_version", "unknown"),
"api_version": info.get("api_version", "unknown")
} }
for port, info in active_instances.items()
# Mark if this was newly discovered in this call ]
instance_info["newly_discovered"] = any(inst["port"] == port for inst in new_instances)
all_instances.append(instance_info)
# Sort by port for consistent ordering
all_instances.sort(key=lambda x: x["port"])
return {
"found": len(all_instances), # Total instances available
"new_instances": new_count, # How many were newly discovered
"instances": all_instances # All available instances
} }
@mcp.tool() @mcp.tool()

10
pom.xml
View File

@ -99,16 +99,6 @@
</resources> </resources>
<plugins> <plugins>
<!-- Set Java version --> <!-- Set Java version -->
<!-- Resources plugin to handle filtering -->
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin> -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>

View File

@ -1,7 +1,7 @@
package eu.starsong.ghidra.api; package eu.starsong.ghidra.api;
public class ApiConstants { public class ApiConstants {
public static final String PLUGIN_VERSION = "v2.0.0-beta.5"; public static final String PLUGIN_VERSION = "v2.0.0";
public static final int API_VERSION = 2005; public static final int API_VERSION = 2005;
public static final int DEFAULT_PORT = 8192; public static final int DEFAULT_PORT = 8192;
public static final int MAX_PORT_ATTEMPTS = 10; public static final int MAX_PORT_ATTEMPTS = 10;

View File

@ -38,7 +38,7 @@ public class TransactionHelper {
Msg.error(TransactionHelper.class, "Transaction failed: " + transactionName, e); Msg.error(TransactionHelper.class, "Transaction failed: " + transactionName, e);
} finally { } finally {
if (txId >= 0) { if (txId >= 0) {
program.endTransaction(txId, success); success = program.endTransaction(txId, success);
} }
} }
}); });