SKILL.md
NetBox Labs Platform MCP Server
The Platform MCP Server is a hosted Model Context Protocol server (on NetBox Cloud) that puts a NetBox instance behind an MCP tool surface so an agent can read and modify NetBox data. It is distinct from the open-source netbox-community/netbox-mcp-server: the platform server adds Code Mode, per-user auth, dynamic plugin/model detection, and first-party product integrations (Assurance, Discovery).
Your knowledge of this server may be outdated. It is Public Preview; tool inventories, helper signatures, and available namespaces are generated live per deployment and evolve. Always discover the real surface (schema search / tool list) before acting — never assume an object type, endpoint, or helper exists.
Retrieval Sources
| Source | URL / Method | Use for |
|---|---|---|
| Platform MCP docs | https://netboxlabs.com/docs/cloud/platform-mcp-server |
Setup, client configs, auth, modes |
| Community MCP server | https://github.com/netbox-community/netbox-mcp-server |
The open-source read-only alternative |
| Live server | The connected MCP server itself | The authoritative tool list + netboxsearchschema are the source of truth |
Connect & Auth
- Endpoint:
https://<instance>.cloud.netboxapp.com/mcp - Transport: MCP over streamable HTTP (no stdio — it is a networked, hosted server, not a local subprocess).
- Auth:
Authorization: Bearer nbt<token>— a NetBox v2 API token (prefixnbt, available on NetBox 4.5+). With per-user auth, your own token's NetBox RBAC applies to every call. - Customers enable the server on their instance via NetBox Labs support.
Any MCP-over-streamable-HTTP client works (Claude Code/Desktop, Cursor, VS Code, ChatGPT Developer Mode). The docs page has copy-paste client configs.
FIRST: Identify Your Mode
The server exposes tools in one of three modes; what you see in the tool list tells you which mode you're in:
- Code Mode — you see exactly three tools:
netboxsearchschema,netboxexecute,rediscovernetbox. Higher tiers default here. This is the primary, most efficient surface. - Discrete Mode — you see ~34
netbox_*tools (plus plugin/product tools). Each operation is its own tool. - Both — you see all of the above.
If you see netboxexecute, use Code Mode. If you only see discrete netbox* tools, jump to [Discrete Mode](#discrete-mode).
Code Mode
Instead of dozens of discrete tools (one round-trip each, bloated context), Code Mode exposes three tools whose arguments are Python code. You write a short program that calls injected NetBox helpers, does its own filtering/joining/looping in-process, and returns a single result. This collapses many tool round-trips into one.
The three tools
| Tool | Argument | What it does |
|---|---|---|
netboxsearchschema(code) |
Python reading a pre-loaded schema var, assigning result |
Discovery. Local filtering of an in-memory schema dict — no NetBox I/O. Confirm object types, endpoints, filters before acting. |
netbox_execute(code) |
Python calling injected helpers, assigning result |
The workhorse. Runs your program against NetBox and returns result. Its description is generated live, listing the exact helpers available in this deployment. |
rediscover_netbox() |
none | Recovery only. Re-probes NetBox and reconciles the tool inventory/schema. Use sparingly — see below. |
The golden loop (minimize round trips)
- Search the schema first — don't guess. Call
netboxsearchschemato confirm the right object type, filter field, or special endpoint. Guessing object types/paths is the #1 failure mode. - Write ONE
netboxexecuteprogram that does the whole job — fetch, filter, join, loop, compute in a single program. Never callnetboxexecuteonce per object when a loop or one list call would do. - Always assign
result. If you never assign it you get back "Code ran successfully but did not assign toresult" — a wasted turn. - Project fields with
fields=[...]on reads to keep payloads (and the result-size cap) small. - Prefer named helpers over raw
api()— they carry validation and clearer errors.
# netbox_search_schema — confirm the type/filters first
result = {k: v for k, v in schema['object_types'].items() if v['app'] == 'dcim'}
# netbox_execute — do the whole task in one program
sites = get('dcim.site', filters={'name': 'NYC-DC1'}, fields=['id', 'name'])
site_id = sites['results'][0]['id']
devs = get('dcim.device', filters={'site_id': site_id, 'status': 'active'},
fields=['id', 'name', 'role', 'primary_ip4'], limit=1000)
result = {'site': 'NYC-DC1', 'count': devs['count'], 'devices': devs['results']}
The sandbox contract (what your code may do)
netboxexecute / netboxsearch_schema run through a validated sandbox. Respect these or your code is rejected:
- NO
import/from ... import. This is the single most common failure — agents reach forjson,re,ipaddress,math,collections. Everything you need is already in the namespace or in allowed builtins. Never writeimport. - NO class definitions,
del,async/await/yield. - NO dunder / introspection (
eval,exec,open,getattr,globals,class,subclasses, etc. are blocked). - Allowed builtins only — the common ones:
len,str,int,float,bool,list,dict,set,tuple,range,enumerate,zip,sorted,sum,min,max,any,all,abs,round,map,filter. - Loops, wall-clock time, and result size are capped — keep programs bounded; project fields to stay under the result cap.
- Must assign
result. - HTTP errors surface the NetBox response body, including per-field validation errors — so a failed
create()/update()returns actionable detail you can correct from and retry in the next program.
Injected helpers
The exact set is rendered live in the netbox_execute description and gated by capability (write/IPAM-allocation/GraphQL/scripts/plugins/products). Object types use dot notation ('dcim.device'); the path form ('dcim/devices') also resolves. Full catalog in [references/code-mode-helpers.md](references/code-mode-helpers.md) — load it when you need a specific signature. The essentials:
get(type, filters={}, fields=[], limit=50, offset=0, ordering=None) → {count, next, previous, results[]}
get_by_id(type, id, fields=[]) → object
search(query, object_types=[], limit=5) → {type: [results]}
options(type) → field schema (required fields, choices)
discover_models(app=None) / inspect_model(app, endpoint) # enumerate types incl. plugins
create(type, data) / update(type, id, data) / delete(type, id) # WRITE-gated
bulk_create / bulk_update / bulk_delete # WRITE-gated
available_prefixes / available_ips / allocate_prefix / allocate_ip ... # IPAM-gated
graphql(query, variables=None, analyze=True) # GRAPHQL-gated
api(method, endpoint, params=None, data=None) # raw escape hatch
Filter rules (two footguns)
- Never use
in. NetBox silently ignoresfieldinon many fields and returns all rows — confidently wrong. The helpers reject it. Pass a list as the field value instead:
``python # WRONG get('dcim.site', filters={'id__in': [1, 2, 3]}) # RIGHT — list value sends repeated query params, which NetBox honors get('dcim.site', filters={'id': [1, 2, 3]}) ``
- No multi-hop traversal.
device__site_id-style nested filters are rejected. Use a two-step query — fetch the parent's ids, then filter the child:
``python racks = get('dcim.rack', filters={'site': 'nyc-dc1'}, fields=['id']) rackids = [r['id'] for r in racks['results']] devices = get('dcim.device', filters={'rackid': rack_ids}, fields=['id', 'name']) result = devices['results'] ``
When to use rediscover_netbox()
Only when you have a specific reason to believe the tool inventory is stale: the user just installed/uninstalled a plugin, a tool you genuinely expected returns "tool not found," or a tool you no longer expect is still advertised. It is also the recovery path after a failed plugin registration. Do NOT call it before every operation, to "refresh" NetBox data (helpers are always live), or "just to be sure" at session start — each call costs a round-trip plus re-registration.
More end-to-end programs in [references/code-mode-workflows.md](references/code-mode-workflows.md).
Discrete Mode
When Code Mode isn't available, every operation is its own MCP tool with a typed signature and a rich docstring. Discrete mode delivers more context directly (the tool list and docstrings are in front of you), so there is less to discover — but each operation is a separate round-trip. Core tools:
| Group | Tools |
|---|---|
| Reads | netboxgetobjects, netboxgetobjectbyid, netboxsearchobjects, netboxgetchangelogs |
| Writes | netboxcreateobject, netboxupdateobject, netboxdeleteobject, netboxbulkcreateobjects, netboxbulkupdateobjects, netboxbulkdelete_objects |
| Connectivity | netboxtracecable, netboxgetcablepaths, netboxgetrackelevation, netboxgetconnected_device |
| IPAM | netboxgetavailable (prefixes/ips/vlans/asns), netboxallocatenext |
| Automation/introspection | netboxrenderconfig, netboxrunscript, netboxgraphqlquery, netboxgetobjectschema, discovermodels, inspectmodel, rediscovernetbox |
Discrete-mode rules the docstrings already enforce (follow them):
- Always pass
fields=to minimize tokens (80–90% payload reduction on reads).limitdefault 50, max 1000. - The
__inand multi-hop rules are identical to code mode — pass a list value; use the two-step pattern. netboxgetchangelogs:actionmust be imperative singular ("create"/"update"/"delete"— NetBox rejects"updated"with 400).changedobjecttypeis"app.model";changedobjecttype_idis the integer ContentType PK.- Object types accept both
"dcim.device"and"dcim/devices". Usediscover_models()to enumerate types (including plugins).
Product & Plugin Namespaces
When the tenant is entitled and the integration is configured, additional helpers/tools appear:
- Plugins: branching (
branch), changes (changerequest, reviews, comments), custom-objects (customtype/customobject_). - Products: Assurance (
assurance) and Discovery (discovery).
These are entitlement-gated — present only when your tenant has the product/plugin. Don't assume they exist; discover them. For their domain logic, see [netbox-branching](../netbox-branching/SKILL.md), [netbox-changes](../netbox-changes/SKILL.md), [netbox-custom-objects](../netbox-custom-objects/SKILL.md), [netbox-assurance](../netbox-assurance/SKILL.md), and [netbox-discovery](../netbox-discovery/SKILL.md).
Anti-Patterns
- Writing
importin code mode → "Forbidden operation: Import." The dominant failure. Use namespace helpers + allowed builtins only. - Not searching the schema first → guessing a wrong object type or unsupported filter. Confirm via
netboxsearchschemabefore executing. - The
__infootgun → silently-broad results / rejection. Use a list as the field value. - Multi-hop filters → rejected. Use the two-step pattern.
- Forgetting to assign
result→ wasted turn. - One
netbox_executeper object instead of a single looped/bulk program → defeats the point of code mode. - Over-calling
rediscover_netboxas a precaution → it's a round-trip cost, not a data refresh. - Not projecting
fields=→ oversized payloads, hitting the result cap, wasted tokens (both modes). - Assuming a product/plugin namespace exists → it's entitlement-gated. Discover it.
NameErroron an undefined name (ip_network,re, …) → only injected helpers + allowed builtins exist.
References
- [references/code-mode-helpers.md](references/code-mode-helpers.md) — Full injected-helper catalog with signatures, grouped by capability gate. Load when you need an exact signature.
- [references/code-mode-workflows.md](references/code-mode-workflows.md) — End-to-end one-program examples (reads, list-of-ids, two-step joins, IPAM allocation, changelog audits).