Skip to main content
Glama
mhajder

NetBox MCP Server

by mhajder
README.md
# NetBox MCP Server

<!-- mcp-name: io.github.mhajder/netbox-mcp -->

NetBox MCP Server is a Python-based Model Context Protocol (MCP) server that gives AI assistants structured access to [NetBox](https://netbox.dev) - the source of truth for network infrastructure (DCIM and IPAM). A small set of generic tools covers all 139 core object types of NetBox 4.7 (and plugin types, if enabled), plus specialised tools for search, change logs, GraphQL, free IP/prefix/VLAN allocation, cable tracing, config rendering and (optionally) netbox-branching. Writes can be hidden entirely (read-only mode) or simulated (dry-run mode).

## Features

### Core Features

- Query any NetBox object type with filtering, ordering and pagination through one tool
- Keep responses small with `fields`, `omit` and `brief` - the tools steer the model to use them
- Search many object types at once, with explicit reporting of failed and truncated types
- Read the change log (who changed what, when) and NetBox status/version/plugins
- Run read-only GraphQL queries for nested data in one round trip
- List free IPs, prefixes, VLANs and ASNs; trace cable paths; render device configs

### Management Operations

- Create, update and delete any object type, singly or in bulk (NetBox runs a bulk write as one transaction)
- Allocate the next free IP, prefix, VLAN or ASN atomically
- Optionally stage changes in a netbox-branching branch instead of main
- Describe an object type's writable fields and choices before writing

### Advanced Capabilities

- Read-only mode hides every write tool; dry-run mode turns every write into a preview
- NetBox v2 (`Bearer nbt_...`) and legacy v1 (`Token ...`) API tokens
- Plugin object type discovery (e.g. `netbox_dns.zone`)
- Filter validation that rejects filters NetBox would silently ignore (`__in`, multi-hop lookups)
- Retries on 429/5xx without ever replaying a create after a server error
- Tag-based tool filtering, optional tool-search transform, rate limiting and response size limit
- Bearer token authentication for HTTP transport
- Multiple transport options (STDIO, SSE, HTTP)
- Optional Sentry integration for error tracking

## Installation

### Prerequisites

- Python 3.11 to 3.14
- A NetBox instance, version 4.2 or newer. Features added later (`omit`, v2
  tokens, `/api/authentication-check/`) are used when the server detects them
- A NetBox API token with the permissions you want the assistant to have. The
  token is the real security boundary: a read-only token cannot write, whatever
  this server's settings are

### Quick Install from PyPI

The easiest way to get started is to install from PyPI:

```sh
# Using UV (recommended)
uvx netbox-mcp

# Or using pip
pip install netbox-mcp
```

Remember to configure the environment variables for your NetBox instance before running the server:

```sh
# Create environment configuration
export NETBOX_URL=https://netbox.example.com
export NETBOX_TOKEN=nbt_your-key.your-token
```

### Install from Source

1. Clone the repository:

```sh
git clone https://github.com/mhajder/netbox-mcp.git
cd netbox-mcp
```

2. Install dependencies:

```sh
# Using UV (recommended)
uv sync

# Or using pip
pip install -e .
```

3. Configure environment variables:

```sh
cp .env.example .env
# Edit .env with your NetBox URL and API token
```

4. Run the server:

```sh
# Using UV (recommended)
uv run netbox-mcp

# Or using the installed command directly
netbox-mcp
```

### Using Docker

A Docker image is available on GitHub Packages for easy deployment.

```sh
docker pull ghcr.io/mhajder/netbox-mcp:latest
```

### Development Setup

For development with additional tools:

```sh
# Clone and install with development dependencies
git clone https://github.com/mhajder/netbox-mcp.git
cd netbox-mcp
uv sync --group dev

# Run tests
uv run pytest

# Run with coverage
uv run pytest --cov=src/

# Run linting and formatting
uv run ruff check .
uv run ruff format .

# Run type checking
uv run ty check .

# Setup prek hooks
uv run prek install
```


## Configuration

### Environment Variables

```env
# NetBox MCP Server Environment Configuration

# NetBox Connection Details
NETBOX_URL=https://netbox.example.com

# API Token - v2 tokens (nbt_<key>.<token>, NetBox 4.5+) are sent as "Bearer",
# legacy v1 tokens as "Token"
NETBOX_TOKEN=nbt_your-key.your-token

# SSL Configuration
NETBOX_VERIFY_SSL=true
# Total time allowed for one NetBox request, retries included (seconds)
NETBOX_TIMEOUT=30

# Branching (requires the netbox-branching plugin)
# Set NETBOX_BRANCHING_ENABLED true to add the branch tools and the 'branch'
# argument to every tool. Off by default - leave it off on a NetBox without the plugin
NETBOX_BRANCHING_ENABLED=false
# Default branch (name or schema ID) used by every call; empty = main.
# Requires NETBOX_BRANCHING_ENABLED=true
NETBOX_BRANCH=

# Plugin Discovery
# Set NETBOX_PLUGIN_DISCOVERY true to expose plugin models (e.g. netbox_dns.zone)
NETBOX_PLUGIN_DISCOVERY=false

# Read-Only Mode
# Set READ_ONLY_MODE true to hide all write tools (create, update, delete)
READ_ONLY_MODE=false

# Dry-Run Mode
# Set DRY_RUN_MODE true to make every write tool only simulate the change:
# it returns the request it would send and never modifies NetBox
DRY_RUN_MODE=false

# Disabled Tags
# Comma-separated list of tags to disable tools for (empty by default)
# Example: DISABLED_TAGS=delete,graphql,branching
DISABLED_TAGS=

# Logging Configuration
LOG_LEVEL=INFO

# Response Size Limit
# Maximum tool response size in bytes; larger responses are truncated (empty = unlimited)
RESPONSE_MAX_SIZE=

# Rate Limiting
# Set RATE_LIMIT_ENABLED true to enable rate limiting
RATE_LIMIT_ENABLED=false
RATE_LIMIT_MAX_REQUESTS=60
RATE_LIMIT_WINDOW_MINUTES=1

# Tool Search Transform (Optional)
# Set TOOL_SEARCH_ENABLED true to replace full tool listings with search_tools + call_tool
TOOL_SEARCH_ENABLED=false
# Search strategy: bm25 (natural language) or regex (pattern match)
TOOL_SEARCH_STRATEGY=bm25
# Maximum number of tools returned by search_tools
TOOL_SEARCH_MAX_RESULTS=5

# Sentry Error Tracking (Optional)
# Set SENTRY_DSN to enable error tracking and performance monitoring
# SENTRY_DSN=https://your-key@o12345.ingest.us.sentry.io/6789
# Optional Sentry configuration
# SENTRY_TRACES_SAMPLE_RATE=1.0
# SENTRY_SEND_DEFAULT_PII=true
# SENTRY_ENVIRONMENT=production
# SENTRY_RELEASE=1.2.3
# SENTRY_PROFILE_SESSION_SAMPLE_RATE=1.0
# SENTRY_PROFILE_LIFECYCLE=trace
# SENTRY_ENABLE_LOGS=true

# MCP Transport Configuration
# Transport type: 'stdio' (default), 'sse' (Server-Sent Events), or 'http' (HTTP Streamable)
# MCP_TRANSPORT=stdio

# HTTP Transport Settings (used when MCP_TRANSPORT=sse or MCP_TRANSPORT=http)
# Host to bind the HTTP server (default: 127.0.0.1)
# MCP_HTTP_HOST=127.0.0.1
# Port to bind the HTTP server (default: 8000)
# MCP_HTTP_PORT=8000
# Optional bearer token for authentication (leave empty for no auth)
# MCP_HTTP_BEARER_TOKEN=
```

## Available Tools

Object types are named `app.model`, as NetBox itself names them (`dcim.device`, `ipam.prefix`, `virtualization.virtualmachine`). API paths such as `dcim/devices` are accepted too. With branching enabled, every tool that reads or writes objects also takes an optional `branch` argument.

### Objects (read)

| Tool | Description |
|---|---|
| `netbox_list_object_types` | List supported object types and their endpoints (core and discovered plugin types) |
| `netbox_get_objects` | List objects of a type with `filters`, `fields`, `omit`, `brief`, `ordering`, `limit`, `offset` |
| `netbox_get_object` | Get one object by ID |
| `netbox_search_objects` | Free-text search (`q`) across several object types at once |
| `netbox_describe_object_type` | Writable fields of a type: data type, required, choices |
| `netbox_get_changelogs` | Change log entries, newest first |

### Specialised (read)

| Tool | Description |
|---|---|
| `netbox_get_status` | NetBox version, plugins, workers, authenticated user and this server's modes |
| `netbox_graphql_query` | Read-only GraphQL query (mutations are rejected) |
| `netbox_get_available` | Free IPs / prefixes in a prefix or IP range, VLANs in a VLAN group, ASNs in an ASN range |
| `netbox_trace_cable` | Full cable path from an interface, front/rear/console/power port or circuit termination |
| `netbox_render_config` | Render a device's or VM's configuration from its config template |
| `netbox_list_branches` | List netbox-branching branches (only with `NETBOX_BRANCHING_ENABLED=true`) |

### Write

| Tool | Description |
|---|---|
| `netbox_create_object` | Create one object |
| `netbox_update_object` | Partially update one object (PATCH) |
| `netbox_bulk_create_objects` | Create up to 500 objects in one transaction |
| `netbox_bulk_update_objects` | Update up to 500 objects in one transaction |
| `netbox_allocate_available` | Allocate the next free IP, prefix, VLAN or ASN |
| `netbox_create_branch` | Create a netbox-branching branch (only with `NETBOX_BRANCHING_ENABLED=true`) |
| `netbox_delete_object` | Delete one object (`destructiveHint`) |
| `netbox_bulk_delete_objects` | Delete up to 500 objects in one transaction (`destructiveHint`) |

### Tool Tags

Every tool is tagged `netbox` plus a group tag, which `DISABLED_TAGS` can switch off: `objects`, `search`, `changelog`, `status`, `graphql`, `ipam`, `dcim`, `config`, `branching`, `write` and `delete`. Read tools also carry `read-only`.

## Security & Safety Features

### Read-Only Mode

Read-only mode hides every write tool - the client only sees the 11 read tools (12 with branching enabled):

```env
READ_ONLY_MODE=true
```

### Dry-Run Mode

Dry-run mode keeps the write tools visible, but none of them changes NetBox. Each one resolves and validates its target, then returns the request it would have sent, flagged `"dry_run": true`. Updates and deletes also fetch the current object, so the preview shows the values that would change and exactly what would be deleted:

```env
DRY_RUN_MODE=true
```

`READ_ONLY_MODE` takes precedence: when both are set, write tools are hidden.

### NetBox Branching

With the [netbox-branching](https://github.com/netboxlabs/netbox-branching) plugin, changes can be staged in a branch and reviewed before they are merged into main. Branching support is **off by default**, so a NetBox without the plugin gets no branch tools and no `branch` argument. Enable it with:

```env
NETBOX_BRANCHING_ENABLED=true
```

This adds `netbox_list_branches` and `netbox_create_branch`, and a `branch` argument (a branch name or its schema ID) to every other tool. To send every call to one branch by default:

```env
NETBOX_BRANCH=assistant-changes
```

Setting `NETBOX_BRANCH` without `NETBOX_BRANCHING_ENABLED=true` is a configuration error: the server refuses to start rather than silently writing to main.

Branch names are resolved to the schema ID that the `X-NetBox-Branch` header needs. Merging, syncing and reverting are deliberately not exposed as tools - they belong in a human review step.

### Plugin Discovery

```env
NETBOX_PLUGIN_DISCOVERY=true
```

On first use the server reads `/api/core/object-types/` and adds every plugin model with a REST endpoint (for example `netbox_dns.zone`). Plugin types never shadow core types. If discovery fails, the core types keep working.

### Tag-Based Tool Filtering

You can disable specific categories of tools by setting disabled tags. For example, allow creates and updates but never deletes:

```env
DISABLED_TAGS=delete
```

### Response Size Limit

Large NetBox objects add up quickly. `RESPONSE_MAX_SIZE` truncates any tool response above the given number of bytes:

```env
RESPONSE_MAX_SIZE=200000
```

### Tool Search for Large Toolsets

FastMCP tool search can reduce prompt size for servers with many tools.
When enabled, `list_tools` returns two synthetic tools:

- `search_tools`: Finds matching tools and returns their full schemas
- `call_tool`: Executes any discovered tool by name

Enable it with:

```env
TOOL_SEARCH_ENABLED=true
TOOL_SEARCH_STRATEGY=bm25      # bm25 or regex
TOOL_SEARCH_MAX_RESULTS=8      # optional, default is 5
```

`bm25` supports natural language queries, while `regex` uses a regex
`pattern` input for deterministic matching.

Tool search respects existing visibility controls (read-only mode and
disabled tags).

### Rate Limiting

The server supports rate limiting to control API usage and prevent abuse. If enabled, requests are limited per client using a sliding window algorithm.

Enable rate limiting by setting the following environment variables in your `.env` file:

```env
RATE_LIMIT_ENABLED=true
RATE_LIMIT_MAX_REQUESTS=100   # Maximum requests allowed per window
RATE_LIMIT_WINDOW_MINUTES=1   # Window size in minutes
```

If `RATE_LIMIT_ENABLED` is set to `true`, the server will apply rate limiting middleware. Adjust `RATE_LIMIT_MAX_REQUESTS` and `RATE_LIMIT_WINDOW_MINUTES` as needed for your environment.

### Sentry Error Tracking & Monitoring (Optional)

The server optionally supports **Sentry** for error tracking, performance monitoring, and debugging. Sentry integration is completely optional and only initialized if configured.

#### Installation

To enable Sentry monitoring, install the optional dependency:

```sh
# Using UV (recommended)
uv sync --extra sentry
```

#### Configuration

Enable Sentry by setting the `SENTRY_DSN` environment variable in your `.env` file:

```env
# Required: Sentry DSN for your project
SENTRY_DSN=https://your-key@o12345.ingest.us.sentry.io/6789

# Optional: Performance monitoring sample rate (0.0-1.0, default: 1.0)
SENTRY_TRACES_SAMPLE_RATE=1.0

# Optional: Include personally identifiable information (default: true)
SENTRY_SEND_DEFAULT_PII=true

# Optional: Environment name (e.g., "production", "staging")
SENTRY_ENVIRONMENT=production

# Optional: Release version (auto-detected from package if not set)
SENTRY_RELEASE=1.2.3

# Optional: Profiling - continuous profiling sample rate (0.0-1.0, default: 1.0)
SENTRY_PROFILE_SESSION_SAMPLE_RATE=1.0

# Optional: Profiling - lifecycle mode for profiling (default: "trace")
# Options: "all", "continuation", "trace"
SENTRY_PROFILE_LIFECYCLE=trace

# Optional: Enable log capture as breadcrumbs and events (default: true)
SENTRY_ENABLE_LOGS=true
```

#### Features

When enabled, Sentry automatically captures:

- **Exceptions & Errors**: All unhandled exceptions with full context
- **Performance Metrics**: Request/response times and traces
- **MCP Integration**: Detailed MCP server activity and interactions
- **Logs & Breadcrumbs**: Application logs and event trails for debugging
- **Context Data**: Environment, client info, and request parameters

#### Getting a Sentry DSN

1. Create a free account at [sentry.io](https://sentry.io)
2. Create a new Python project
3. Copy your DSN from the project settings
4. Set it in your `.env` file

#### Disabling Sentry

Sentry is completely optional. If you don't set `SENTRY_DSN`, the server will run normally without any Sentry integration, and no monitoring data will be collected.

### SSL/TLS Configuration

The server supports SSL certificate verification and custom timeout settings:

```env
NETBOX_VERIFY_SSL=true    # Enable SSL certificate verification
NETBOX_TIMEOUT=30         # Total time per request, retries included (seconds)
```

Certificates are verified against the operating system trust store, so a NetBox behind an internal CA works as soon as that CA is trusted by the OS. `SSL_CERT_FILE` or `SSL_CERT_DIR` take precedence when set.

### Transport Configuration

The server supports multiple transport mechanisms for the MCP protocol:

#### STDIO Transport (Default)

The default transport uses standard input/output for communication. This is ideal for local usage and integration with tools that communicate via stdin/stdout:

```env
MCP_TRANSPORT=stdio
```

#### HTTP SSE Transport (Server-Sent Events)

For network-based deployments, you can use HTTP with Server-Sent Events. This allows the MCP server to be accessed over HTTP with real-time streaming:

```env
MCP_TRANSPORT=sse
MCP_HTTP_HOST=127.0.0.1        # Localhost
MCP_HTTP_PORT=8000           # Port to listen on
MCP_HTTP_BEARER_TOKEN=your-secret-token  # Optional authentication token
```

When using SSE transport with a bearer token, clients must include the token in their requests:

```bash
curl -H "Authorization: Bearer your-secret-token" http://localhost:8000/sse
```

#### HTTP Streamable Transport

The HTTP Streamable transport provides HTTP-based communication with request/response streaming. This is ideal for web integrations and tools that need HTTP endpoints:

```env
MCP_TRANSPORT=http
MCP_HTTP_HOST=127.0.0.1        # Localhost
MCP_HTTP_PORT=8000           # Port to listen on
MCP_HTTP_BEARER_TOKEN=your-secret-token  # Optional authentication token
```

When using streamable transport with a bearer token:

```sh
curl -H "Authorization: Bearer your-secret-token" \
     -H "Accept: application/json, text/event-stream" \
     -H "Content-Type: application/json" \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
     http://localhost:8000/mcp
```

**Note**: The HTTP transport requires proper JSON-RPC formatting with `jsonrpc` and `id` fields. The server may also require session initialization for some operations.

For more information on FastMCP transports, see the [FastMCP documentation](https://gofastmcp.com/deployment/running-server#transport-protocols).

## Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run tests and ensure code quality (`uv run pytest && uv run ruff check .`)
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request

## License

MIT License - see LICENSE file for details.