mcpField
B
batfish-q-mcp

batfish-q-mcp

Local
github.com/martimyCloud & Infra0Updated 1d ago

About batfish-q-mcp

batfish-q-mcp is an MCP server for querying network configuration snapshots through Batfish. It is intended for network operators and AI assistants, offering curated questions, assertions, failure simulations, and topology data without requiring users to write Batfish queries.

Key features

  1. 01Run a curated catalog of 60 Batfish questions
  2. 02Execute single assertions or YAML suites
  3. 03Fork snapshots with failed nodes or interfaces
  4. 04Compare snapshots with differential questions
  5. 05Return layer 3 and routing topologies

Use cases

  1. 01Check configuration parsing and routing properties
  2. 02Test network assertions against a snapshot
  3. 03Assess reachability after node or interface failures
  4. 04Inspect layer 3 or BGP routing topology

MCP support

Exposes Batfish snapshot, question, assertion, simulation, and topology operations as MCP tools. The stdio server connects to a running Batfish instance and returns structured results to MCP clients.

Configuration

Local server config is the same for every client. Copy it into your client’s MCP config file.

mcp.json · Universal config
{
"mcpServers": {
"martimy-batfish-q": {
"command": "uvx",
"args": [
"batfish-q-mcp"
],
"env": {
"BATFISH_SERVER": "<BATFISH_SERVER>"
}
}
}
}
Run commanduvx batfish-q-mcp
Environment variables to set
BATFISH_SERVERHost of the Batfish server (default 127.0.0.1)

Local servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.

Packages
pypibatfish-q-mcp0.2.0

Tools

9

Saved tool list · updated today

ParameterTypeRequiredDescription
categoryanyNo

Details

From the project README

batfish-q-mcp

An MCP (Model Context Protocol) server that exposes Batfish as tools for AI assistants. Point it at a snapshot of your network configs and ask questions in plain language, without writing Batfish queries yourself.

With it, an assistant can:

  • run any of 60 curated Batfish questions (node properties, routing, ACLs, reachability, parse issues, and more)
  • run assertions, such as "no forwarding loops", singly or as a YAML suite
  • fork a snapshot with failed nodes or interfaces and diff the result against the original
  • return layer 3 and routing topology as nodes and edges

This project is an offshoot of Bat-Q, a Streamlit GUI over Batfish. See Acknowledgements.

Example prompts

Once the server is connected to your MCP client and a snapshot is loaded, try:

  • "List the snapshots and tell me whether the configs parsed cleanly."
  • "Are there any forwarding loops in this snapshot?"
  • "Show me the layer 3 topology."
  • "What happens to reachability from r2 to r3 if r1 fails?"
  • "Which nodes have BGP configured, and what are their peers?"

Behind the scenes the assistant calls the tools listed below.

Quick start

1. Start Batfish

The server needs a running Batfish instance. The quickest way is Docker:

docker run --rm -p 9996:9996 -p 9997:9997 batfish/allinone

The server connects to 127.0.0.1 by default. If Batfish runs elsewhere, set BATFISH_SERVER (for example BATFISH_SERVER=<batfish-host>).

2. Install batfish-q-mcp

Requires Python >=3.10.

git clone https://github.com/martimy/batfish-q-mcp.git
cd batfish-q-mcp

# Recommended: use a virtual environment
python3 -m venv .batmcp
source .batmcp/bin/activate
pip install -e .

Alternatives:

pip install -r requirements.txt
pip install -e ".[dev]"   # includes test dependencies

This installs a console script, batfish-q-mcp, which starts the MCP server over stdio.

3. Connect your MCP client
Claude Desktop, Cursor, and other stdio clients
{
  "mcpServers": {
    "batfish-q-mcp": {
      "command": "batfish-q-mcp",
      "env": { "BATFISH_SERVER": "127.0.0.1" }
    }
  }
}

If you installed into a virtual environment, use the absolute path to the script instead (find it with which batfish-q-mcp while the environment is active):

{
  "mcpServers": {
    "batfish-q-mcp": {
      "command": "/path/to/batfish-q-mcp/.batmcp/bin/batfish-q-mcp",
      "env": { "BATFISH_SERVER": "127.0.0.1" }
    }
  }
}

Restart the client so it picks up the new server.

OpenCode

OpenCode uses opencode.json (schema https://opencode.ai/config.json) with mcp.servers, type: "local", and command given as an array:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "servers": {
      "batfish-q-mcp": {
        "type": "local",
        "command": ["/path/to/batfish-q-mcp/.batmcp/bin/batfish-q-mcp"],
        "environment": {
          "BATFISH_SERVER": "127.0.0.1"
        }
      }
    }
  }
}

Put the file in your project root (or ~/.config/opencode/), then start or restart OpenCode from that directory so it reloads MCP servers.

Note: OpenCode Code Mode isolates tools. There are no direct batfish_* tools; call them through the execute tool, for example:

await tools["batfish-q-mcp"].batfish_list_questions({});
4. Load a snapshot and ask

Package your configs as a snapshot zip (see Providing a snapshot), put it in your workspace, and tell the assistant the filename. The assistant uploads it and runs the questions you ask.

A fresh server process has no Batfish network set yet. Pass network (for example "NETWORK") on the first call that needs snapshots; it then stays set for the life of the process. Calling batfish_list_snapshots without network fails with Network must be set.

Running the server manually (optional)

To check that the server starts outside a client:

export BATFISH_SERVER=127.0.0.1
batfish-q-mcp
# or
python -m batfish_mcp.server

It speaks stdio JSON-RPC (FastMCP) and does not print logs to stdout.

Providing a snapshot

  • Format: one Batfish zip containing configs, plus optional batfish/ and hosts/ folders. For example, abc_net.zip contains abc_net/configs/R1.cfg, R2.cfg, R3.cfg, FW.cfg and hosts/server_*.json.

  • Where: drop the zip in your workspace and tell the assistant the filename.

  • Upload: the assistant base64-encodes the zip and calls batfish_upload_snapshot(name, zip_base64, network?, overwrite?).

    • name is sanitized (safe_snapshot_name). Collisions are made unique (name_1, ...) unless overwrite=true.
    • zip_base64 must be valid base64 with correct padding (= or ==). Validation is enforced server-side.
  • Already on the Batfish server: give the assistant the snapshot and network names; no upload is needed.

  • Shell access: you can also upload directly with pybatfish:

    PYTHONPATH=src python3 - <<'PY'
    import os
    from pybatfish.client.session import Session
    bf = Session(host=os.getenv("BATFISH_SERVER", "127.0.0.1"))
    bf.set_network("NETWORK")
    bf.init_snapshot("abc_net.zip", name="abc_net", overwrite=True)
    print(bf.list_snapshots())
    PY
    

Tools

The server provides 9 tools. batfish_run_question gives access to the full 60-question catalog.

ToolDescription
batfish_list_questions(category?)The 60-question curated catalog, with input types and TYPE_HINTS descriptions. No Batfish server needed.
batfish_list_snapshots(network?)Snapshots of a network.
batfish_upload_snapshot(name, zip_base64, network?, overwrite?)Upload a snapshot zip (base64). The name is sanitized (safe_snapshot_name); collisions are made unique (name_1, ...).
batfish_run_question(fun, options?, snapshot?, reference_snapshot?, network?)Run any catalog question. Giving reference_snapshot and snapshot runs a differential question. Returns JSON-safe {fun, snapshot, reference_snapshot, status, rows, removed_columns}.
batfish_list_assertions()Live pybatfish assertion names plus the 16-entry registry (specs) with parameter descriptions.
batfish_run_assertion(name, params?, question?, snapshot?, network?)Validate and run one assertion (answer-based ones fetch the answer first). Returns {type, status, message, snapshot, ran_at, runtime, fun?, answer_rows?}.
batfish_run_assertion_suite(suite_yaml, snapshot?, network?)Validate (all-or-nothing) and run a YAML suite; examples/assertions.yaml shows the schema. Returns results[], errors[], summary.
batfish_fork_snapshot(base_name, name?, failed_nodes?, failed_interfaces?, overwrite?, network?)Fork a snapshot, optionally failing nodes or interfaces, then diff the fork against the base with a differential question.
batfish_get_topology(kind, snapshot?, network?)kind="layer3" (from layer3Edges, with interface titles) or "routing" (from bgpEdges). Returns {kind, snapshot, node_count, edge_count, nodes, edges}.

All tools share one Batfish session (batfish_mcp.session, using BATFISH_SERVER, default 127.0.0.1), serialized with a lock.

Tool call examples

For reference, this is roughly what the assistant calls when you ask the example prompts. Upload abc_net.zip, then:

batfish_upload_snapshot(name="abc_net.zip", zip_base64="UEsDBB==", network="NETWORK")
batfish_run_question(fun="nodeProperties", snapshot="abc_net")
batfish_run_assertion(name="assert_no_forwarding_loops", snapshot="abc_net")
batfish_fork_snapshot(base_name="abc_net", failed_nodes=["r1"])
batfish_run_question(fun="differentialReachability",
    options={"pathConstraints": {"startLocation": "r2", "endLocation": "r3"},
             "headers": {"dstIps": "192.168.3.1"}},
    snapshot="abc_net_Fail", reference_snapshot="abc_net")

Topology:

batfish_get_topology(kind="layer3", snapshot="abc_net", network="NETWORK")
batfish_get_topology(kind="routing", snapshot="abc_net", network="NETWORK")

In OpenCode, wrap these in execute, for example:

await tools["batfish-q-mcp"].batfish_run_question({
  fun: "initIssues", snapshot: "abc_net", network: "NETWORK"
});

See HOW-TO.md for a longer walkthrough.

Troubleshooting

  • Network must be set: pass network="NETWORK" on the first call.
  • zip_base64 is not valid base64: Incorrect padding: re-encode with base64 -w 0 abc_net.zip (no newlines) and keep the trailing = or ==. Do not trim by hand.
  • Snapshot already exists: pass overwrite: true, or accept the name_1 suffix.
  • No parse issues but wrong IPs? Run batfish_run_question(fun="initIssues") and fileParseStatus first.
  • Reachability to 240.254.254.1 fails but 8.8.8.8 works: 240.0.0.0/4 is reserved, and an ISP egress filter such as Block outgoing traffic using reserved addresses drops it. Use a public IP to test internet egress.

Layout

src/batfish_mcp/
  server.py           # FastMCP tools over the headless core
  session.py          # thread-safe singleton Session
  catalog.py          # curated question catalog (60 questions)
  assertion_specs.py  # assertion registry (params as {name, type, optional})
  assertion_io.py     # assertion YAML load/validate/dump
  assertions.py       # assertion runner (session- vs answer-based, PASS/FAIL/ERROR)
  queries.py          # question runner (incl. differential snapshots)
  utils.py            # snapshot-name sanitizing, template helpers
  presenter.py        # pure DataFrame/trace serialization helpers
  hints.py            # TYPE_HINTS: tool-parameter descriptions
  topology.py         # L3 + routing graph construction from answers
tests/
  fakes.py            # FakeBatfish + assertion fake (no server needed)
  test_queries.py
  test_assertions.py
  test_server.py      # MCP tools + wire round-trip
examples/assertions.yaml
HOW-TO.md             # mini tutorial

Tests

pip install -e ".[dev]"
pytest          # no Batfish server required (fakes + one in-memory MCP round-trip)

tests/test_assertions.py also checks that assertion_specs.py stays in sync with the installed pybatfish (scripted-assertion signatures).

Acknowledgements

batfish-q-mcp started as an offshoot of Bat-Q, a Streamlit GUI over Batfish. The reusable headless core (question catalog, assertion registry and runner, answer serialization, snapshot tooling) came from there; the GUI layer was dropped and replaced with the MCP transport. Both projects are Apache-2.0.

License

Apache-2.0 (see LICENSE).

Comments

Markdown supported

New to MCP?

From what the protocol is to installing your first server, these answers cover most first questions.

An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.