Skip to content

Adding External Tools with MCP

Sidekick can load tools from external Model Context Protocol (MCP) servers. This lets you give the agent capabilities beyond its built-in toolset — file system access, web search, database queries, or any custom tool you write — without modifying Sidekick itself.

MCP tools appear alongside built-in tools in the chat. They go through the same tool permission system, so you approve or reject MCP tool calls the same way you would any other tool.


How it works

  1. You describe your MCP servers and which tools to expose in the MCP Servers dialog (or directly in a JSON configuration file).
  2. When Sidekick starts, it launches each configured server, queries it for available tools, and registers them.
  3. The agent can then call those tools during a conversation. The first call starts a server session, and later calls reuse it while it remains healthy.

MCP servers communicate over stdio — Sidekick starts the server as a subprocess and exchanges JSON-RPC messages over stdin/stdout. Any program that speaks the MCP stdio transport can be a tool server.

Sidekick uses only the MCP tools feature, with one JSON message per line and jsonrpc set to "2.0". A server must meet these requirements:

  • Reply to initialize with a protocolVersion of 2024-11-05, 2025-03-26, 2025-06-18, or 2025-11-25, a capabilities.tools object, and a serverInfo with non-empty name and version.
  • Return tools from tools/list that each have a unique, non-empty name and an object inputSchema.

Sidekick answers ping requests from the server but rejects other server-initiated requests (such as sampling or roots) with a method-not-found error. Resources and prompts are ignored.


Configuring servers in Sidekick

Choose Plugins > Sidekick > Configure MCP Servers... to add and edit servers without editing JSON by hand. The MCP Servers Configuration dialog lists your servers and shows the selected server's settings. Each server in the list shows a status of Disabled, Invalid, Verified, or Error.

  • Click Add Server to create a server (named new-mcp-server-1, new-mcp-server-2, and so on). Right-click a server for View Server and Delete Server.
  • Discover Tools starts the selected server and lists the tools it reports.
  • Validate checks the configuration.
  • Save writes the configuration file. It is enabled only when there are unsaved changes, and it refuses to save while validation errors remain.
  • Revert discards unsaved changes.
  • Open JSON opens mcp_tools_config.json in your editor. It is available only when there are no unsaved changes.

Note

Changes apply after reopening the workspace or restarting Binary Ninja.

Server editor

The selected server has three tabs.

Tab Contents
Server Enabled, Server Name, Server Tags (comma-separated; blank defaults to mcp), Timeout (seconds) (1 to 600), a Command (argv) table with one row per argument, and an Environment table. These map to enabled, name, tags, server_config.timeout, server_config.command, and server_config.env in the schema below.
Tools A table with the columns Enabled, Tool Name, Alias, Tags Override, Effective Tags, State, and Description. Enable All, Disable All, and Remove Selected act on the table.
Validation The config path, validation errors, the discovery result, and the server's stderr output.

Discovery keeps your configured tools and appends any newly reported tools as disabled; tick Enabled to expose a tool. State shows Verified (reported by the server at the last discovery), Missing from server, or Unverified (no successful discovery yet). Remove Selected drops the tool's saved entry.


Creating the configuration file

Place a file named mcp_tools_config.json in Sidekick's config directory:

$BN_USER_DIRECTORY/sidekick/config/mcp_tools_config.json

See Data Storage for how this path is resolved.

On a fresh plugin install, Sidekick creates this file for you if it is missing and adds a disabled sample_mcp_server entry that points at a bundled sample server copied into $BN_USER_DIRECTORY/sidekick/mcp_servers/mcp_test_server.py. If you already have an MCP config file, Sidekick leaves it untouched.

Minimal example

{
  "version": "1.0",
  "servers": [
    {
      "name": "my_tools",
      "server_config": {
        "command": ["python3", "/path/to/my_mcp_server.py"],
        "timeout": 30
      },
      "tools": [
        { "name": "tool_a", "enabled": true },
        { "name": "tool_b", "enabled": true }
      ],
      "enabled": true
    }
  ]
}

Full schema

The top level of the file has a version string ("1.0"; include it as shown in the examples) and a servers array. Each entry in servers describes one MCP server:

