Skip to content

Commit 662e202

Browse files
committed
feat: add line filtering to decompile for context management
Add start_line, end_line, and max_lines parameters to decompile functions, allowing AI models to retrieve only specific portions of decompiled code for better context management. Parameters: - start_line: Start at this line number (1-indexed) - end_line: End at this line number (inclusive) - max_lines: Maximum lines to return (overrides end_line) Response includes filter metadata with total_lines when filtering is applied, helping models understand what portion they're viewing. Examples: - Get first 20 lines: max_lines=20 - Get lines 10-30: start_line=10, end_line=30 - Get 15 lines from line 25: start_line=25, max_lines=15
1 parent 60124d2 commit 662e202

File tree

2 files changed

+85
-15
lines changed

2 files changed

+85
-15
lines changed

bridge_mcp_hydra.py

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,20 +1351,35 @@ def functions_get(name: str = None, address: str = None, port: int = None) -> di
13511351
return simplify_response(response)
13521352

13531353
@mcp.tool()
1354-
def functions_decompile(name: str = None, address: str = None,
1354+
def functions_decompile(name: str = None, address: str = None,
13551355
syntax_tree: bool = False, style: str = "normalize",
1356+
start_line: int = None, end_line: int = None, max_lines: int = None,
13561357
port: int = None) -> dict:
1357-
"""Get decompiled code for a function
1358-
1358+
"""Get decompiled code for a function with optional line filtering for context management
1359+
13591360
Args:
13601361
name: Function name (mutually exclusive with address)
13611362
address: Function address in hex format (mutually exclusive with name)
13621363
syntax_tree: Include syntax tree (default: False)
13631364
style: Decompiler style (default: "normalize")
1365+
start_line: Start at this line number (1-indexed, optional)
1366+
end_line: End at this line number (inclusive, optional)
1367+
max_lines: Maximum number of lines to return (optional, takes precedence over end_line)
13641368
port: Specific Ghidra instance port (optional)
1365-
1369+
13661370
Returns:
1367-
dict: Contains function information and decompiled code
1371+
dict: Contains function information and decompiled code (potentially filtered).
1372+
If filtering is applied, includes a 'filter' object with total_lines and applied parameters.
1373+
1374+
Examples:
1375+
# Get first 20 lines of decompiled code
1376+
functions_decompile(name="main", max_lines=20)
1377+
1378+
# Get lines 10-30
1379+
functions_decompile(name="main", start_line=10, end_line=30)
1380+
1381+
# Get 15 lines starting from line 25
1382+
functions_decompile(name="main", start_line=25, max_lines=15)
13681383
"""
13691384
if not name and not address:
13701385
return {
@@ -1375,22 +1390,30 @@ def functions_decompile(name: str = None, address: str = None,
13751390
},
13761391
"timestamp": int(time.time() * 1000)
13771392
}
1378-
1393+
13791394
port = _get_instance_port(port)
1380-
1395+
13811396
params = {
13821397
"syntax_tree": str(syntax_tree).lower(),
13831398
"style": style
13841399
}
1385-
1400+
1401+
# Add line filtering parameters if provided
1402+
if start_line is not None:
1403+
params["start_line"] = str(start_line)
1404+
if end_line is not None:
1405+
params["end_line"] = str(end_line)
1406+
if max_lines is not None:
1407+
params["max_lines"] = str(max_lines)
1408+
13861409
if address:
13871410
endpoint = f"functions/{address}/decompile"
13881411
else:
13891412
endpoint = f"functions/by-name/{quote(name)}/decompile"
1390-
1413+
13911414
response = safe_get(port, endpoint, params)
13921415
simplified = simplify_response(response)
1393-
1416+
13941417
return simplified
13951418

13961419
@mcp.tool()

src/main/java/eu/starsong/ghidra/endpoints/FunctionEndpoints.java

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,20 +1090,67 @@ public void handleDecompileFunction(HttpExchange exchange, Function function) th
10901090
String style = params.getOrDefault("style", "normalize");
10911091
String format = params.getOrDefault("format", "structured");
10921092
int timeout = parseIntOrDefault(params.get("timeout"), 30);
1093-
1093+
1094+
// Line filtering parameters for context management
1095+
int startLine = parseIntOrDefault(params.get("start_line"), -1);
1096+
int endLine = parseIntOrDefault(params.get("end_line"), -1);
1097+
int maxLines = parseIntOrDefault(params.get("max_lines"), -1);
1098+
10941099
// Decompile function
10951100
String decompilation = GhidraUtil.decompileFunction(function);
1096-
1101+
1102+
// Apply line filtering if requested
1103+
String filteredDecompilation = decompilation;
1104+
int totalLines = 0;
1105+
if (decompilation != null) {
1106+
String[] lines = decompilation.split("\n");
1107+
totalLines = lines.length;
1108+
1109+
// Apply line range filtering
1110+
if (startLine > 0 || endLine > 0 || maxLines > 0) {
1111+
int start = startLine > 0 ? Math.max(0, startLine - 1) : 0;
1112+
int end = endLine > 0 ? Math.min(lines.length, endLine) : lines.length;
1113+
1114+
// If maxLines is specified, limit the range
1115+
if (maxLines > 0) {
1116+
end = Math.min(end, start + maxLines);
1117+
}
1118+
1119+
if (start < lines.length) {
1120+
StringBuilder filtered = new StringBuilder();
1121+
for (int i = start; i < end && i < lines.length; i++) {
1122+
if (i > start) {
1123+
filtered.append("\n");
1124+
}
1125+
filtered.append(lines[i]);
1126+
}
1127+
filteredDecompilation = filtered.toString();
1128+
} else {
1129+
filteredDecompilation = "// No lines in specified range";
1130+
}
1131+
}
1132+
}
1133+
10971134
// Create function info
10981135
Map<String, Object> functionInfo = new HashMap<>();
10991136
functionInfo.put("address", function.getEntryPoint().toString());
11001137
functionInfo.put("name", function.getName());
1101-
1138+
11021139
// Create the result structure according to GHIDRA_HTTP_API.md
11031140
Map<String, Object> result = new HashMap<>();
11041141
result.put("function", functionInfo);
1105-
result.put("decompiled", decompilation != null ? decompilation : "// Decompilation failed");
1106-
1142+
result.put("decompiled", filteredDecompilation != null ? filteredDecompilation : "// Decompilation failed");
1143+
1144+
// Add metadata about line filtering if applied
1145+
if (startLine > 0 || endLine > 0 || maxLines > 0) {
1146+
Map<String, Object> filterInfo = new HashMap<>();
1147+
filterInfo.put("total_lines", totalLines);
1148+
if (startLine > 0) filterInfo.put("start_line", startLine);
1149+
if (endLine > 0) filterInfo.put("end_line", endLine);
1150+
if (maxLines > 0) filterInfo.put("max_lines", maxLines);
1151+
result.put("filter", filterInfo);
1152+
}
1153+
11071154
// Add syntax tree if requested
11081155
if (syntaxTree) {
11091156
result.put("syntax_tree", "Syntax tree not implemented");

0 commit comments

Comments
 (0)