From 662e202482a74867dcfbaf4a3f592d80d38234d6 Mon Sep 17 00:00:00 2001 From: Teal Bauer Date: Fri, 14 Nov 2025 17:49:19 +0100 Subject: [PATCH] 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 --- bridge_mcp_hydra.py | 43 ++++++++++---- .../ghidra/endpoints/FunctionEndpoints.java | 57 +++++++++++++++++-- 2 files changed, 85 insertions(+), 15 deletions(-) diff --git a/bridge_mcp_hydra.py b/bridge_mcp_hydra.py index 4cd476e..af14c1f 100644 --- a/bridge_mcp_hydra.py +++ b/bridge_mcp_hydra.py @@ -1351,20 +1351,35 @@ def functions_get(name: str = None, address: str = None, port: int = None) -> di return simplify_response(response) @mcp.tool() -def functions_decompile(name: str = None, address: str = None, +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 { @@ -1375,22 +1390,30 @@ def functions_decompile(name: str = None, address: str = None, }, "timestamp": int(time.time() * 1000) } - + port = _get_instance_port(port) - + params = { "syntax_tree": str(syntax_tree).lower(), "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: endpoint = f"functions/by-name/{quote(name)}/decompile" - + response = safe_get(port, endpoint, params) simplified = simplify_response(response) - + return simplified @mcp.tool() diff --git a/src/main/java/eu/starsong/ghidra/endpoints/FunctionEndpoints.java b/src/main/java/eu/starsong/ghidra/endpoints/FunctionEndpoints.java index 565e7b7..05a2323 100644 --- a/src/main/java/eu/starsong/ghidra/endpoints/FunctionEndpoints.java +++ b/src/main/java/eu/starsong/ghidra/endpoints/FunctionEndpoints.java @@ -1090,20 +1090,67 @@ public class FunctionEndpoints extends AbstractEndpoint { String style = params.getOrDefault("style", "normalize"); 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 functionInfo = new HashMap<>(); functionInfo.put("address", function.getEntryPoint().toString()); functionInfo.put("name", function.getName()); - + // Create the result structure according to GHIDRA_HTTP_API.md Map 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 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) { result.put("syntax_tree", "Syntax tree not implemented");