Skip to content
Draft
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
26 changes: 24 additions & 2 deletions binaryninjacore.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@
// Current ABI version for linking to the core. This is incremented any time
// there are changes to the API that affect linking, including new functions,
// new types, or modifications to existing functions or types.
#define BN_CURRENT_CORE_ABI_VERSION 192
#define BN_CURRENT_CORE_ABI_VERSION 193

// Minimum ABI version that is supported for loading of plugins. Plugins that
// are linked to an ABI version less than this will not be able to load and
// will require rebuilding. The minimum version is increased when there are
// incompatible changes that break binary compatibility, such as changes to
// existing types or functions.
#define BN_MINIMUM_CORE_ABI_VERSION 192
#define BN_MINIMUM_CORE_ABI_VERSION 193

#define BN_DEMANGLER_MSVC "MS"
#define BN_DEMANGLER_GNU3 "GNU3"
Expand Down Expand Up @@ -348,6 +348,7 @@ extern "C"
typedef struct BNUndoEntry BNUndoEntry;
typedef struct BNDemangler BNDemangler;
typedef struct BNMcpTool BNMcpTool;
typedef struct BNMcpToolGroup BNMcpToolGroup;
typedef struct BNMcpToolCall BNMcpToolCall;
typedef struct BNMcpToolResult BNMcpToolResult;
typedef struct BNFirmwareNinja BNFirmwareNinja;
Expand Down Expand Up @@ -4109,8 +4110,17 @@ extern "C"
const char* outputSchema;
BNMcpToolScope scope;
uint32_t annotations;
BNMcpToolGroup* group;
} BNMcpToolDefinition;

typedef struct BNMcpToolGroupDefinition
{
const char* name;
const char* title;
const char* description;
bool enabledByDefault;
} BNMcpToolGroupDefinition;

