Forward ghidra()/ghidra_tools() to the shared read-only Ghidra MCP server

Every agent reaches GhidrAssistMCP through Harbor's own MCP server, so no
per-host configuration is needed. Configured by ghidra_url in [reverse];
long decompiles are polled; also 'harbor re ghidra <tool> <json>'.
This commit is contained in:
KeelrLeek committed 2026-10-05 12:27:19 -05:00
1 parent 3f6df88f95
commit 7ed914ebdb
4 files changed
+186 -6

No files matched your search

+34 -1
View File
@@ -35,7 +35,9 @@ review queue, presence and stop flags. Protocol:
9. A correction is send(..., corrects=<id of your earlier message>).
10. Before your session ends or your allowance runs out, write handoff(): finished, in flight, next.
11. To read the original machine code use re_disassemble, re_xrefs, re_bytes and re_lookup: they read the project's
own image with the team's current names, so do not write your own disassembler or offset maths."""
own image with the team's current names, so do not write your own disassembler or offset maths.
For Ghidra's view (decompiler, p-code, stack frames, struct field references, types) use ghidra_tools() once,
then ghidra(tool, arguments): one shared read-only Ghidra session holding the team's annotations."""
KindName = Literal["note", "question", "answer", "request", "finding", "retraction", "decision", "status", "handoff"]
Importance = Literal["normal", "high", "urgent"]
@@ -484,6 +486,37 @@ def re_lookup(target: str) -> dict:
return _re(lambda r: r.lookup(target))
_ghidra = {}
def _gh(fn):
try:
h = harbor()
g = _ghidra.get(h.project.name)
if g is None:
from .reverse import Ghidra
g = _ghidra[h.project.name] = Ghidra(h.project.reverse)
return fn(g)
except (HarborError, projects.ProjectError) as error:
raise ToolError(str(error)) from None
@mcp.tool()
def ghidra_tools() -> list:
"""The tools of the team's shared Ghidra session (GhidrAssistMCP, read-only, the curated project with every
applied annotation): name, description and arguments. Call them with ghidra(tool, arguments)."""
return _gh(lambda g: g.tools())
@mcp.tool()
def ghidra(tool: str, arguments: dict | None = None) -> str:
"""Run one tool in the shared read-only Ghidra session, e.g. ghidra('get_code', {'function': 'GetFogLevel',
'format': 'decompiler'}), ghidra('get_function_stack_layout', {'function_name_or_address': '0x4ADC70'}),
ghidra('struct', {'action': 'field_xrefs', ...}). Long decompiles are waited for. Renames, retypes and other
changes are refused: names and types change through annotations/ and land on main."""
return _gh(lambda g: g.call(tool, arguments or {}))
def _report_in():
"""Tell Harbor this agent's app has its tools running: at start, then every 5 minutes while the app keeps the
server open (updates the connection only, not the agent's last-seen time)."""