From c9a4fdf49c9821a5dc6d0ffae503d76ae4320751 Mon Sep 17 00:00:00 2001 From: Jeff McJunkin Date: Wed, 4 Feb 2026 20:02:00 -0800 Subject: [PATCH 1/2] Add binary name retrieval for project files Extract original binary name from BinaryView.file.project_file.name, stripping .bndb suffix when present. This fixes display issues when using Binary Ninja projects (.bnpr) where file.filename returns internal project paths instead of the original binary name. Changes: - Add _get_binary_name() helper in binary_operations.py - Include binary_name in get_status() and list_open_binaries() - Update MCP tools to prominently display binary names Co-Authored-By: Claude Opus 4.5 --- bridge/binja_mcp_bridge.py | 20 +++++++++++++++---- plugin/api/endpoints.py | 12 +++++++---- plugin/core/binary_operations.py | 34 ++++++++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/bridge/binja_mcp_bridge.py b/bridge/binja_mcp_bridge.py index a59efe9..d6ba849 100755 --- a/bridge/binja_mcp_bridge.py +++ b/bridge/binja_mcp_bridge.py @@ -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: @@ -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 "" diff --git a/plugin/api/endpoints.py b/plugin/api/endpoints.py index 08c2b5d..0ba0d4c 100644 --- a/plugin/api/endpoints.py +++ b/plugin/api/endpoints.py @@ -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]]: @@ -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] = [] @@ -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) diff --git a/plugin/core/binary_operations.py b/plugin/core/binary_operations.py index 93f0900..18717ee 100644 --- a/plugin/core/binary_operations.py +++ b/plugin/core/binary_operations.py @@ -111,6 +111,25 @@ 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 + return os.path.basename(bv.file.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() @@ -194,7 +213,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: @@ -218,11 +238,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: From 2525b569bd7acea4a08078dd76e9608227982f2b Mon Sep 17 00:00:00 2001 From: Jeff McJunkin Date: Thu, 5 Feb 2026 10:40:45 -0800 Subject: [PATCH 2/2] Update plugin/core/binary_operations.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- plugin/core/binary_operations.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugin/core/binary_operations.py b/plugin/core/binary_operations.py index 18717ee..1b3aaf9 100644 --- a/plugin/core/binary_operations.py +++ b/plugin/core/binary_operations.py @@ -126,8 +126,11 @@ def _get_binary_name(self, bv: bn.BinaryView) -> str | None: if name and name.endswith(".bndb"): return name[:-5] # Strip .bndb return name - # Fallback to basename of filename - return os.path.basename(bv.file.filename) + # 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: