Skip to content

MCP Server

netbox_mcp provides a deliberately small Model Context Protocol surface over the same SchemaIndex, request resolver, plugin discovery, pagination, client, and profile configuration used by the SDK and CLI. It does not generate one tool per OpenAPI operation, so its tool list stays stable when NetBox plugins change the reachable resources.

Install and run

pip install 'netbox-sdk[mcp]'
nbx-mcp

stdio is the default transport. For Streamable HTTP:

NETBOX_MCP_AUTH_TOKEN="$NETBOX_MCP_AUTH_TOKEN" nbx-mcp --transport streamable-http --host 127.0.0.1 --port 8000

The MCP endpoint is /mcp. Every Streamable HTTP bind requires a shared-secret bearer token via --auth-token or NETBOX_MCP_AUTH_TOKEN; the server raises RuntimeError and refuses to start without one, including on loopback hosts (127.0.0.1, localhost, ::1). Binding to loopback only restricts reachability to this machine — it does not authenticate other local processes or users, who could otherwise reach the server's loaded NetBox credential (and any active --allow-mutations window) unauthenticated on a shared dev or bastion host. Put TLS termination in front of it when it is exposed beyond the local host — the bearer token authenticates the caller, it does not encrypt the transport. Prefer NETBOX_MCP_AUTH_TOKEN over --auth-token on any shared host: a CLI argument's value is visible to other local users through ps and /proc/<pid>/cmdline, while the environment variable is not.

This gate cannot be bypassed by calling the server object directly instead of going through run(): create_mcp_server() always shadows the returned server's streamable_http_app and sse_app with wrappers that enforce the same auth_token, including raising RuntimeError when no token was configured at all. There is no code path — run("streamable-http"), streamable_http_app(), run("sse"), sse_app(), or otherwise — that yields an unauthenticated network app from a create_mcp_server()-produced instance. The --transport CLI flag only exposes stdio and streamable-http, but any embedder holding the returned FastMCP instance directly (not just the nbx-mcp entrypoint) could otherwise reach the SSE transport unauthenticated, since it shares the same instance-attribute shadowing mechanism as Streamable HTTP.

Tool surface

Tool Behavior
list_groups, list_resources, describe_operation Stable JSON schema introspection; live=true includes runtime resources
list, get Schema-resolved reads; list supports pagination and repeated query keys; live=true dispatches against the same connected-instance schema describe_operation(live=true) reports, so a runtime-discovered resource can be listed or fetched, not just described
filters Local filter introspection with no HTTP request
create, update, patch, delete Detail mutations guarded off by default
bulk_update, bulk_patch, bulk_delete List-path mutations guarded off by default
plugin_discover Enrich the active schema through live plugin discovery
plugin_list_tools Discover and validate semantic tools explicitly advertised by NetBox plugin API roots
plugin_call_tool Invoke one advertised plugin operation through the configured SDK client; writes remain guarded off by default
call Relative /api/ escape hatch; GET/HEAD only while the mutation gate is closed

Every tool input is validated by an explicit Pydantic schema. Names, IDs, methods, relative API paths, list sizes, unknown fields, and credential control characters are rejected before dispatch. Raw call paths containing an encoded path separator (%2F or %5C, case-insensitive) are rejected before cache or network access because routers disagree on whether those octets split segments. Plugin manifests are also treated as hostile input: origin, namespace, path, method/effect consistency, schemas, payloads, nesting, and sizes are checked before target dispatch. See NetBox Plugin Bridge for the wire contract and plugin-author checklist.

Authentication

For stdio, the server loads the existing default profile from netbox_sdk.config; it does not create a second credential store. Each tool that contacts NetBox can instead receive a token bearer credential for that call. Avoid placing tokens in logs, model-visible transcripts, or checked-in server configuration.

This per-call NetBox token is separate from the Streamable HTTP transport's own --auth-token/NETBOX_MCP_AUTH_TOKEN bearer gate described above: the transport token authenticates the MCP caller to this server, while the per-call token authenticates this server to NetBox.

Mutation safety

Live writes are denied by default. Preview the request first with dry_run=true, then start a deliberately scoped execution window with either:

NETBOX_MCP_ALLOW_MUTATIONS=1 nbx-mcp
nbx-mcp --allow-mutations

dry_run=true resolves the method, path, query, and body locally without constructing a client. It is not server-side validation and does not prove that the live NetBox call will succeed. The one exception is plugin_call_tool: discovery of an advertised tool necessarily performs read-only GET requests for the live plugin root and manifest. Its dry-run still never dispatches the advertised target mutation.

The nbx process independently refuses dynamic CRUD/bulk writes, Proxbox CRUD/sync or TUI launch, write-method raw/dev-HTTP calls, and mutating Branching verbs unless the reviewed command includes --confirm or its environment contains NETBOX_SDK_CONFIRM_WRITE=1. Repository-local Claude Code and Codex hooks add a defense-in-depth early denial for recognizable Bash source; decoded or generated shell input still reaches the authoritative CLI-process gate.

Agent operating sequence

  1. Inspect nbx capabilities --json, or call list_groups, list_resources, describe_operation, and plugin_list_tools.
  2. Preview every write with --dry-run or dry_run=true.
  3. Explicitly enable/confirm only the reviewed operation and execute it.
  4. Verify the result with get or a filtered list.

The repository ships this procedure as the mirrored netbox-sdk-operations Skill under .claude/skills/ and .codex/skills/.