Field Type Default Description
name string (required) A unique display name for this server, shown in the MCP Servers dialog. Empty or duplicate names are rejected when saving there. It does not affect tool names.
enabled boolean true Set to false to skip this server entirely without removing it from the file.
tags string[] ["mcp"] Capability tags assigned to every tool from this server, unless overridden per-tool. See Tags and access control below.
server_config object (required) How to start the server.
server_config.command string[] (required) The command and arguments to start the MCP server process.
server_config.env object {} Additional environment variables passed to the server process. Merged with Binary Ninja's environment, except that inherited variables beginning with PYTHON (such as PYTHONHOME and PYTHONPATH) are removed so an external Python server does not pick up Binary Ninja's interpreter settings. Set them here explicitly if you need them.
server_config.timeout number 30 Timeout in seconds for each MCP request. Graceful shutdown stages use this value, capped at 5 seconds.
tools array [] Which tools from this server to expose. If omitted or empty, nothing is exposed.

Each entry in tools selects a tool by the name the server reports:

Field Type Default Description
name string (required) The tool name as reported by the server's tools/list response.
alias string same as name An alternative name to use in Sidekick. Useful if two servers expose tools with the same name. Each enabled tool must end up with a name (its alias, or its name if no alias) that is unique across all servers and does not match a built-in Sidekick tool. If names collide, only one of the tools is available and the duplicate is skipped (a warning appears in the log). The MCP Servers dialog reports duplicate names among enabled MCP tools as validation errors and will not save until they are fixed. It does not check names against built-in Sidekick tools, so give any tool that matches a built-in name an alias.
tags string[] (inherited from server) Override tags for this specific tool. Replaces server-level tags when present.
enabled boolean true Set to false to hide this tool without removing it from the list.

Multiple servers

You can configure as many servers as you need. Each can use a different runtime:

{
  "version": "1.0",
  "servers": [
    {
      "name": "filesystem",
      "tags": ["mcp.filesystem"],
      "server_config": {
        "command": ["uvx", "mcp-server-filesystem", "/home/user/allowed"]
      },
      "tools": [
        { "name": "read_file" },
        { "name": "list_directory", "alias": "list_dir" }
      ]
    },
    {
      "name": "git",
      "tags": ["mcp.git"],
      "server_config": {
        "command": ["uvx", "mcp-server-git", "--repository", "/path/to/repo"],
        "env": { "GIT_CONFIG_GLOBAL": "/dev/null" }
      },
      "tools": [
        { "name": "git_log" },
        { "name": "git_diff" }
      ]
    }
  ]
}

Tags and access control

Every tool in Sidekick is registered with capability tags that control which agents can see it. Built-in tools use tags like database, notebook, and python.binja. MCP tools default to the tag mcp.

The agent you chat with, in both the Workspace Chat and your work chats, imports the mcp tag, so it can see all MCP tools that carry the default tag. The specialist agents it delegates to do not import mcp by default, so MCP tools are available only to the agent you are chatting with, not to delegated sub-agents.

Custom tags

If you want finer control, assign more specific tags in the config:

{
  "name": "filesystem",
  "tags": ["mcp.filesystem"],
  ...
}

Tag matching is hierarchical: the agent imports mcp, which matches mcp, mcp.filesystem, mcp.git, and any other mcp.* tag. If you ever needed to restrict an agent to only filesystem MCP tools, you could configure that agent to import mcp.filesystem instead of the broad mcp.

Warning

Keep custom tags under mcp (for example mcp.filesystem). Agents import the mcp tag, so a tool tagged with something outside that hierarchy (for example just filesystem) is not offered to the agent. Tags set on a single tool replace, rather than add to, the server tags.

Tool permissions

MCP tools go through the same approval system as built-in tools. By default, the agent must request approval before calling an MCP tool. You can pre-approve MCP tools by tag or by name in Plugins > Sidekick > Configure Tool Permissions... — see Understanding Tool Permissions for details.


Writing a simple MCP server

An MCP server is any process that reads JSON-RPC messages from stdin and writes responses to stdout. Here is a minimal Python example that exposes two tools:

#!/usr/bin/env python3
"""Minimal MCP tool server."""

import json
import sys
from datetime import datetime, timezone

TOOLS = [
    {
        "name": "echo",
        "description": "Returns the text you send it.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "text": {"type": "string", "description": "Text to echo back"}
            },
            "required": ["text"]
        }
    },
    {
        "name": "get_time",
        "description": "Returns the current UTC time.",
        "inputSchema": {"type": "object", "properties": {}}
    },
]