typedef struct BNMcpToolCallbacks
{
void* context;
Expand Down Expand Up @@ -8954,6 +8964,18 @@ extern "C"
// MCP tools
BINARYNINJACOREAPI BNMcpTool* BNRegisterMcpTool(
const BNMcpToolDefinition* definition, const BNMcpToolCallbacks* callbacks);
BINARYNINJACOREAPI BNMcpToolGroup* BNRegisterMcpToolGroup(const BNMcpToolGroupDefinition* definition);
BINARYNINJACOREAPI BNMcpToolGroup* BNNewMcpToolGroupReference(BNMcpToolGroup* group);
BINARYNINJACOREAPI void BNFreeMcpToolGroup(BNMcpToolGroup* group);
BINARYNINJACOREAPI BNMcpToolGroup** BNGetMcpToolGroupList(size_t* count);
BINARYNINJACOREAPI void BNFreeMcpToolGroupList(BNMcpToolGroup** groups, size_t count);
BINARYNINJACOREAPI BNMcpToolGroup* BNGetMcpToolGroupByName(const char* name);
BINARYNINJACOREAPI char* BNGetMcpToolGroupName(BNMcpToolGroup* group);
BINARYNINJACOREAPI char* BNGetMcpToolGroupTitle(BNMcpToolGroup* group);
BINARYNINJACOREAPI char* BNGetMcpToolGroupDescription(BNMcpToolGroup* group);
BINARYNINJACOREAPI bool BNIsMcpToolGroupEnabledByDefault(BNMcpToolGroup* group);
BINARYNINJACOREAPI bool BNIsMcpToolGroupEnabled(BNMcpToolGroup* group);
BINARYNINJACOREAPI BNMcpToolGroup* BNGetMcpToolGroup(BNMcpTool* tool);
BINARYNINJACOREAPI BNMcpTool* BNNewMcpToolReference(BNMcpTool* tool);
BINARYNINJACOREAPI void BNFreeMcpTool(BNMcpTool* tool);
BINARYNINJACOREAPI BNMcpTool** BNGetMcpToolList(size_t* count);
Expand Down
10 changes: 5 additions & 5 deletions docs/dev/mcp-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Plugins can add tools to Binary Ninja's [MCP server](../guide/mcp.md). Both the
A tool has a name, a description, a JSON Schema describing its input, and a handler that receives the arguments and returns a result.

- **Names** must be 1 to 64 letters, digits, `_` or `-`, and unique in the process. Prefix yours with your plugin's name, such as `myplugin_find_crypto`. The `bn_` prefix is used by Binary Ninja's built-in tools.
- **Scope.** A BinaryView-scoped tool, the default, runs against the MCP session's active BinaryView. The server resolves that view before the handler runs and reports `no_active_binary_view` itself when there is none, so the handler can rely on having one. A global tool runs without a view, and can still ask for one if the session has it.
- **Scope.** A BinaryView-scoped tool, the default, requires a `binary` handle on each call. The server resolves that view before the handler runs and reports `binary_not_found` when it cannot, so the handler can rely on having one. A global tool runs without a view.
- **Annotations** mark a tool as read-only, destructive, idempotent or open-world. Clients use them to decide how to present a tool and whether to ask the user before running it. Every hint is sent, so a tool without a flag is advertised as one that may modify its environment, is not destructive, is not idempotent, and does not reach outside Binary Ninja.
- **Results** are text, structured content (a JSON object), or both. When a tool returns structured content and no text, clients that ignore structured content see the JSON as text. An error result carries a machine-readable `errorCode` and an `errorMessage` as structured content. A tool with an output schema returns them as text instead, since the error does not match the schema.
- **Return one representation.** Some MCP clients show the model only the structured content, and others only the text. A tool that returns both must put everything the model needs in each, so prefer returning one or the other.
Expand All @@ -33,7 +33,7 @@ def myplugin_find_strings(
min_length: int = 4,
section: Optional[str] = None,
) -> dict:
"""Find strings in the active binary view.
"""Find strings in the selected binary view.

:param min_length: Minimum string length in bytes.
:param section: Only search this section.
Expand Down Expand Up @@ -86,7 +86,7 @@ using namespace BinaryNinja::MCP;

extern "C" BINARYNINJAPLUGIN bool CorePluginInit()
{
MakeTool({"myplugin_set_comment", "Set Comment", "Set the comment at an address in the active binary view.",
MakeTool({"myplugin_set_comment", "Set Comment", "Set the comment at an address in the selected binary view.",
Scope::BinaryView, IdempotentHint})
.Param(Address("address", "Address expression of the comment."))
.Param(String("text", "Comment text."))
Expand Down Expand Up @@ -136,7 +136,7 @@ struct FunctionCount;

impl McpTool for FunctionCount {
fn definition(&self) -> McpToolDefinition {
McpToolInfo::new("myplugin_function_count", "Count the functions in the active binary view.")
McpToolInfo::new("myplugin_function_count", "Count the functions in the selected binary view.")
.with_annotations(McpToolAnnotations::READ_ONLY)
.with_input_schema(empty_input_schema())
}
Expand Down Expand Up @@ -171,7 +171,7 @@ impl TypedMcpTool for SetComment {
type Args = CommentArgs;

fn info(&self) -> McpToolInfo {
McpToolInfo::new("myplugin_set_comment", "Set the comment at an address in the active binary view.")
McpToolInfo::new("myplugin_set_comment", "Set the comment at an address in the selected binary view.")
}

fn invoke(&self, call: &McpToolCall, args: CommentArgs) -> Result<McpToolResult, McpToolError> {
Expand Down
56 changes: 29 additions & 27 deletions docs/guide/mcp.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MCP Server

Binary Ninja can expose analysis data to MCP clients through the Model Context Protocol (MCP). This allows an AI assistant or other MCP client to inspect open files, open binaries, run analysis updates, and query read-only BinaryView information such as functions, symbols, strings, sections, disassembly, IL, and decompiled text.
Binary Ninja can expose analysis data to MCP clients through the Model Context Protocol (MCP). This allows an AI assistant or other MCP client to open binaries, run analysis updates, inspect functions, symbols, strings, sections, disassembly, IL, and decompiled text, and edit analysis data such as comments, names, and types.

Binary Ninja provides two MCP server variants:

Expand All @@ -13,17 +13,20 @@ Binary Ninja provides two MCP server variants:

The exact tool list may change as the MCP server develops, but both server variants expose tools in the following broad categories:

- **File and view management**: list open files and databases, open a file or database, close or save an open item, list BinaryViews, and select the active BinaryView.
- **Binary and project management**: list, open, save, and close binaries and databases, list and open projects, and open files within projects.
- **Analysis control**: inspect analysis status, start an analysis update, run analysis and wait for completion, or abort active analysis work.
- **Binary overview**: request a compact triage summary of the active BinaryView, including entry point, segment, section, symbol, function, string, and data variable counts.
- **Binary overview**: request a compact triage summary of a BinaryView, including entry point, segment, section, symbol, function, string, and data variable counts.
- **Program structure**: list entry points, segments, sections, symbols, imports, exports, relocations, data variables, and strings.
- **Memory inspection**: read bytes from the active BinaryView and receive the result as hex and base64.
- **Memory inspection**: read bytes from a BinaryView and receive the result as hex and base64.
- **Function inspection**: list and search functions, request function metadata, render disassembly, render decompiled Pseudo C, render IL, inspect basic blocks, callers, callees, cross-references, stack layout, and complexity metrics.
- **Analysis editing**: manage comments, symbols, variables, function prototypes, types, data variables, and sections.

Use your MCP client's tool listing UI or command to see the complete set of tools available in your installed Binary Ninja version.

Plugins can add their own tools, which both server variants offer alongside the built-in ones. See [Writing MCP Tools](../dev/mcp-tools.md).

Tool groups are controlled by boolean settings under `mcp.groups`. Built-in groups are enabled by default. Disabling a group hides its tools and rejects calls to them. Reconnect your MCP client after changing these settings so it loads the updated tool list.

## Tool Calling Conventions

The MCP server exposes Binary Ninja state through a small set of identifiers and conventions. These are worth understanding because they differ from many REST APIs.
Expand All @@ -32,51 +35,51 @@ The MCP server exposes Binary Ninja state through a small set of identifiers and

Open files, projects, databases, and BinaryViews are identified by opaque handles returned from MCP tools:

- `openItem` handles identify files, databases, or projects. Tools such as `bn_open_item_save` and `bn_open_item_close` use these handles.
- `binaryView` handles identify BinaryViews for an open item. Use `bn_binary_view_list` to discover them and `bn_binary_view_set_active` to select one.
- `binary` handles identify BinaryViews for open files and databases. Get them from `bn_binary_open` or `bn_binary_list`.
- `project` handles identify open projects. Get them from `bn_project_open` or `bn_project_list`.

Do not construct handles yourself. Treat them as session-scoped values returned by the server.
Do not construct handles yourself. They remain valid while the corresponding binary or project is open in the server process.

Some BinaryViews may be listed as available but not yet created. Selecting one with `bn_binary_view_set_active` may create that view. When multiple BinaryViews are available for a file, use the `recommended` field from `bn_binary_view_list` or choose the view type you need before running read-only analysis tools.
The `viewTypes` metadata lists available view types for a file. Pass `viewType` to `bn_binary_open` to request a particular view type.

### Active BinaryView
### Target BinaryView

Most read-only tools operate on the active BinaryView rather than taking a `binaryView` parameter every time. If a tool returns `no_active_binary_view`, first call `bn_binary_view_list`, choose a view, and call `bn_binary_view_set_active`.
Every BinaryView-scoped tool requires a `binary` handle on each call. If a tool returns `binary_not_found`, call `bn_binary_list` and supply a valid handle.

In the GUI server, the active BinaryView follows the Binary Ninja UI unless an MCP client explicitly changes it. In the headless server, the active BinaryView is managed entirely through MCP tools.
Both server variants use this per-call target. In the GUI, the target can be any open binary, regardless of which tab has focus.

### Address Expressions

Address parameters are strings, not JSON numbers. They are parsed as Binary Ninja expressions in the active BinaryView, so clients can use plain addresses, symbols, and expressions supported by Binary Ninja.
Address parameters are strings, not JSON numbers. They are parsed as Binary Ninja expressions in the selected BinaryView, so clients can use plain addresses, symbols, and expressions supported by Binary Ninja.

Binary-view-domain numeric parameters, such as byte counts for memory reads and ranges, accept either JSON integers or strings when the tool input schema advertises them. JSON integers are consumed directly. String values are parsed as Binary Ninja expressions in the active BinaryView. Binary Ninja expression syntax applies to string values; use `0n` for decimal strings when needed, for example `0n64`.
Binary-view-domain numeric parameters, such as byte counts for memory reads and ranges, accept either JSON integers or strings when the tool input schema advertises them. JSON integers are consumed directly. String values are parsed as Binary Ninja expressions in the selected BinaryView. Binary Ninja expression syntax applies to string values; use `0n` for decimal strings when needed, for example `0n64`.

Examples:

```json
{"address": "0x401000", "length": 64}
{"binary": "<binary handle>", "address": "0x401000", "length": 64}
```

```json
{"function": "main"}
{"binary": "<binary handle>", "function": "main"}
```

```json
{"start": ".text", "length": "0n256"}
{"binary": "<binary handle>", "start": ".text", "length": "0n256"}
```

Use address expressions for fields named `address`, `start`, `end`, `function`, `symbol`, `comment`, and `datavar`. Use JSON integers or expression strings for byte-span input parameters such as `length` when the tool input schema advertises them. A `length` output column is returned metadata and does not imply that the tool accepts a `length` input. Metadata parameters such as pagination `offset` and `limit`, symbol `ordinal`, and collection caps such as `maxItems` are JSON integers only.

Function tools identify a function by its start address expression:

```json
{"function": "0x401000"}
{"binary": "<binary handle>", "function": "0x401000"}
```

If more than one function has the same start address, pass `arch` with the architecture name returned by function-listing tools:

```json
{"function": "0x401000", "arch": "x86_64"}
{"binary": "<binary handle>", "function": "0x401000", "arch": "x86_64"}
```

### Ranges, Queries, and Pagination
Expand All @@ -99,21 +102,21 @@ Addresses in tool output are formatted as hexadecimal strings so clients do not

## GUI Server

The GUI MCP server is hosted inside the Binary Ninja application. It uses HTTP only and must be both enabled and started before a client can connect. Some MCP clients refer to this as an HTTP or streamable HTTP server.
The GUI MCP server is hosted inside the Binary Ninja application. It uses HTTP only and must be started before a client can connect. Some MCP clients refer to this as an HTTP or streamable HTTP server.

On Windows, use this built-in HTTP server for native MCP clients. Binary Ninja does not yet ship a standalone `binaryninja_mcp.exe` in Windows packages.

### Enable and Start
### Start the Server

1. Open Settings with `[CMD/CTRL] ,`.
2. Enable `ui.mcp.enabled`. This setting requires a restart.
3. Optionally configure the HTTP settings:
2. Optionally configure the HTTP settings:
- `ui.mcp.port`: HTTP port. Default: `24642`; use `0` for an OS-assigned port
- `ui.mcp.endpoint`: HTTP endpoint path. Default: `/mcp`
- `ui.mcp.token`: Optional bearer token. Leave blank to disable HTTP authorization
4. Restart Binary Ninja.
5. Start the server with `Plugins > MCP > Start Server`.
6. Use `Plugins > MCP > Copy Connection Info` to copy the exact URL and authorization header for your current session.
3. Start the server with `Plugins > MCP > Start Server`.
4. Use `Plugins > MCP > Copy Connection Info` to copy the exact URL and authorization header for your current session.

Enable `ui.mcp.enabled` to start the server automatically the next time Binary Ninja launches.

Use `Plugins > MCP > Stop Server` to stop the server.

Expand Down Expand Up @@ -143,7 +146,7 @@ If `ui.mcp.port` is `0`, the operating system chooses a port when the server sta

The standalone `binaryninja_mcp` server is for headless operation and uses stdio only. Configure your MCP client to launch `binaryninja_mcp` as a local command-line MCP server.

The headless server opens and analyzes files without the GUI and exposes the same file manager and read-only BinaryView inspection tools as the GUI server.
The headless server opens and analyzes files without the GUI and exposes the same built-in tools as the GUI server.

Native Windows packages do not yet include `binaryninja_mcp.exe`. On Windows, connect to the built-in GUI MCP HTTP server instead. If you require the headless stdio server, install and run the Linux build of Binary Ninja under WSL and use the Linux `binaryninja_mcp` executable from that environment.

Expand Down Expand Up @@ -267,7 +270,6 @@ Set `BN_MCP_TOKEN` to the value of `ui.mcp.token`. Omit `bearer_token_env_var` i

If an MCP client cannot connect to the GUI server:

- Confirm `ui.mcp.enabled` is enabled.
- Confirm the server has been started with `Plugins > MCP > Start Server`.
- Use `Plugins > MCP > Copy Connection Info` and copy the exact URL and token header into the client configuration.
- If you changed `ui.mcp.port` or `ui.mcp.endpoint`, restart the GUI MCP server.
Expand Down
75 changes: 75 additions & 0 deletions mcp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Ref<Tool> CreateToolUsing(const ToolDefinition& definition, const std::string& i
apiDefinition.outputSchema = definition.outputSchema.empty() ? nullptr : definition.outputSchema.c_str();
apiDefinition.scope = static_cast<BNMcpToolScope>(definition.scope);
apiDefinition.annotations = definition.annotations;
apiDefinition.group = definition.group ? definition.group->GetObject() : nullptr;

auto* context = new ToolHandler(std::move(handler));
BNMcpToolCallbacks callbacks = {};
Expand Down Expand Up @@ -252,6 +253,9 @@ std::vector<Ref<Tool>> Tool::GetList()

Ref<Tool> Tool::GetByName(const std::string& name)
{
if (name.find('\0') != std::string::npos)
return nullptr;

BNMcpTool* tool = BNGetMcpToolByName(name.c_str());
return tool ? new Tool(tool) : nullptr;
}
Expand Down Expand Up @@ -856,3 +860,74 @@ ArgumentResult<const rapidjson::Value*> JsonValue::Convert(
{
return &value;
}


Ref<ToolGroup> Tool::GetGroup() const
{
return new ToolGroup(BNGetMcpToolGroup(m_object));
}


ToolGroup::ToolGroup(BNMcpToolGroup* group)
{
m_object = group;
}


Ref<ToolGroup> ToolGroup::Register(const GroupDefinition& definition)
{
BNMcpToolGroupDefinition group = {
definition.name.c_str(), definition.title.c_str(), definition.description.c_str(), definition.enabledByDefault};
BNMcpToolGroup* handle = BNRegisterMcpToolGroup(&group);
return handle ? new ToolGroup(handle) : nullptr;
}


std::vector<Ref<ToolGroup>> ToolGroup::GetList()
{
size_t count = 0;
BNMcpToolGroup** groups = BNGetMcpToolGroupList(&count);
std::vector<Ref<ToolGroup>> result;
result.reserve(count);
for (size_t i = 0; i < count; i++)
result.push_back(new ToolGroup(BNNewMcpToolGroupReference(groups[i])));
BNFreeMcpToolGroupList(groups, count);
return result;
}


Ref<ToolGroup> ToolGroup::GetByName(const std::string& name)
{
BNMcpToolGroup* group = BNGetMcpToolGroupByName(name.c_str());
return group ? new ToolGroup(group) : nullptr;
}


std::string ToolGroup::GetName() const
{
return TakeString(BNGetMcpToolGroupName(m_object));
}


std::string ToolGroup::GetTitle() const
{
return TakeString(BNGetMcpToolGroupTitle(m_object));
}


std::string ToolGroup::GetDescription() const
{
return TakeString(BNGetMcpToolGroupDescription(m_object));
}


bool ToolGroup::IsEnabledByDefault() const
{
return BNIsMcpToolGroupEnabledByDefault(m_object);
}


bool ToolGroup::IsEnabled() const
{
return BNIsMcpToolGroupEnabled(m_object);
}
Loading
Loading