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
This commit is contained in:
Teal Bauer 2025-11-14 17:49:19 +01:00
parent 60124d2315
commit 662e202482
2 changed files with 85 additions and 15 deletions

View File

@ -1353,18 +1353,33 @@ def functions_get(name: str = None, address: str = None, port: int = None) -> di
@mcp.tool()
def functions_decompile(name: str = None, address: str = None,
syntax_tree: bool = False, style: str = "normalize",
start_line: int = None, end_line: int = None, max_lines: int = None,
port: int = None) -> dict:
"""Get decompiled code for a function
"""Get decompiled code for a function with optional line filtering for context management
Args:
name: Function name (mutually exclusive with address)
address: Function address in hex format (mutually exclusive with name)
syntax_tree: Include syntax tree (default: False)
style: Decompiler style (default: "normalize")
start_line: Start at this line number (1-indexed, optional)
end_line: End at this line number (inclusive, optional)
max_lines: Maximum number of lines to return (optional, takes precedence over end_line)
port: Specific Ghidra instance port (optional)
Returns:
dict: Contains function information and decompiled code
dict: Contains function information and decompiled code (potentially filtered).
If filtering is applied, includes a 'filter' object with total_lines and applied parameters.
Examples:
# Get first 20 lines of decompiled code
functions_decompile(name="main", max_lines=20)
# Get lines 10-30
functions_decompile(name="main", start_line=10, end_line=30)
# Get 15 lines starting from line 25
functions_decompile(name="main", start_line=25, max_lines=15)
"""
if not name and not address:
return {
@ -1383,6 +1398,14 @@ def functions_decompile(name: str = None, address: str = None,
"style": style
}
# Add line filtering parameters if provided
if start_line is not None:
params["start_line"] = str(start_line)
if end_line is not None:
params["end_line"] = str(end_line)
if max_lines is not None:
params["max_lines"] = str(max_lines)
if address:
endpoint = f"functions/{address}/decompile"
else:

View File

@ -1091,9 +1091,46 @@ public class FunctionEndpoints extends AbstractEndpoint {
String format = params.getOrDefault("format", "structured");
int timeout = parseIntOrDefault(params.get("timeout"), 30);
// Line filtering parameters for context management
int startLine = parseIntOrDefault(params.get("start_line"), -1);
int endLine = parseIntOrDefault(params.get("end_line"), -1);
int maxLines = parseIntOrDefault(params.get("max_lines"), -1);
// Decompile function
String decompilation = GhidraUtil.decompileFunction(function);
// Apply line filtering if requested
String filteredDecompilation = decompilation;
int totalLines = 0;
if (decompilation != null) {
String[] lines = decompilation.split("\n");
totalLines = lines.length;
// Apply line range filtering
if (startLine > 0 || endLine > 0 || maxLines > 0) {
int start = startLine > 0 ? Math.max(0, startLine - 1) : 0;
int end = endLine > 0 ? Math.min(lines.length, endLine) : lines.length;
// If maxLines is specified, limit the range
if (maxLines > 0) {
end = Math.min(end, start + maxLines);
}
if (start < lines.length) {
StringBuilder filtered = new StringBuilder();
for (int i = start; i < end && i < lines.length; i++) {
if (i > start) {
filtered.append("\n");
}
filtered.append(lines[i]);
}
filteredDecompilation = filtered.toString();
} else {
filteredDecompilation = "// No lines in specified range";
}
}
}
// Create function info
Map<String, Object> functionInfo = new HashMap<>();
functionInfo.put("address", function.getEntryPoint().toString());
@ -1102,7 +1139,17 @@ public class FunctionEndpoints extends AbstractEndpoint {
// Create the result structure according to GHIDRA_HTTP_API.md
Map<String, Object> result = new HashMap<>();
result.put("function", functionInfo);
result.put("decompiled", decompilation != null ? decompilation : "// Decompilation failed");
result.put("decompiled", filteredDecompilation != null ? filteredDecompilation : "// Decompilation failed");
// Add metadata about line filtering if applied
if (startLine > 0 || endLine > 0 || maxLines > 0) {
Map<String, Object> filterInfo = new HashMap<>();
filterInfo.put("total_lines", totalLines);
if (startLine > 0) filterInfo.put("start_line", startLine);
if (endLine > 0) filterInfo.put("end_line", endLine);
if (maxLines > 0) filterInfo.put("max_lines", maxLines);
result.put("filter", filterInfo);
}
// Add syntax tree if requested
if (syntaxTree) {