Metadata-Version: 2.4
Name: netbox-agent-mcp
Version: 0.1.7
Summary: Agentic Model Context Protocol server for NetBox — read-only, tool-rich, built for LLM reasoning workflows
Project-URL: Homepage, https://magicboxlab-ai.github.io/netbox-agent-mcp/
Project-URL: Documentation, https://magicboxlab-ai.github.io/netbox-agent-mcp/
Project-URL: Repository, https://github.com/magicboxlab-ai/netbox-agent-mcp
Project-URL: Issues, https://github.com/magicboxlab-ai/netbox-agent-mcp/issues
Project-URL: Changelog, https://magicboxlab-ai.github.io/netbox-agent-mcp/changelog/
Author: rmarnold
License: MIT
License-File: LICENSE
Keywords: agent,dcim,ipam,llm,mcp,model-context-protocol,netbox
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Networking
Classifier: Topic :: System :: Systems Administration
Requires-Python: <3.15,>=3.11
Requires-Dist: fastmcp<4,>=3.2.0
Requires-Dist: httpx>=0.28.1
Requires-Dist: pydantic-settings>=2.13.1
Requires-Dist: pydantic>=2.12.5
Description-Content-Type: text/markdown

# NetBox Agent MCP

[![PyPI](https://img.shields.io/pypi/v/netbox-agent-mcp)](https://pypi.org/project/netbox-agent-mcp/)
[![Docs](https://img.shields.io/badge/docs-mkdocs--material-526cfe?logo=materialformkdocs&logoColor=white)](https://magicboxlab-ai.github.io/netbox-agent-mcp/)
[![CI](https://github.com/magicboxlab-ai/netbox-agent-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/magicboxlab-ai/netbox-agent-mcp/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://pre-commit.com/)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

A read-only [Model Context Protocol](https://modelcontextprotocol.io/) server for [NetBox](https://netbox.dev/), 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](https://docs.astral.sh/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.**

```bash
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](#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):

```bash
NETBOX_URL=https://netbox.example.com/ \
NETBOX_TOKEN=<your-token> \
netbox-agent-mcp
```

**HTTP transport:**

```bash
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):

```bash
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:

```bash
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`](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](https://lmstudio.ai/docs/app/plugins/mcp) 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:

```json
{
  "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` (or `netbox-agent-mcp`) not on the PATH LM Studio inherits, or missing `NETBOX_URL` / `NETBOX_TOKEN`.
- **`uvx` / `netbox-agent-mcp` not found**: LM Studio does not inherit your shell `PATH`. Use the absolute path to the binary — e.g. `/opt/homebrew/bin/uvx` on macOS, `/usr/local/bin/uvx` on Linux — as the `command` value.
- **Tools never get called**: confirm the model supports tool use, then ask explicitly ("Use `netbox_search` to find…").

## Development install

Contributors, or anyone who wants to run integration tests against a local NetBox:

```bash
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`](CONTRIBUTING.md) and [`docs/DEVELOPMENT.md`](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`.

```python
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_next` contains 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.

```json
{
  "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-ips`
- `prefix.available-prefixes`
- `ip-range.available-ips`
- `vlan-group.available-vlans`
- `asn-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 count
- `cable_terminations` — top devices by cable count
- `interfaces` — top devices by interface count
- `inventory_items` — top devices by inventory item count
- `ip_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
