Skip to content
This repository was archived by the owner on Nov 16, 2025. It is now read-only.

Commit 46aeb40

Browse files
committed
Add batch and analysis tools for Ghidra MCP v1.5.x
Introduces batch operations and analysis tools for comments, functions, labels, structs, types, and variables in the Ghidra MCP bridge. Adds new Python tools for batch setting comments, renaming function components, creating labels, analyzing struct field usage, suggesting field names, and batch setting variable types. Implements corresponding Java handlers for finding the next undefined function and batch setting variable types. Improves documentation and validation throughout the tool modules. Also updates server shutdown behavior for cleaner resource release.
1 parent cbee790 commit 46aeb40

10 files changed

Lines changed: 754 additions & 81 deletions

File tree

bridge_mcp_ghidra/tools/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from .labels import register_label_tools
1212
from .misc import register_misc_tools
1313
from .namespaces import register_namespace_tools
14-
from .security import register_security_tools
1514
from .strings import register_string_tools
1615
from .structs import register_struct_tools
1716
from .types import register_type_tools
@@ -32,7 +31,6 @@ def register_all_tools():
3231
register_label_tools()
3332
register_misc_tools()
3433
register_namespace_tools()
35-
register_security_tools()
3634
register_string_tools()
3735
register_struct_tools()
3836
register_type_tools()

bridge_mcp_ghidra/tools/comments.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,37 @@
44
def register_comment_tools(mcp: FastMCP):
55
"""Register comment tools for Ghidra context."""
66

7+
@mcp.tool()
8+
def batch_set_comments(
9+
function_address: str,
10+
decompiler_comments: list = None,
11+
disassembly_comments: list = None,
12+
plate_comment: str = None
13+
) -> str:
14+
"""
15+
Set multiple comments in a single operation (v1.5.0).
16+
Reduces API calls from 10+ to 1 for typical function documentation.
17+
18+
Args:
19+
function_address: Function address for plate comment
20+
decompiler_comments: List of {"address": "0x...", "comment": "..."} for PRE_COMMENT
21+
disassembly_comments: List of {"address": "0x...", "comment": "..."} for EOL_COMMENT
22+
plate_comment: Function header summary comment
23+
24+
Returns:
25+
JSON with success status and counts of comments set
26+
"""
27+
validate_hex_address(function_address)
28+
29+
payload = {
30+
"function_address": function_address,
31+
"decompiler_comments": decompiler_comments or [],
32+
"disassembly_comments": disassembly_comments or [],
33+
"plate_comment": plate_comment
34+
}
35+
36+
return ghidra_context.http_client.safe_post_json("batch_set_comments", payload)
37+
738
@mcp.tool()
839
def set_decompiler_comment(address: str, comment: str) -> str:
940
"""
@@ -39,3 +70,25 @@ def set_disassembly_comment(address: str, comment: str) -> str:
3970
raise GhidraValidationError(f"Invalid hexadecimal address: {address}")
4071

4172
return ghidra_context.http_client.safe_post("set_disassembly_comment", {"address": address, "comment": comment})
73+
74+
75+
@mcp.tool()
76+
def set_plate_comment(
77+
function_address: str,
78+
comment: str
79+
) -> str:
80+
"""
81+
Set function plate (header) comment (v1.5.0).
82+
This comment appears above the function in both disassembly and decompiler views.
83+
84+
Args:
85+
function_address: Function address in hex format (e.g., "0x401000")
86+
comment: Function header summary comment
87+
88+
Returns:
89+
Success or failure message
90+
"""
91+
validate_hex_address(function_address)
92+
93+
params = {"function_address": function_address, "comment": comment}
94+
return ghidra_context.http_client.safe_post("set_plate_comment", params)

bridge_mcp_ghidra/tools/functions.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,65 @@
11
from mcp.server.fastmcp import FastMCP
2-
from ..context import ghidra_context, GhidraValidationError, validate_function_name, validate_hex_address
2+
from ..context import ghidra_context, GhidraValidationError, validate_hex_address
33

44
def register_function_tools(mcp: FastMCP):
55
"""Register function-related tools to the FastMCP instance."""
66

7+
@mcp.tool()
8+
def analyze_function_completeness(
9+
function_address: str
10+
) -> str:
11+
"""
12+
Analyze how completely a function has been documented (v1.5.0).
13+
Checks for custom names, prototypes, comments, and undefined variables.
14+
15+
Args:
16+
function_address: Function address in hex format
17+
18+
Returns:
19+
JSON with completeness analysis including:
20+
- has_custom_name, has_prototype, has_calling_convention
21+
- has_plate_comment, undefined_variables
22+
- completeness_score (0-100)
23+
"""
24+
validate_hex_address(function_address)
25+
26+
params = {"function_address": function_address}
27+
return ghidra_context.http_client.safe_get("analyze_function_completeness", params)
28+
29+
@mcp.tool()
30+
def batch_rename_function_components(
31+
function_address: str,
32+
function_name: str = None,
33+
parameter_renames: dict = None,
34+
local_renames: dict = None,
35+
return_type: str = None
36+
) -> str:
37+
"""
38+
Rename function and all its components atomically (v1.5.0).
39+
Combines multiple rename operations into a single transaction.
40+
41+
Args:
42+
function_address: Function address in hex format
43+
function_name: New name for the function (optional)
44+
parameter_renames: Dict of {"old_name": "new_name"} for parameters
45+
local_renames: Dict of {"old_name": "new_name"} for local variables
46+
return_type: New return type (optional)
47+
48+
Returns:
49+
JSON with success status and counts of renamed components
50+
"""
51+
validate_hex_address(function_address)
52+
53+
payload = {
54+
"function_address": function_address,
55+
"function_name": function_name,
56+
"parameter_renames": parameter_renames or {},
57+
"local_renames": local_renames or {},
58+
"return_type": return_type
59+
}
60+
61+
return ghidra_context.http_client.safe_post_json("batch_rename_function_components", payload)
62+
763
@mcp.tool()
864
def create_function_signature(name: str, return_type: str, parameters: str = None) -> str:
965
"""
@@ -66,6 +122,37 @@ def disassemble_function(address: str) -> list:
66122

