Agentic Model Context Protocol server for NetBox — read-only, tool-rich, built for LLM reasoning workflows
Project description
NetBox Agent MCP
A read-only Model Context Protocol server for NetBox, designed for agentic LLM workflows.
The server is built around four patterns agents need most:
- Deep relational queries via GraphQL selection paths (
site.region.name) - Pre-computed follow-up filters in search and list responses (no hand-parsing IDs)
- Live schema introspection so agents don't guess at field names
- Hard-capped auto-pagination — no "partial-result hallucinations"
Requirements
- Python 3.11+, < 3.15
- uv (recommended) or
pipx - A NetBox instance (4.x recommended) with a read-only API token
Install
Two end-user options. Pick one.
Option A — uvx (zero install, always latest). Nothing to install; your MCP client invokes uvx netbox-agent-mcp, which fetches the latest published wheel into uv's cache on first use and re-runs from cache afterwards. Best for casual use.
Option B — persistent install.
uv tool install netbox-agent-mcp # preferred
# or
pipx install netbox-agent-mcp # if you don't have uv
Either puts a netbox-agent-mcp executable on your PATH. Upgrade with uv tool upgrade netbox-agent-mcp / pipx upgrade netbox-agent-mcp.
See Development install below if you want to hack on the code.
Run
The server reads configuration from environment variables or CLI flags (flags win). Minimum: NETBOX_URL + NETBOX_TOKEN.
stdio transport (default — used by MCP clients like Claude Code):
NETBOX_URL=https://netbox.example.com/ \
NETBOX_TOKEN=<your-token> \
netbox-agent-mcp
HTTP transport:
netbox-agent-mcp --transport http --host 127.0.0.1 --port 8000
See netbox-agent-mcp --help for the full flag list.
Claude Code integration
Register the server with uvx (no prior install needed):
claude mcp add --transport stdio netbox \
--env NETBOX_URL=https://netbox.example.com/ \
--env NETBOX_TOKEN=<your-token> \
-- uvx netbox-agent-mcp
If you used uv tool install / pipx install instead, drop uvx — the binary is already on PATH:
claude mcp add --transport stdio netbox \
--env NETBOX_URL=... --env NETBOX_TOKEN=... \
-- netbox-agent-mcp
After registering, start a Claude Code session — the netbox_* tools will be available. See also docs/SYSTEM_PROMPT.md for a recommended agent system prompt that unlocks the server's follow-up hints.
LM Studio integration
LM Studio supports MCP servers via mcp.json. Open Program → Install → Edit mcp.json, or edit the file directly:
- macOS / Linux:
~/.lmstudio/mcp.json - Windows:
%USERPROFILE%\.lmstudio\mcp.json
Merge this block into your mcpServers map:
{
"mcpServers": {
"netbox": {
"command": "uvx",
"args": ["netbox-agent-mcp"],
"env": {
"NETBOX_URL": "https://netbox.example.com/",
"NETBOX_TOKEN": "<your-token>"
}
}
}
}
Save and either restart LM Studio or toggle the server off/on in the Program panel. Tool calling requires a tool-capable model — look for the tool icon in LM Studio's model list.
Troubleshooting LM Studio:
- "Disconnected": check the MCP log (Program panel → log icon). Most common cause:
uvx(ornetbox-agent-mcp) not on the PATH LM Studio inherits, or missingNETBOX_URL/NETBOX_TOKEN. uvx/netbox-agent-mcpnot found: LM Studio does not inherit your shellPATH. Use the absolute path to the binary — e.g./opt/homebrew/bin/uvxon macOS,/usr/local/bin/uvxon Linux — as thecommandvalue.- Tools never get called: confirm the model supports tool use, then ask explicitly ("Use
netbox_searchto find…").
Development install
Contributors, or anyone who wants to run integration tests against a local NetBox:
git clone https://github.com/magicboxlab-ai/netbox-agent-mcp.git
cd netbox-agent-mcp
uv sync
uv run pre-commit install
Invoke the checked-out copy from an MCP client by swapping uvx netbox-agent-mcp for uv --directory /absolute/path/to/netbox-agent-mcp run netbox-agent-mcp. See CONTRIBUTING.md and docs/DEVELOPMENT.md for the full development workflow.
Tools
Ten read-only tools are exposed.
netbox_query(graphql, variables=None)
Raw GraphQL escape hatch. Use when you need fragments, deep hydration, or cross-entity joins not expressible via netbox_get.
netbox_query("""
query {
device_list(filters: {site_id: {in_list: [8, 12]}}, limit: 5) {
id name site { name region { name } } primary_ip4 { address }
}
}
""")
netbox_get(object_type, filters, select=None, limit=10, offset=0)
High-level list/filter. Compiles to GraphQL internally.
filters: REST-style grammar (site_id__in,name__ic,vid__gte, etc.)select: dotted paths for arbitrary-depth field selection (["name", "site.name", "site.region.name"])- Returns
{results, total_count, has_more, suggested_next}—suggested_nextcontains pre-built filter blocks for common follow-ups.
netbox_get_all(object_type, filters, select=None, max_records=10000)
Paginated variant of netbox_get. Loops internally; fails loud if total_count > max_records. Never silently truncates. max_records may be raised up to 100,000.
netbox_search(query, object_types=None, limit=25)
Text search across NetBox objects via REST ?q=. Returns matches, match_counts (per-type total/returned/truncated so agents can detect silent caps), and suggested_filters — ready-to-use filter dicts the agent can paste into netbox_get without re-parsing.
{
"matches": {"dcim.site": [{"id": 8, "name": "london-dc1"}]},
"match_counts": {
"dcim.site": {"total": 1, "returned": 1, "truncated": false}
},
"suggested_filters": {
"devices_at_these_sites": {
"object_type": "dcim.device",
"filters": {"site_id__in": [8]}
}
}
}
suggested_filters is a base filter — merge your other constraints (e.g. role_id=3) before calling netbox_get.
netbox_inspect_type(object_type)
Live GraphQL schema introspection. Returns the type's full field list with types and relations. Call this before constructing filters or select paths against unfamiliar types.
netbox_trace_path(endpoint_type, endpoint_id)
Cable path tracing. endpoint_type is one of interfaces, console-ports, console-server-ports, power-ports, power-outlets, power-feeds.
netbox_get_available(resource_type, parent_id, count=1)
Free-capacity queries. resource_type is one of:
prefix.available-ipsprefix.available-prefixesip-range.available-ipsvlan-group.available-vlansasn-range.available-asns
netbox_aggregate_by_device(relation, top_n=10, filters=None, max_scan=10000)
Server-side "top N devices by X" aggregation. Scans the junction/direct endpoint with a cheap projection, counts in-process, returns ONLY the top N — never ships 1000s of raw rows through the MCP wire.
Supported relations:
tunnel_terminations— top devices by VPN tunnel countcable_terminations— top devices by cable countinterfaces— top devices by interface countinventory_items— top devices by inventory item countip_addresses— top devices by assigned IP count
Scope via filters (standard NetBox REST filter grammar): {"role_id": 3} → top firewalls only, {"site_id__in": [8, 17]} → scope by site. Safety-capped at max_scan=10000 rows by default.
netbox_device_profile(device_id, include=None, max_per_category=100)
Composite one-shot view of a device: the full device record plus interfaces, IP addresses, tunnel terminations, and inventory items (plus optional cable terminations, console/power ports, module bays). Related items are returned in ?brief=1 shape. Hints (auto-computed): interface type breakdown, and the "N tunnel-named interfaces vs M structural tunnel terminations" gap that commonly shows up after firewall imports.
Pass max_per_category=0 for summary mode — counts and hints only, no item bodies.
netbox_find_ip_references(ip)
Fan-out lookup for every place an IP is referenced. Checks the structured
IPAM record (ipam.ipaddress with assigned_object) and does
case-insensitive description searches across vpn.tunnel, dcim.interface,
and ipam.prefix.
Use when a straight netbox_get("ipam.ipaddress", ...) doesn't find a
structural assignment — common for tunnel peer IPs that live only in
free-text descriptions (e.g. Palo Alto imports).
Returns {query, ipam_record, text_references, summary} with a hint in
summary that tells you which sub-result to follow.
Environment variables
| Variable | Default | Purpose |
|---|---|---|
NETBOX_URL |
(required) | Base URL of the NetBox instance (e.g. https://netbox.example.com/) |
NETBOX_TOKEN |
(required) | API token. Read-only strongly recommended. |
NETBOX_VERIFY_SSL |
true |
Verify TLS certificates |
LOG_LEVEL |
INFO |
DEBUG / INFO / WARNING / ERROR / CRITICAL |
TRANSPORT |
stdio |
stdio or http |
HOST |
127.0.0.1 |
HTTP bind address (when TRANSPORT=http) |
PORT |
8000 |
HTTP port (when TRANSPORT=http) |
MAX_RECORDS_CEILING |
100000 |
Absolute ceiling for netbox_get_all |
Tokens starting with nbt_ use Bearer auth; other token formats use Token auth. Detection is automatic.
Supported object types
Ships with ~48 curated types across dcim, ipam, circuits, virtualization, tenancy, vpn, wireless, extras, and core. See schema/object_types.py for the full registry. Plugin types are out of scope.
Read-only by design
No create/update/delete tools are exposed. Writes are deliberately excluded to keep the attack surface minimal. For writes, use NetBox's web UI or REST API directly.
Troubleshooting
"Configuration error: … missing NETBOX_URL / NETBOX_TOKEN" Set both environment variables before running the server.
"UnknownObjectType: 'dcim.wizard'"
The type is not in the registry. Call netbox_inspect_type after listing valid types in the error message.
"CapExceeded: Query would return 45230 records, exceeds max_records=10000"
Either narrow your filters (e.g. add site_id or status) or pass max_records=50000 on netbox_get_all. The absolute ceiling is 100,000.
"SchemaIntrospectionError: GraphQL error: Cannot query field 'X' on type 'Y'"
The field doesn't exist on that type, or NetBox's schema has changed. Call netbox_inspect_type('Y') to see the current field list.
Connection refused / TLS errors
Verify NETBOX_URL is reachable and includes the scheme (https://). For self-signed certs in dev, set NETBOX_VERIFY_SSL=false (not recommended for production).
Further reading
- Developer guide (design, implementation, testing, extending):
docs/DEVELOPMENT.md - Recommended agent system prompt:
docs/SYSTEM_PROMPT.md - NetBox REST API: https://docs.netbox.dev/en/stable/integrations/rest-api/
- NetBox GraphQL: https://docs.netbox.dev/en/stable/integrations/graphql-api/
- Model Context Protocol: https://modelcontextprotocol.io/
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file netbox_agent_mcp-0.1.8.tar.gz.
File metadata
- Download URL: netbox_agent_mcp-0.1.8.tar.gz
- Upload date:
- Size: 197.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ccee11cd4452b79fbef82b6665af47e3baaf4fa86acea2ecdc7344a1c15ddf1
|
|
| MD5 |
d96c18e9ea582b24d9cfb05e8c7d0a21
|
|
| BLAKE2b-256 |
963d270f9fa37c2df504522afeee32f1e144eafba13db00688a15ee6657ea5e5
|
Provenance
The following attestation bundles were made for netbox_agent_mcp-0.1.8.tar.gz:
Publisher:
publish.yml on magicboxlab-ai/netbox-agent-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netbox_agent_mcp-0.1.8.tar.gz -
Subject digest:
3ccee11cd4452b79fbef82b6665af47e3baaf4fa86acea2ecdc7344a1c15ddf1 - Sigstore transparency entry: 1366832462
- Sigstore integration time:
-
Permalink:
magicboxlab-ai/netbox-agent-mcp@ea2e534224cf76a1c1da1a16556a0db2cc8b13f5 -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/magicboxlab-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ea2e534224cf76a1c1da1a16556a0db2cc8b13f5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file netbox_agent_mcp-0.1.8-py3-none-any.whl.
File metadata
- Download URL: netbox_agent_mcp-0.1.8-py3-none-any.whl
- Upload date:
- Size: 57.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3aad2ce0a02aa2a44b3af52c1f0baad6dfeb02739cab85eaf6f79f2e693ec2a7
|
|
| MD5 |
c589809cae742371c3ed01a7f286a249
|
|
| BLAKE2b-256 |
0a75088cd8b4d2f0206a8bc5b79bbfad5df4333bcb2b72214a4b1acf0915d77b
|
Provenance
The following attestation bundles were made for netbox_agent_mcp-0.1.8-py3-none-any.whl:
Publisher:
publish.yml on magicboxlab-ai/netbox-agent-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netbox_agent_mcp-0.1.8-py3-none-any.whl -
Subject digest:
3aad2ce0a02aa2a44b3af52c1f0baad6dfeb02739cab85eaf6f79f2e693ec2a7 - Sigstore transparency entry: 1366832483
- Sigstore integration time:
-
Permalink:
magicboxlab-ai/netbox-agent-mcp@ea2e534224cf76a1c1da1a16556a0db2cc8b13f5 -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/magicboxlab-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ea2e534224cf76a1c1da1a16556a0db2cc8b13f5 -
Trigger Event:
push
-
Statement type: