Model Context Protocol · read-only · HTTP

One MCP server.
A whole team's live IPAM.

Engineers ask their AI assistant which IPs are free in a subnet, and it answers from NetBox — right now, not from a stale copy in a wiki. Nobody installs anything. The API token never leaves the server.

In production on a telecom infrastructure engineering team

The gap between what you know and what is true

Infrastructure teams keep two kinds of knowledge, and they rot in different ways.

Structured data — subnets, addresses, VLANs — lives in NetBox. Accurate, queryable, and reachable only through a web UI or a REST API. Mid-incident, an engineer opens a browser tab and squints.

Narrative knowledge — runbooks, incident write-ups, the reason a VLAN exists — lives in a wiki. Engineers now read this through AI assistants, which is fast and pleasant.

So the assistant can reason about why the network looks like it does, but cannot see what it currently is.

The tempting fix

Copy the IP addresses into the documentation, so the assistant can read them.

docs/network.md
10.20.30.7 — mail relay
// reassigned in March.
// nobody noticed.
What this does instead

Give the assistant a way to ask the source of truth directly, at the moment the question is asked.

docs/network.md
see NetBox → VLAN 41
// always current.
// or it says it can't reach it.

A copied live value goes stale silently. That is the whole argument. Six months later the assistant confidently quotes an address that was reassigned in March, and it has no way to know it is wrong. A tool call either returns today's answer or fails loudly.

What it looks like in use

Real tools, real shapes. Pick a question.

assistant → netbox-mcp → NetBox

Addresses shown are from an illustrative lab range, not any production network.

Why HTTP, and why one server

The default way to ship MCP is stdio. For a team, that is a security posture, not a convenience choice.

With stdio, the assistant spawns the process locally. Every engineer installs Python, clones a repo, and pastes an API token into a config file on their laptop. Now the token exists on N laptops. Rotating it means chasing N people. Revoking access for someone who left means hoping they deleted a dotfile.

ENGINEERS AI assistant AI assistant AI assistant no token no install HTTP /mcp netbox-mcp systemd · one process NETBOX_TOKEN (read-only) the token lives here. exactly here. GET only NetBox IPAM / DCIM source of truth

Serving MCP over HTTP inverts every one of those properties:

The trade-off, stated plainly. The endpoint is now a network service and must be treated as one. It listens on an internal network or a private mesh — never the public internet — and it carries no authentication of its own. Network reachability is the access control. If that assumption does not hold for you, put it behind a reverse proxy with auth.

Read-only by construction, not by convention

The single HTTP helper in this server issues GET and nothing else. There is no code path that writes to NetBox, so an assistant cannot be talked into deleting a prefix, however the prompt is phrased.

Pair that with a read-only NetBox token and the property survives a bug. Two independent mechanisms, because one of them will eventually be a mistake.

Failure is a sentence, not a stack trace

An unguarded MCP tool hands the assistant a Python traceback, and the assistant hands the operator a shrug. Every tool here is wrapped so transport failures come back as something a human can act on:

NetBox rejected the token (401/403). Check NETBOX_TOKEN.
NetBox is unreachable at http://netbox.internal:8090 (Connection refused).
NetBox did not answer within 20s.

This matters more than it sounds. An incident is exactly when your tooling is most likely to be degraded, and exactly when a confusing error costs the most.

Five tools, and four ways to fail in a full sentence.

An incident is exactly when your tooling is most likely to be degraded — so every failure here comes back as something an operator can act on, not a traceback. Pick a scenario and watch the path run.

AI assistant plain language MCP tool 5 tools · all read _api() method="GET", hardcoded NetBox source of truth _guard error → sentence VLAN10 MGMT 200 · rows of text, not JSON Check NETBOX_TOKEN. HTTPError 401 / 403 NetBox returned HTTP 500 HTTPError · any other code NetBox is unreachable URLError · connection refused No answer within 20s TimeoutError

Five tools

The docstring is the interface — the assistant reads it to choose. Each is written for someone who has never seen NetBox, with a concrete example.

ToolWhat it answers
list_prefixesEvery documented subnet, with its VLAN and description.
prefix_reportFor one subnet: how many IPs are documented, and the first free ones.
find_ipSearch by DNS name, description or address fragment.
subnet_contentsEvery documented IP inside a subnet, in address order.
list_vlansKnown VLANs, ordered by VLAN ID.

Adding one is a decorated function

Three rules keep the assistant using it correctly: GET only (the read-only guarantee is the whole security model); the docstring is the API (write it for someone who does not know your schema); and return text a human could read, not JSON — the assistant will paraphrase it anyway, and readable output makes a hallucinated field obvious.

Install

Python 3.10+, a reachable NetBox, and a read-only API token.

git clone https://github.com/msemino/netbox-mcp
cd netbox-mcp
python3 -m venv venv && ./venv/bin/pip install -r requirements.txt

cp .env.example .env    # set NETBOX_URL and a read-only NETBOX_TOKEN
chmod 600 .env
set -a && . ./.env && set +a && ./venv/bin/python src/server.py

Then point an assistant at it:

claude mcp add --transport http netbox http://netbox-mcp.internal:8097/mcp

A systemd unit with a hardened sandbox ships in deploy/.