67123
return ghidra_context.http_client.safe_get("disassemble_function", {"address": address})
68124

125+
@mcp.tool()
126+
def find_next_undefined_function(
127+
start_address: str = None,
128+
criteria: str = "name_pattern",
129+
pattern: str = "FUN_",
130+
direction: str = "ascending"
131+
) -> str:
132+
"""
133+
Find the next function needing analysis (v1.5.0).
134+
Intelligently searches for functions matching specified criteria.
135+
136+
Args:
137+
start_address: Starting address for search (default: program min address)
138+
criteria: Search criteria (default: "name_pattern")
139+
pattern: Name pattern to match (default: "FUN_")
140+
direction: Search direction "ascending" or "descending" (default: "ascending")
141+
142+
Returns:
143+
JSON with found function details or {"found": false}
144+
"""
145+
if start_address:
146+
validate_hex_address(start_address)
147+
148+
params = {
149+
"start_address": start_address,
150+
"criteria": criteria,
151+
"pattern": pattern,
152+
"direction": direction
153+
}
154+
return ghidra_context.http_client.safe_get("find_next_undefined_function", params)
155+
69156
@mcp.tool()
70157
def get_current_function() -> str:
71158
"""

bridge_mcp_ghidra/tools/labels.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,47 @@
44
def register_label_tools(mcp: FastMCP):
55
"""Register label-related tools in the MCP instance."""
66

7+
@mcp.tool()
8+
def batch_create_labels(labels: list) -> str:
9+
"""
10+
Create multiple labels in a single atomic operation (v1.5.1).
11+
12+
This tool creates multiple labels in one transaction, dramatically reducing API calls
13+
and preventing user interruption hooks from triggering repeatedly. This is the
14+
preferred method for creating multiple labels during function documentation.
15+
16+
Performance impact:
17+
- Reduces N API calls to 1 call
18+
- Prevents interruption after each label creation
19+
- Atomic transaction ensures all-or-nothing semantics
20+
21+
Args:
22+
labels: List of label objects, each with "address" and "name" fields
23+
Example: [{"address": "0x6faeb266", "name": "begin_slot_processing"},
24+
{"address": "0x6faeb280", "name": "loop_check_slot_active"}]
25+
26+
Returns:
27+
JSON string with success status, counts, and any errors:
28+
{"success": true, "labels_created": 5, "labels_skipped": 1, "labels_failed": 0}
29+
"""
30+
if not labels or not isinstance(labels, list):
31+
raise GhidraValidationError("labels must be a non-empty list")
32+
33+
# Validate each label entry
34+
for i, label in enumerate(labels):
35+
if not isinstance(label, dict):
36+
raise GhidraValidationError(f"Label at index {i} must be a dictionary")
37+
38+
if "address" not in label or "name" not in label:
39+
raise GhidraValidationError(f"Label at index {i} must have 'address' and 'name' fields")
40+
41+
if not validate_hex_address(label["address"]):
42+
raise GhidraValidationError(f"Invalid hexadecimal address at index {i}: {label['address']}")
43+
44+
return safe_post_json("batch_create_labels", {
45+
"labels": labels
46+
})
47+
748
@mcp.tool()
849
def create_label(address: str, name: str) -> str:
950
"""
@@ -49,12 +90,29 @@ def rename_data(address: str, new_name: str) -> str:
4990
"""
5091
Rename a data label at the specified address.
5192
93+
IMPORTANT: This tool only works for DEFINED data (data with an existing symbol/type).
94+
For undefined memory addresses, use create_label() or rename_or_label() instead.
95+
96+
What is "defined data"?
97+
- Data that has been typed (e.g., dword, struct, array)
98+
- Data created via apply_data_type() or Ghidra's "D" key
99+
- Data with existing symbols in the Symbol Tree
100+
101+
If you get an error like "No defined data at address", use:
102+
- create_label(address, name) for undefined addresses
103+
- rename_or_label(address, name) for automatic detection (recommended)
104+
52105
Args:
53106
address: Memory address in hex format (e.g., "0x1400010a0")
54107
new_name: New name for the data label
55-
108+
56109
Returns:
57110
Success or failure message indicating the result of the rename operation
111+
112+
See Also:
113+
- create_label(): Create label at undefined address
114+
- rename_or_label(): Automatically detect and use correct method
115+
- apply_data_type(): Define data type before renaming
58116
"""
59117

60118
if not validate_hex_address(address):

0 commit comments

Comments
 (0)