def handle(msg):
    method = msg.get("method")
    rid = msg.get("id")

    # Notifications (no id) are silently ignored
    if rid is None:
        return

    if method == "initialize":
        respond(rid, {
            "protocolVersion": msg["params"]["protocolVersion"],
            "capabilities": {"tools": {}},
            "serverInfo": {"name": "my-server", "version": "1.0.0"}
        })
    elif method == "tools/list":
        respond(rid, {"tools": TOOLS})
    elif method == "tools/call":
        name = msg["params"]["name"]
        args = msg["params"].get("arguments", {})
        if name == "echo":
            respond(rid, {"content": [{"type": "text", "text": args.get("text", "")}]})
        elif name == "get_time":
            now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
            respond(rid, {"content": [{"type": "text", "text": now}]})
        else:
            error(rid, -32601, f"Unknown tool: {name}")
    else:
        error(rid, -32601, f"Unknown method: {method}")

def respond(rid, result):
    sys.stdout.write(json.dumps({"jsonrpc": "2.0", "id": rid, "result": result}) + "\n")
    sys.stdout.flush()

def error(rid, code, message):
    sys.stdout.write(json.dumps({"jsonrpc": "2.0", "id": rid, "error": {"code": code, "message": message}}) + "\n")
    sys.stdout.flush()

while True:
    line = sys.stdin.readline()
    if not line:
        break
    line = line.strip()
    if line:
        handle(json.loads(line))

Save this as my_mcp_server.py and reference it in your config:

{
  "version": "1.0",
  "servers": [
    {
      "name": "my_tools",
      "server_config": {
        "command": ["python3", "/path/to/my_mcp_server.py"]
      },
      "tools": [
        { "name": "echo" },
        { "name": "get_time" }
      ]
    }
  ]
}

After reopening the workspace or restarting Binary Ninja, the agent will be able to call echo and get_time during chat conversations.


Using community MCP servers

The MCP ecosystem includes many ready-made servers. Any server that supports the stdio transport works with Sidekick. Some popular examples:

  • mcp-server-filesystem — read, write, and search files in allowed directories
  • mcp-server-git — query git repositories (log, diff, status)

Install with uvx (or npx for Node.js servers), then add the command to your config. Refer to each server's documentation for the command syntax and available tools.


Troubleshooting

Tools do not appear in chat

  • Open Plugins > Sidekick > Configure MCP Servers..., select the server, and click Discover Tools. The Validation tab shows the discovery error (for example a handshake timeout or an invalid tools/list response) and the tail of the server's stderr output.
  • Verify the config file is at $BN_USER_DIRECTORY/sidekick/config/mcp_tools_config.json.
  • Check that the server command works standalone: run it in a terminal and paste a JSON-RPC initialize message to stdin.
  • Check the Sidekick log for Loaded N MCP tool(s) messages at startup. If the server failed to start or the handshake timed out, errors will appear in the log.
  • Confirm the server and tool entries have "enabled": true (or omit the field — it defaults to true).

Tool calls fail at runtime

  • Sidekick reuses the server subprocess across tool calls while the process is alive. A new process is started (and the MCP handshake is re-run) on the next call if none exists yet, if the previous process has exited, or if the previous call failed with a timeout or protocol error (Sidekick shuts the server down after such a failure). The configured timeout applies to initialization, discovery, and tool calls. Failures are logged as Error communicating with MCP server, including the tail of the server's stderr.
  • During the handshake Sidekick sends an initialize request and waits for the server's response, then sends a notifications/initialized notification to the server. The server does not respond to the notification; it is a one-way signal that the client is ready. Only after this sequence are tools/call messages sent.
  • The server may send valid MCP notifications or requests before a response. It must not write non-MCP logging or debug text to stdout; send diagnostics to stderr instead.
  • On shutdown Sidekick closes the server's stdin and waits for it to exit before escalating to SIGTERM and then SIGKILL.

Some tools from a large server are missing

Sidekick follows nextCursor values until every tools/list page has been loaded. Check the log for malformed responses, repeated cursors, or configured tool names that are absent from the complete discovery result.

Permission denied

  • MCP tools default to requiring approval. If you want the agent to call them without asking, add an allow rule for the mcp tag in Plugins > Sidekick > Configure Tool Permissions....