MCP server that bridges Cutter reverse engineering tool with Claude via HTTP, providing binary analysis capabilities including decompilation, disassembly, function listing, and code metadata inspection
Cutter MCP has 15 tools with basic functionality but significant quality gaps. All tools are registered with @mcp.tool() decorators and have descriptions, but descriptions are minimal (averaging 60 chars, well below the 194-char production baseline). All parameters have type annotations and defaults, but lack descriptions entirely in the schema, descriptions appear only in docstrings, not in the input schema JSON. No input schemas are explicitly visible in the code; tool definitions rely on function signatures inferred by FastMCP. No output schemas are documented. Error handling returns raw HTTP status codes and strings without recovery guidance. Schema validation is absent, no enums, no format constraints, no bounds checking. The tool naming is action-oriented (list_, decompile_, search_, rename_, set_) which is good, but some descriptions are inconsistent (set_function_prototype says 'Rename a function' but the tool sets a prototype). No pagination total_count or next_cursor is returned, only offset/limit accepted. Responses from safe_get() return text.splitlines() (line-delimited), not structured JSON, forcing LLMs to parse unstructured output.
No input schemas visible in source code, tool definitions inferred from function signatures only. FastMCP may auto-generate schemas from type hints, but explicit schema JSON is not shown. Cannot verify parameter constraints, enums, or bounds.
Descriptions are minimal and lack LLM-optimized guidance. Average description length ~60 chars; production baseline is 194 chars. Descriptions do not explain WHEN to use each tool, WHAT it returns, or how it differs from similar tools (e.g., list_functions vs search_functions_by_name).
Export explicit input schemas as JSON Schema objects for each tool. Verify that FastMCP generates and exposes these schemas; if not, define them manually. Each schema must include 'type', 'description', 'enum' (for constrained params), 'minimum', 'maximum', 'pattern', and 'default' where applicable.
Expand tool descriptions to 100 - 250 characters following the LLM-optimized pattern: 'Do X [when Y] [constraint Z].' Example: 'List all functions in the binary with their memory addresses. Pagination supported via offset/limit. Returns function address and name pairs. For decompiled pseudocode, call decompile_function_by_address.'
Add parameter descriptions to the schema, not just docstrings. For each parameter, include: (1) what it controls, (2) expected format (e.g., 'hex address with 0x prefix or decimal'), (3) valid range (e.g., 'offset must be >= 0'), (4) example values (e.g., '0x1000'). Example: 'address: Memory address of the function, in hex (0x1000) or decimal (4096). Required.'
Document output schemas explicitly. For list_functions, specify: 'Returns array of objects: [{"address": string, "name": string}, ...]. Example: [{"address": "0x1000", "name": "main"}]'. For multi-line text responses (decompile_function_by_address), specify 'Returns decompiled pseudocode as a single string, one statement per line.'
Add pagination metadata to list_* tools. Modify safe_get() to parse JSON responses and include 'total_count', 'has_more', 'next_offset' in the output. Example: '{"items": [...], "total_count": 150, "offset": 0, "limit": 100, "has_more": true}'.
Score history
Overall score trend
↑ 4 points across a rubric change (v1 → v2)
38/100
Scored
Grade
Overall
Spec posture
Rubric
2026-09-22
F
38
2026-07-28+
v2
2026-03-09
F
34
-
v1
read only
source verified
43/100
List all memory segments in the program with pagination.
Parameter descriptions absent from schema, only present in function docstrings. Docstrings describe 'pagination offset' and 'number of results' but do not explain what 'address' means or what format it expects (hex string? decimal? with 0x prefix?). LLMs cannot read docstrings; they parse schema only.
No output schema documentation. Tools return strings or lists of strings (from safe_get.splitlines()), but LLMs do not know the structure of returned lines. For example, list_functions returns lines of text, are they 'address function_name' pairs? JSON? CSV? Unstructured text requires LLM parsing, which is error-prone.
Inconsistent tool descriptions. set_function_prototype docstring says 'Rename a function by its address' but the parameter is 'description' which suggests setting a prototype/signature, not renaming. This mismatch will confuse both LLMs and users.
No error recovery guidance. safe_get() and safe_post() return plain error strings like 'Error 404: Not found' or 'Request failed: Connection timeout'. LLMs have no guidance on whether to retry, call a different tool, or ask the user.
No input validation or constraints. Parameters like 'address', 'offset', 'limit' have no validation. No enums, no regex patterns, no bounds checking. An LLM can pass offset=-1 or limit=999999 without feedback.
No pagination metadata returned. list_* tools accept offset and limit but do not return total_count, has_more, or next_cursor. LLMs cannot know if they've fetched all results or if they need to paginate further.
Raw text output without structure. safe_get() returns response.text.splitlines(), which is unstructured. For example, list_functions returns a list of strings like ['0x1000 main', '0x2000 foo'] but the schema does not declare this format. LLMs must infer structure.
Improve error messages with recovery guidance. Example: Instead of 'Error 404: Not found', return 'Address 0x9999 not found. Verify the address using list_functions() or decompile_function_by_address(0x1000) to see valid addresses.' For connection timeouts, return 'Cutter server unreachable (timeout after 5s). Ensure Cutter HTTP server is running on http://127.0.0.1:8000.'
Add input validation. Before calling safe_get/safe_post, validate: (1) offset >= 0, (2) limit in range [1, 1000], (3) address matches hex or decimal pattern. Return clear validation errors: 'Invalid limit: 2000. Must be 1 - 1000.'
Add tool annotations via FastMCP. Mark read-only tools with readOnlyHint=true (list_*, decompile_*, disassemble_*, search_*, xrefs_*, get_*, show_*). Mark destructive tools with destructiveHint=true (rename_function_by_address, set_decompiler_comment, set_function_prototype).
Fix the set_function_prototype description. Change from 'Rename a function by its address' to 'Set the function signature/prototype at the given address (e.g., int main(int argc, char* argv[])).'
Convert unstructured text output to JSON. Modify safe_get() to return structured objects, not raw text lines. Parse the Cutter HTTP response and shape it into an array of typed objects with clear field names.
Add tool chaining fields. If get_function_prototype typically needs the address from list_functions, ensure list_functions returns both address and name so the agent can chain calls efficiently without extra lookups.