diff --git a/README.md b/README.md
index 27a729f..6707a09 100644
--- a/README.md
+++ b/README.md
@@ -7,19 +7,28 @@

-
-# ghidraMCP
-ghidraMCP is an Model Context Protocol server for allowing LLMs to autonomously reverse engineer applications. It exposes numerous tools from core Ghidra functionality to MCP clients.
+# GhydraMCP
+GhydraMCP is an Model Context Protocol server for allowing LLMs to autonomously reverse engineer applications. It exposes numerous tools from core Ghidra functionality to MCP clients.
https://github.com/user-attachments/assets/36080514-f227-44bd-af84-78e29ee1d7f9
+GhydraMCP is based on [GhidraMCP by Laurie Wired](https://github.com/LaurieWired/GhidraMCP/).
# Features
MCP Server + Ghidra Plugin
-- Decompile and analyze binaries in Ghidra
-- Automatically rename methods and data
-- List methods, classes, imports, and exports
+- Full program analysis capabilities:
+ - Decompile functions to C code
+ - Cross-reference analysis
+ - Data type propagation
+- Interactive reverse engineering:
+ - Rename functions, variables, and data
+ - Add comments and labels
+ - Modify data types
+- Program exploration:
+ - List functions, classes, namespaces
+ - View imports, exports, segments
+ - Search by name or pattern
# Installation
@@ -29,14 +38,14 @@ MCP Server + Ghidra Plugin
- MCP [SDK](https://github.com/modelcontextprotocol/python-sdk)
## Ghidra
-First, download the latest [release](https://github.com/LaurieWired/GhidraMCP/releases) from this repository. This contains the Ghidra plugin and Python MCP client. Then, you can directly import the plugin into Ghidra.
+First, download the latest [release](https://github.com/teal-bauer/GhydraMCP/releases) from this repository. This contains the Ghidra plugin and Python MCP client. Then, you can directly import the plugin into Ghidra.
1. Run Ghidra
2. Select `File` -> `Install Extensions`
3. Click the `+` button
-4. Select the `GhidraMCP-1-0.zip` (or your chosen version) from the downloaded release
+4. Select the `GhydraMCP-1-1.zip` (or your chosen version) from the downloaded release
5. Restart Ghidra
-6. Make sure the GhidraMCPPlugin is enabled in `File` -> `Configure` -> `Developer`
+6. Make sure the GhydraMCPPlugin is enabled in `File` -> `Configure` -> `Developer`
Video Installation Guide:
@@ -47,35 +56,63 @@ https://github.com/user-attachments/assets/75f0c176-6da1-48dc-ad96-c182eb4648c3
## MCP Clients
-Theoretically, any MCP client should work with ghidraMCP. Two examples are given below.
+Theoretically, any MCP client should work with GhydraMCP. Two examples are given below.
-## Example 1: Claude Desktop
-To set up Claude Desktop as a Ghidra MCP client, go to `Claude` -> `Settings` -> `Developer` -> `Edit Config` -> `claude_desktop_config.json` and add the following:
+## API Reference
+### Available Tools
+
+**Program Analysis**:
+- `list_methods`: List all functions (params: offset, limit)
+- `list_classes`: List all classes/namespaces (params: offset, limit)
+- `decompile_function`: Get decompiled C code (params: name)
+- `rename_function`: Rename a function (params: old_name, new_name)
+- `rename_data`: Rename data at address (params: address, new_name)
+- `list_segments`: View memory segments (params: offset, limit)
+- `list_imports`: List imported symbols (params: offset, limit)
+- `list_exports`: List exported functions (params: offset, limit)
+- `list_namespaces`: Show namespaces (params: offset, limit)
+- `list_data_items`: View data labels (params: offset, limit)
+- `search_functions_by_name`: Find functions (params: query, offset, limit)
+
+**Instance Management**:
+- `list_instances`: List active Ghidra instances (no params)
+- `register_instance`: Register new instance (params: port, url)
+- `unregister_instance`: Remove instance (params: port)
+
+**Example Usage**:
+```python
+# Program analysis
+client.use_tool("ghydra", "decompile_function", {"name": "main"})
+
+# Instance management
+client.use_tool("ghydra", "register_instance", {"port": 8192, "url": "http://localhost:8192/"})
+client.use_tool("ghydra", "register_instance", {"port": 8193})
+```
+
+## Client Setup
+
+### Claude Desktop Configuration
```json
{
"mcpServers": {
- "ghidra": {
+ "ghydra": {
"command": "python",
"args": [
- "/ABSOLUTE_PATH_TO/bridge_mcp_ghidra.py"
- ]
+ "/ABSOLUTE_PATH_TO/bridge_mcp_hydra.py"
+ ],
+ "env": {
+ "GHIDRA_HYDRA_HOST": "localhost" // Optional - defaults to localhost
+ }
}
}
}
```
-Alternatively, edit this file directly:
-```
-/Users/YOUR_USER/Library/Application Support/Claude/claude_desktop_config.json
-```
-
-## Example 2: 5ire
-Another MCP client that supports multiple models on the backend is [5ire](https://github.com/nanbingxyz/5ire). To set up GhidraMCP, open 5ire and go to `Tools` -> `New` and set the following configurations:
-
-1. Tool Key: ghidra
-2. Name: GhidraMCP
-3. Command: `python /ABSOLUTE_PATH_TO/bridge_mcp_ghidra.py`
+### 5ire Configuration
+1. Tool Key: ghydra
+2. Name: GhydraMCP
+3. Command: `python /ABSOLUTE_PATH_TO/bridge_mcp_hydra.py`
# Building from Source
Build with Maven by running:
@@ -84,6 +121,6 @@ Build with Maven by running:
The generated zip file includes the built Ghidra plugin and its resources. These files are required for Ghidra to recognize the new extension.
-- lib/GhidraMCP.jar
+- lib/GhydraMCP.jar
- extensions.properties
- Module.manifest
diff --git a/bridge_mcp_ghidra.py b/bridge_mcp_hydra.py
similarity index 73%
rename from bridge_mcp_ghidra.py
rename to bridge_mcp_hydra.py
index e9043e9..92227ad 100644
--- a/bridge_mcp_ghidra.py
+++ b/bridge_mcp_hydra.py
@@ -5,6 +5,7 @@
# "requests==2.32.3",
# ]
# ///
+import os
import sys
import requests
from typing import Dict
@@ -19,11 +20,11 @@ DEFAULT_GHIDRA_HOST = "localhost"
mcp = FastMCP("hydra-mcp")
-# Get host from command line or use default
-ghidra_host = DEFAULT_GHIDRA_HOST
+# Get host from environment variable, command line, or use default
+ghidra_host = os.environ.get("GHIDRA_HYDRA_HOST", DEFAULT_GHIDRA_HOST)
if len(sys.argv) > 1:
ghidra_host = sys.argv[1]
- print(f"Using Ghidra host: {ghidra_host}")
+print(f"Using Ghidra host: {ghidra_host}")
def get_instance_url(port: int) -> str:
"""Get URL for a Ghidra instance by port"""
@@ -65,6 +66,26 @@ def safe_get(port: int, endpoint: str, params: dict = None) -> list:
except Exception as e:
return [f"Request failed: {str(e)}"]
+def safe_put(port: int, endpoint: str, data: dict) -> str:
+ """Perform a PUT request to a specific Ghidra instance"""
+ try:
+ url = f"{get_instance_url(port)}/{endpoint}"
+ response = requests.put(url, data=data, timeout=5)
+ response.encoding = 'utf-8'
+ if response.ok:
+ return response.text.strip()
+ elif response.status_code == 404 and port != DEFAULT_GHIDRA_PORT:
+ # Try falling back to default instance
+ return safe_put(DEFAULT_GHIDRA_PORT, endpoint, data)
+ else:
+ return f"Error {response.status_code}: {response.text.strip()}"
+ except requests.exceptions.ConnectionError:
+ if port != DEFAULT_GHIDRA_PORT:
+ return safe_put(DEFAULT_GHIDRA_PORT, endpoint, data)
+ return "Error: Failed to connect to Ghidra instance"
+ except Exception as e:
+ return f"Request failed: {str(e)}"
+
def safe_post(port: int, endpoint: str, data: dict | str) -> str:
"""Perform a POST request to a specific Ghidra instance"""
try:
@@ -129,25 +150,32 @@ def unregister_instance(port: int) -> str:
return f"No instance found on port {port}"
# Updated tool implementations with port parameter
+from urllib.parse import quote
+
@mcp.tool()
-def list_methods(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
- return safe_get(port, "methods", {"offset": offset, "limit": limit})
+def list_functions(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
+ """List all functions with pagination"""
+ return safe_get(port, "functions", {"offset": offset, "limit": limit})
@mcp.tool()
def list_classes(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
+ """List all classes with pagination"""
return safe_get(port, "classes", {"offset": offset, "limit": limit})
@mcp.tool()
-def decompile_function(port: int = DEFAULT_GHIDRA_PORT, name: str = "") -> str:
- return safe_post(port, "decompile", name)
+def get_function(port: int = DEFAULT_GHIDRA_PORT, name: str = "") -> str:
+ """Get decompiled code for a specific function"""
+ return safe_get(port, f"functions/{quote(name)}", {})
@mcp.tool()
-def rename_function(port: int = DEFAULT_GHIDRA_PORT, old_name: str = "", new_name: str = "") -> str:
- return safe_post(port, "renameFunction", {"oldName": old_name, "newName": new_name})
+def update_function(port: int = DEFAULT_GHIDRA_PORT, name: str = "", new_name: str = "") -> str:
+ """Rename a function"""
+ return safe_put(port, f"functions/{quote(name)}", {"newName": new_name})
@mcp.tool()
-def rename_data(port: int = DEFAULT_GHIDRA_PORT, address: str = "", new_name: str = "") -> str:
- return safe_post(port, "renameData", {"address": address, "newName": new_name})
+def update_data(port: int = DEFAULT_GHIDRA_PORT, address: str = "", new_name: str = "") -> str:
+ """Rename data at specified address"""
+ return safe_put(port, "data", {"address": address, "newName": new_name})
@mcp.tool()
def list_segments(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
@@ -155,11 +183,11 @@ def list_segments(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int =
@mcp.tool()
def list_imports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
- return safe_get(port, "imports", {"offset": offset, "limit": limit})
+ return safe_get(port, "symbols/imports", {"offset": offset, "limit": limit})
@mcp.tool()
def list_exports(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
- return safe_get(port, "exports", {"offset": offset, "limit": limit})
+ return safe_get(port, "symbols/exports", {"offset": offset, "limit": limit})
@mcp.tool()
def list_namespaces(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int = 100) -> list:
@@ -173,7 +201,7 @@ def list_data_items(port: int = DEFAULT_GHIDRA_PORT, offset: int = 0, limit: int
def search_functions_by_name(port: int = DEFAULT_GHIDRA_PORT, query: str = "", offset: int = 0, limit: int = 100) -> list:
if not query:
return ["Error: query string is required"]
- return safe_get(port, "searchFunctions", {"query": query, "offset": offset, "limit": limit})
+ return safe_get(port, "functions", {"query": query, "offset": offset, "limit": limit})
# Handle graceful shutdown
import signal
diff --git a/pom.xml b/pom.xml
index f7f5dc4..1fdadc5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,12 +3,12 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- com.lauriewired
- HydraMCP
+ eu.starsong.ghidra
+ GhydraMCP
jar
1.1
- HydraMCP
- https://github.com/LaurieWired/GhidraMCP
+ GhydraMCP
+ https://github.com/teal-bauer/GhydraMCP
@@ -93,7 +93,7 @@
src/main/resources/META-INF/MANIFEST.MF
- HydraMCP
+ GhydraMCP
**/App.class
@@ -115,7 +115,7 @@
- HydraMCP-${project.version}
+ GhydraMCP-${project.version}
false
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 7d62d8d..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-mcp==1.5.0
-requests==2.32.3
diff --git a/src/assembly/ghidra-extension.xml b/src/assembly/ghidra-extension.xml
index 24cc449..86bf855 100644
--- a/src/assembly/ghidra-extension.xml
+++ b/src/assembly/ghidra-extension.xml
@@ -17,24 +17,24 @@
+ of a folder named GhydraMCP/ (the actual extension folder). -->
src/main/resources
extension.properties
Module.manifest
- HydraMCP
+ GhydraMCP
-
+
${project.build.directory}
- HydraMCP.jar
+ GhydraMCP.jar
- HydraMCP/lib
+ GhydraMCP/lib
diff --git a/src/main/java/com/lauriewired/HydraMCPPlugin.java b/src/main/java/eu/starsong/ghidra/GhydraMCPPlugin.java
similarity index 74%
rename from src/main/java/com/lauriewired/HydraMCPPlugin.java
rename to src/main/java/eu/starsong/ghidra/GhydraMCPPlugin.java
index bd28383..bc07381 100644
--- a/src/main/java/com/lauriewired/HydraMCPPlugin.java
+++ b/src/main/java/eu/starsong/ghidra/GhydraMCPPlugin.java
@@ -1,7 +1,6 @@
-package com.lauriewired;
+package eu.starsong.ghidra;
import ghidra.framework.plugintool.*;
-import ghidra.framework.plugintool.util.*;
import ghidra.framework.main.ApplicationLevelPlugin;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.GlobalNamespace;
@@ -38,17 +37,16 @@ import java.util.concurrent.atomic.*;
shortDescription = "HTTP server plugin",
description = "Starts an embedded HTTP server to expose program data."
)
-public class HydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
+public class GhydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
- private static final Map activeInstances = new ConcurrentHashMap<>();
- private static final AtomicInteger nextPort = new AtomicInteger(8192);
+ private static final Map activeInstances = new ConcurrentHashMap<>();
private static final Object baseInstanceLock = new Object();
private HttpServer server;
private int port;
private boolean isBaseInstance = false;
- public HydraMCPPlugin(PluginTool tool) {
+ public GhydraMCPPlugin(PluginTool tool) {
super(tool);
// Find available port
@@ -64,8 +62,8 @@ public class HydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
}
// Log to both console and log file
- Msg.info(this, "HydraMCPPlugin loaded on port " + port);
- System.out.println("[HydraMCP] Plugin loaded on port " + port);
+ Msg.info(this, "GhydraMCPPlugin loaded on port " + port);
+ System.out.println("[GhydraMCP] Plugin loaded on port " + port);
try {
startServer();
@@ -82,85 +80,127 @@ public class HydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
server = HttpServer.create(new InetSocketAddress(port), 0);
// Each listing endpoint uses offset & limit from query params:
- server.createContext("/methods", exchange -> {
- Map qparams = parseQueryParams(exchange);
- int offset = parseIntOrDefault(qparams.get("offset"), 0);
- int limit = parseIntOrDefault(qparams.get("limit"), 100);
- sendResponse(exchange, getAllFunctionNames(offset, limit));
+ // Function resources
+ server.createContext("/functions", exchange -> {
+ if ("GET".equals(exchange.getRequestMethod())) {
+ Map qparams = parseQueryParams(exchange);
+ int offset = parseIntOrDefault(qparams.get("offset"), 0);
+ int limit = parseIntOrDefault(qparams.get("limit"), 100);
+ String query = qparams.get("query");
+
+ if (query != null && !query.isEmpty()) {
+ sendResponse(exchange, searchFunctionsByName(query, offset, limit));
+ } else {
+ sendResponse(exchange, getAllFunctionNames(offset, limit));
+ }
+ } else {
+ exchange.sendResponseHeaders(405, -1); // Method Not Allowed
+ }
});
+ server.createContext("/functions/", exchange -> {
+ String path = exchange.getRequestURI().getPath();
+ String name = path.substring(path.lastIndexOf('/') + 1);
+ try {
+ name = java.net.URLDecoder.decode(name, StandardCharsets.UTF_8.name());
+ } catch (Exception e) {
+ Msg.error(this, "Failed to decode function name", e);
+ exchange.sendResponseHeaders(400, -1); // Bad Request
+ return;
+ }
+
+ if ("GET".equals(exchange.getRequestMethod())) {
+ sendResponse(exchange, decompileFunctionByName(name));
+ } else if ("PUT".equals(exchange.getRequestMethod())) {
+ Map params = parsePostParams(exchange);
+ String newName = params.get("newName");
+ String response = renameFunction(name, newName)
+ ? "Renamed successfully" : "Rename failed";
+ sendResponse(exchange, response);
+ } else {
+ exchange.sendResponseHeaders(405, -1); // Method Not Allowed
+ }
+ });
+
+ // Class resources
server.createContext("/classes", exchange -> {
- Map qparams = parseQueryParams(exchange);
- int offset = parseIntOrDefault(qparams.get("offset"), 0);
- int limit = parseIntOrDefault(qparams.get("limit"), 100);
- sendResponse(exchange, getAllClassNames(offset, limit));
- });
-
- server.createContext("/decompile", exchange -> {
- String name = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8);
- sendResponse(exchange, decompileFunctionByName(name));
- });
-
- server.createContext("/renameFunction", exchange -> {
- Map params = parsePostParams(exchange);
- String response = renameFunction(params.get("oldName"), params.get("newName"))
- ? "Renamed successfully" : "Rename failed";
- sendResponse(exchange, response);
- });
-
- server.createContext("/renameData", exchange -> {
- Map params = parsePostParams(exchange);
- renameDataAtAddress(params.get("address"), params.get("newName"));
- sendResponse(exchange, "Rename data attempted");
+ if ("GET".equals(exchange.getRequestMethod())) {
+ Map qparams = parseQueryParams(exchange);
+ int offset = parseIntOrDefault(qparams.get("offset"), 0);
+ int limit = parseIntOrDefault(qparams.get("limit"), 100);
+ sendResponse(exchange, getAllClassNames(offset, limit));
+ } else {
+ exchange.sendResponseHeaders(405, -1); // Method Not Allowed
+ }
});
+ // Memory segments
server.createContext("/segments", exchange -> {
- Map qparams = parseQueryParams(exchange);
- int offset = parseIntOrDefault(qparams.get("offset"), 0);
- int limit = parseIntOrDefault(qparams.get("limit"), 100);
- sendResponse(exchange, listSegments(offset, limit));
+ if ("GET".equals(exchange.getRequestMethod())) {
+ Map qparams = parseQueryParams(exchange);
+ int offset = parseIntOrDefault(qparams.get("offset"), 0);
+ int limit = parseIntOrDefault(qparams.get("limit"), 100);
+ sendResponse(exchange, listSegments(offset, limit));
+ } else {
+ exchange.sendResponseHeaders(405, -1); // Method Not Allowed
+ }
});
- server.createContext("/imports", exchange -> {
- Map qparams = parseQueryParams(exchange);
- int offset = parseIntOrDefault(qparams.get("offset"), 0);
- int limit = parseIntOrDefault(qparams.get("limit"), 100);
- sendResponse(exchange, listImports(offset, limit));
+ // Symbol resources (imports/exports)
+ server.createContext("/symbols/imports", exchange -> {
+ if ("GET".equals(exchange.getRequestMethod())) {
+ Map qparams = parseQueryParams(exchange);
+ int offset = parseIntOrDefault(qparams.get("offset"), 0);
+ int limit = parseIntOrDefault(qparams.get("limit"), 100);
+ sendResponse(exchange, listImports(offset, limit));
+ } else {
+ exchange.sendResponseHeaders(405, -1); // Method Not Allowed
+ }
});
- server.createContext("/exports", exchange -> {
- Map qparams = parseQueryParams(exchange);
- int offset = parseIntOrDefault(qparams.get("offset"), 0);
- int limit = parseIntOrDefault(qparams.get("limit"), 100);
- sendResponse(exchange, listExports(offset, limit));
+ server.createContext("/symbols/exports", exchange -> {
+ if ("GET".equals(exchange.getRequestMethod())) {
+ Map qparams = parseQueryParams(exchange);
+ int offset = parseIntOrDefault(qparams.get("offset"), 0);
+ int limit = parseIntOrDefault(qparams.get("limit"), 100);
+ sendResponse(exchange, listExports(offset, limit));
+ } else {
+ exchange.sendResponseHeaders(405, -1); // Method Not Allowed
+ }
});
+ // Namespace resources
server.createContext("/namespaces", exchange -> {
- Map qparams = parseQueryParams(exchange);
- int offset = parseIntOrDefault(qparams.get("offset"), 0);
- int limit = parseIntOrDefault(qparams.get("limit"), 100);
- sendResponse(exchange, listNamespaces(offset, limit));
+ if ("GET".equals(exchange.getRequestMethod())) {
+ Map qparams = parseQueryParams(exchange);
+ int offset = parseIntOrDefault(qparams.get("offset"), 0);
+ int limit = parseIntOrDefault(qparams.get("limit"), 100);
+ sendResponse(exchange, listNamespaces(offset, limit));
+ } else {
+ exchange.sendResponseHeaders(405, -1); // Method Not Allowed
+ }
});
+ // Data resources
server.createContext("/data", exchange -> {
- Map qparams = parseQueryParams(exchange);
- int offset = parseIntOrDefault(qparams.get("offset"), 0);
- int limit = parseIntOrDefault(qparams.get("limit"), 100);
- sendResponse(exchange, listDefinedData(offset, limit));
- });
-
- server.createContext("/searchFunctions", exchange -> {
- Map qparams = parseQueryParams(exchange);
- String searchTerm = qparams.get("query");
- int offset = parseIntOrDefault(qparams.get("offset"), 0);
- int limit = parseIntOrDefault(qparams.get("limit"), 100);
- sendResponse(exchange, searchFunctionsByName(searchTerm, offset, limit));
+ if ("GET".equals(exchange.getRequestMethod())) {
+ Map qparams = parseQueryParams(exchange);
+ int offset = parseIntOrDefault(qparams.get("offset"), 0);
+ int limit = parseIntOrDefault(qparams.get("limit"), 100);
+ sendResponse(exchange, listDefinedData(offset, limit));
+ } else if ("PUT".equals(exchange.getRequestMethod())) {
+ Map params = parsePostParams(exchange);
+ renameDataAtAddress(params.get("address"), params.get("newName"));
+ sendResponse(exchange, "Rename data attempted");
+ } else {
+ exchange.sendResponseHeaders(405, -1); // Method Not Allowed
+ }
});
// Instance management endpoints
server.createContext("/instances", exchange -> {
StringBuilder sb = new StringBuilder();
- for (Map.Entry entry : activeInstances.entrySet()) {
+ for (Map.Entry entry : activeInstances.entrySet()) {
sb.append(entry.getKey()).append(": ")
.append(entry.getValue().isBaseInstance ? "base" : "secondary")
.append("\n");
@@ -192,9 +232,9 @@ public class HydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
server.setExecutor(null);
new Thread(() -> {
server.start();
- Msg.info(this, "HydraMCP HTTP server started on port " + port);
- System.out.println("[HydraMCP] HTTP server started on port " + port);
- }, "HydraMCP-HTTP-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();
}
// ----------------------------------------------------------------------------------
@@ -542,7 +582,7 @@ public class HydraMCPPlugin extends Plugin implements ApplicationLevelPlugin {
if (server != null) {
server.stop(0);
Msg.info(this, "HTTP server stopped on port " + port);
- System.out.println("[HydraMCP] HTTP server stopped on port " + port);
+ System.out.println("[GhydraMCP] HTTP server stopped on port " + port);
}
activeInstances.remove(port);
super.dispose();
diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF
index 4145ab2..02a73ab 100644
--- a/src/main/resources/META-INF/MANIFEST.MF
+++ b/src/main/resources/META-INF/MANIFEST.MF
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
-Plugin-Class: com.lauriewired.GhidraMCP
-Plugin-Name: GhidraMCP
-Plugin-Version: 1.0
-Plugin-Author: LaurieWired
-Plugin-Description: A custom plugin by LaurieWired
+Plugin-Class: eu.starsong.ghidra.GhydraMCP
+Plugin-Name: GhydraMCP
+Plugin-Version: 1.1
+Plugin-Author: LaurieWired, Teal Bauer
+Plugin-Description: Expose multiple Ghidra tools to MCP servers
diff --git a/src/main/resources/Module.manifest b/src/main/resources/Module.manifest
index 1aa8264..00f5a85 100644
--- a/src/main/resources/Module.manifest
+++ b/src/main/resources/Module.manifest
@@ -1,2 +1,2 @@
-GHIDRA_MODULE_NAME=GhidraMCP
-GHIDRA_MODULE_DESC=An HTTP server plugin for Ghidra
+GHIDRA_MODULE_NAME=GhydraMCP
+GHIDRA_MODULE_DESC=A multi-headed REST interface for Ghidra for use with MCP agents.
diff --git a/src/main/resources/extension.properties b/src/main/resources/extension.properties
index 409fa12..4b45921 100644
--- a/src/main/resources/extension.properties
+++ b/src/main/resources/extension.properties
@@ -1,6 +1,6 @@
-name=HydraMCP
-description=A plugin that runs an embedded HTTP server to expose program data.
-author=LaurieWired
-createdOn=2025-03-22
+name=GhydraMCP
+description=A multi-headed REST interface for Ghidra for use with MCP agents.
+author=Laurie Wired, Teal Bauer
+createdOn=2025-03-29
version=11.3.1
-ghidraVersion=11.3.1
\ No newline at end of file
+ghidraVersion=11.3.1
diff --git a/src/test/java/com/lauriewired/AppTest.java b/src/test/java/eu/starsong/ghidra/AppTest.java
similarity index 95%
rename from src/test/java/com/lauriewired/AppTest.java
rename to src/test/java/eu/starsong/ghidra/AppTest.java
index 77b1a97..481f612 100644
--- a/src/test/java/com/lauriewired/AppTest.java
+++ b/src/test/java/eu/starsong/ghidra/AppTest.java
@@ -1,4 +1,4 @@
-package com.lauriewired;
+package eu.starsong.ghidra;
import junit.framework.Test;
import junit.framework.TestCase;