Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions bridge/binja_mcp_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,15 +594,25 @@ def search_functions_by_name(query: str, offset: int = 0, limit: int = 100) -> l
@mcp.tool()
def get_binary_status() -> str:
"""
Get the current status of the loaded binary.
Get the current status of the loaded binary, including the original binary name.
"""
return safe_get("status")[0]
data = get_json("status")
if not data:
return "Error: no response"
if isinstance(data, dict) and data.get("error"):
return data.get("error")
loaded = data.get("loaded", False)
if not loaded:
return "No binary loaded"
binary_name = data.get("binary_name") or "(unknown)"
filename = data.get("filename") or "(unknown)"
return f"Binary: {binary_name}\nPath: {filename}"


@mcp.tool()
def list_binaries() -> list:
"""
List managed/open binaries known to the server with ids and active flag.
List managed/open binaries known to the server with ids, binary names, and active flag.
"""
data = get_json("binaries")
if not data:
Expand All @@ -616,9 +626,11 @@ def list_binaries() -> list:
view_id = it.get("view_id")
fn = it.get("filename")
basename = it.get("basename") or ""
binary_name = it.get("binary_name") or ""
selectors = it.get("selectors") or []
active = it.get("active")
label = basename or fn or "(unknown)"
# Use binary_name as primary label, fallback to basename or filename
label = binary_name or basename or fn or "(unknown)"
full = fn or "(no filename)"
selector_text = ", ".join(str(s) for s in selectors if s)
mark = " *active*" if active else ""
Expand Down
12 changes: 8 additions & 4 deletions plugin/api/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ def __init__(self, binary_ops: BinaryOperations):

def get_status(self) -> dict[str, Any]:
"""Get the current status of the binary view"""
bv = self.binary_ops.current_view
binary_name = self.binary_ops._get_binary_name(bv) if bv else None
return {
"loaded": self.binary_ops.current_view is not None,
"filename": self.binary_ops.current_view.file.filename
if self.binary_ops.current_view
else None,
"loaded": bv is not None,
"filename": bv.file.filename if bv else None,
"binary_name": binary_name,
}

def get_entry_points(self) -> list[dict[str, Any]]:
Expand All @@ -31,11 +32,13 @@ def _format_binary_listing(self, raw: list[dict[str, Any]]) -> list[dict[str, An
filename = item.get("filename")
view_id = str(item.get("id") or "")
basename = os.path.basename(filename) if filename else None
binary_name = item.get("binary_name")
entry: dict[str, Any] = {
"id": str(ordinal),
"view_id": view_id,
"filename": filename,
"basename": basename,
"binary_name": binary_name,
"active": bool(item.get("active")),
}
selectors: list[str] = []
Expand All @@ -44,6 +47,7 @@ def _format_binary_listing(self, raw: list[dict[str, Any]]) -> list[dict[str, An
view_id,
filename,
basename,
binary_name,
):
if candidate and candidate not in selectors:
selectors.append(candidate)
Expand Down
37 changes: 33 additions & 4 deletions plugin/core/binary_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,28 @@ def _prune_views(self) -> None:
except Exception:
self._current_view = None

def _get_binary_name(self, bv: bn.BinaryView) -> str | None:
"""Get the original binary name from a BinaryView.

For project files (.bnpr), extracts the original name from project_file.name,
stripping the .bndb suffix if present. Falls back to basename of filename.
"""
import os

if bv and bv.file:
pf = getattr(bv.file, "project_file", None)
if pf:
name = getattr(pf, "name", None)
if name and name.endswith(".bndb"):
return name[:-5] # Strip .bndb
return name
# Fallback to basename of filename, handling missing filename safely
filename = getattr(bv.file, "filename", None)
if not filename:
return None
return os.path.basename(filename)
return None

def _register_view(self, bv: bn.BinaryView) -> str:
"""Add a view to the managed list if not present, return its id."""
self._prune_views()
Expand Down Expand Up @@ -194,7 +216,8 @@ def list_open_binaries(self) -> list[dict[str, str]]:
# Do NOT auto-register current_view here; UI monitor handles discovery.
# This avoids re-introducing closed views via a stale strong reference.
# Deduplicate by canonical filename; prefer the id mapped in _id_by_filename
entries: list[tuple[str, str, bool]] = [] # (id, filename, active)
# (id, filename, active, binary_view)
entries: list[tuple[str, str, bool, bn.BinaryView]] = []
seen: set[str] = set()
for vid, w in self._views_by_id.items():
try:
Expand All @@ -218,11 +241,17 @@ def list_open_binaries(self) -> list[dict[str, str]]:
vb_canon = vb_canon_ref() if vb_canon_ref else vb
except Exception:
vb_canon = vb
entries.append((canonical_id, fn, bool(vb_canon is self._current_view)))
entries.append((canonical_id, fn, bool(vb_canon is self._current_view), vb_canon))
# Sort by filename for stable ordering
entries.sort(key=lambda t: (t[1] or ""))
for cid, fn, active in entries:
items.append({"id": cid, "filename": fn, "active": active})
for cid, fn, active, bv in entries:
binary_name = self._get_binary_name(bv)
items.append({
"id": cid,
"filename": fn,
"binary_name": binary_name,
"active": active,
})
return items

def select_view(self, ident: str) -> dict[str, str] | None:
Expand Down