Back to Blog
Guide10 min

How to Connect an AI Agent to NetBox Safely (Read-Only, 2026)

Pointing an AI agent at your network source of truth? Four ways to enforce read-only — service accounts, write-disabled tokens, a gateway, or a curated tool allow-list — and when each is enough.

S
Sarah Chen
Network Engineer

Your NetBox instance knows your network better than any diagram does. That makes it the obvious thing to give an AI agent — and the obvious thing to be careful with. The question engineers actually ask isn't "can I connect an agent to NetBox?" It's "can I connect one without it ever writing to my source of truth?"

The answer is yes, and there are four different ways to get there. They are not equivalent, and the difference matters most on the day something is misconfigured.

The four ways to enforce read-only

ApproachWhat enforces itBlocks writesLimits which dataSurvives a misconfigEffort
Read-only API tokenNetBox rejects non-GET for the token⚠️ one flagMinutes
Scoped service accountNetBox object permissions⚠️ objects, not fields⚠️ permission drift~1 hour
Gateway / proxyYour code allow-lists GET + paths⚠️ your codeDays
Curated tool allow-listWrite tools never offered to the agent❌ tools, not fields⚠️ names, not behaviourMinutes

Bottom line: no single row is sufficient. A write-disabled token is the correct floor and takes five minutes; a curated allow-list stops write tools from ever being offered, but it constrains which tools the agent can call, not which fields come back — and NetBox object permissions gate which objects a user may view, not which fields come back on them. If you need local_context_data or custom_fields kept away from the model, response shaping in a gateway is the only thing that does it. The meaningful upgrade is moving from read-only by policy to read-only by construction, where the write tools simply aren't in the agent's world. NetPilot ships that model: the agent is offered only a curated allow-list of read tools, by exact name.

Start with the floor: a write-disabled token

Whatever else you build, do this first. It is the control most likely to be there when everything else fails.

Create a dedicated user — never reuse a human admin account — and grant it only the view permissions the agent needs:

Username: ai-netbox-reader
Object permissions: view only
Object types: dcim.device, dcim.interface, dcim.site,
              ipam.prefix, ipam.ipaddress, ipam.vlan

Then create an API token for that user with Write enabled unset. NetBox rejects POST, PATCH, PUT and DELETE for such a token. Set an expiration, and restrict the token by source IP. Note whose address that is: this token lives inside your MCP server or gateway, so that component originates the REST calls to NetBox — allow-list its egress address, not the AI agent's:

User: ai-netbox-reader
Write enabled: false
Expires: 90 days
Allowed IPs: 203.0.113.10/32   # your MCP server / gateway egress

Two things this does not do, and they are the reason people keep going:

  • It does not limit which read endpoints get called. A read-only token will happily return every device in a 5,000-device estate, or local_context_data and journal entries that carry credentials and customer names.
  • It is one flag — and it is only as strong as the account under it. If you did the step above, the user holds view permissions only, so NetBox refuses the write even with the flag enabled. That is the layering working. But issue a token against an account that can write and the flag is the only thing standing there, which is exactly the shortcut people take under time pressure. Keep the scoped account as its own layer rather than leaning on the flag.

NetBox also exposes a read-only GraphQL API, which is a good fit for query-only agents because the agent asks for exactly the fields it needs. It is still an unbounded query surface, so it does not remove the need for the layers below.

The DIY ceiling: a gateway in front of NetBox

The pattern most teams land on when the token isn't enough is a thin service between the agent and NetBox that allows only GET, allow-lists paths, strips sensitive fields, and enforces page limits:

from urllib.parse import urljoin
 
# Trailing slash matters: urljoin against ".../netbox" would discard the
# last path segment, so normalise the base once at startup.
BASE = NETBOX_URL.rstrip("/") + "/"
 
ALLOWED_ENDPOINTS = {
    "devices": "api/dcim/devices/",
    "interfaces": "api/dcim/interfaces/",
    "prefixes": "api/ipam/prefixes/",
}
 
# The allow-list that actually does the redaction work. Anything not named
# here — local_context_data, journal entries, custom fields — never reaches
# the model, because the response is rebuilt rather than forwarded.
SAFE_FIELDS = {"id", "name", "display", "status", "site", "role", "primary_ip4"}
 
 
def netbox_get(resource: str, params: dict | None = None) -> dict:
    if resource not in ALLOWED_ENDPOINTS:
        raise ValueError(f"Resource not allowed: {resource}")
 
    params = dict(params or {})
    params.setdefault("limit", 50)
    # NetBox reads limit=0 as "every record, up to MAX_PAGE_SIZE", so an
    # upper bound alone is not a cap. Require an explicit 1..100.
    if not 1 <= int(params["limit"]) <= 100:
        raise ValueError("limit must be between 1 and 100")
 
    response = session.get(urljoin(BASE, ALLOWED_ENDPOINTS[resource]),
                           params=params, timeout=10)
    response.raise_for_status()
    payload = response.json()
 
    return {
        "count": payload.get("count"),
        "results": [_project(item) for item in payload.get("results", [])],
    }
 
 
def _project(item: dict) -> dict:
    """Rebuild a record from allow-listed fields only.
 
    Nested objects are flattened to their label. Keeping `site` whole would
    forward every child key NetBox happens to attach to it -- description,
    custom fields, tags -- none of which are in SAFE_FIELDS.
 
    Two nested shapes matter: related objects carry `display`/`name`, while
    choice fields like `status` carry `label`/`value`. Handle both, or status
    silently projects to None.
    """
    labels = ("display", "name", "label", "value")
    out = {}
    for key in SAFE_FIELDS:
        if key not in item:
            continue
        value = item[key]
        if isinstance(value, dict):
            out[key] = next((value[k] for k in labels if value.get(k)), None)
        else:
            out[key] = value
    return out

This is genuinely good engineering and it is the right answer when you need field-level redaction, per-tenant constraints, or an audit trail in your own SIEM. It also means you now own a service: its deployment, its dependency updates, its own credential handling, and the drift between what it allows and what NetBox actually returns.

The honest trade: the gateway is where the guarantee now lives. You have moved the risk from a NetBox flag to your own code. That is an improvement if you maintain it, and a liability if it becomes the thing nobody has touched in eight months.

Read-only by construction: never offer the write tools

The fourth approach changes the question. Instead of blocking writes, don't put them in the agent's world.

This is how NetPilot's source-of-truth connectors work. Each provider in the catalog declares a curated allow-list of read-only tool names — exact names, never wildcards — and when you attach a connection, the agent is offered exactly the intersection of what your server advertises and what that allow-list contains.

The consequence is the useful part. Because a connection is only a URL you supply, declaring a connection as netbox doesn't prove the server behind it is stock NetBox. If you point it at a write-capable fork or a wrapper that advertises create and delete tools, those tools are not in the allow-list, so they are never pre-approved. The agent never sees them. There is no rule to misconfigure, because there is no rule — there is an absence.

Be precise about what that does and doesn't cover, because the distinction matters when you're the one signing off on it. The allow-list constrains which tool names the agent may call. It cannot inspect what your server does behind a name — a custom endpoint that advertises netbox_get_objects and quietly writes on the way through would still pass the intersection. The guarantee is verified against the upstream server implementations the catalog is pinned to; it is not a proof about arbitrary code you point it at.

Which is the argument for stacking layers rather than picking one. Keep a write-disabled NetBox token underneath the connector and the enforcement no longer depends on your MCP server behaving: NetBox itself rejects the write, whoever asked.

The same shape applies to Nautobot: all the get, list, search and GraphQL read tools are included, and the four mutating tools its community server advertises — rest_create, rest_update, rest_delete and run_job — are deliberately excluded from the catalog.

Around that core, three supporting properties:

  • Tokens are write-only secrets. Encrypted at rest, never displayed again after you save them. Endpoints must be HTTPS, and outbound requests pass an SSRF guard.
  • Enabling is gated on a live test. Adding a connector runs a real handshake against your endpoint; it is only enabled when the test passes. A misconfigured endpoint fails closed rather than sitting there half-wired.
  • Setup is a URL and a token. In the chat composer: +Connectors → label, HTTPS URL, token → Add & test connection.

Two different tokens — this is the one people get wrong. The NetBox API token from the section above goes into your MCP server, so the server can read NetBox. The token you paste into NetPilot is your MCP server's own endpoint bearer — the credential it requires for inbound connections. Paste the NetBox API token into the connector and the live handshake fails.

Live device state is a separate decision

Reading your source of truth and reading your live network are different risks, and they should be different switches.

NetPilot keeps them separate. The Nornir connector is a live-CLI executor exposing exactly three tools — list_devices, get_device and run_show — and the server enforces a show-only guard on run_show. It is opt-in: connecting NetBox does not connect your devices.

The credential model is the part worth checking against any vendor you evaluate: device credentials are brokered from your Nautobot at call time. NetPilot stores neither the credentials nor the inventory. There is no copy of your device passwords sitting in a SaaS database waiting to be a breach headline, because there is no copy.

Worth being precise here: Nornir reads live device output. It is not source-of-truth ingest, and it never writes configuration. Reading your NetBox records and running show commands against production are two different capabilities, and conflating them is how people end up over-scoping access.

What this unlocks once it's safe

Guardrails aren't the point — they're what makes the point reachable. With read-only access in place, the agent can answer questions against your real records:

"Find a free /28 in the datacenter prefix and tell me which VLAN it belongs to."

And it can go further than answering. Because the agent can read your source of truth and build labs, it can construct a runnable replica of a site from your own records — real network operating systems you can SSH into — so you can rehearse a change before the maintenance window. That workflow is covered in building a network digital twin from NetBox, and the pre-change loop it feeds is network change validation.

The other high-value read-only workflow is drift: compare what your source of truth says against what the devices actually report, and bucket the differences into stale records, live violations, and verified matches. Nothing is written anywhere — it is two reads and a diff.

Copy-paste ready: Source-of-Truth Drift Detection and Source-of-Truth Queries.

Which approach should you choose?

  • Prototyping, or a single engineer exploring — write-disabled token, scoped service account, done. Do not build a gateway for this.
  • You need field-level redaction or per-tenant constraints — build the gateway. Nothing else gives you arbitrary response shaping, and that requirement is real in multi-tenant environments.
  • You want the agent useful without writing the integration — a curated allow-list connector. Be clear on what this removes and what it doesn't: you no longer build or maintain a bespoke gateway, but you still host the MCP server, hold its credentials, and keep it updated. It trades custom code for standard plumbing, not for no plumbing.
  • You need the agent to touch live devices — keep that a separate, opt-in connector with a show-only guard and brokered credentials. Never fold it into source-of-truth access.

These are not mutually exclusive, and the strongest posture stacks them: a write-disabled token and a scoped service account and a tool surface where writes were never offered. Budget roughly an hour end to end — the permission scoping is the part that actually takes time; the token and the connector are about ten minutes between them. That is a cheap afternoon for a control that holds when one of the other layers is misconfigured.

FAQ

Is a read-only NetBox API token enough to make an AI agent safe?

It is the right starting point but not the whole control. A write-disabled token rejects POST, PATCH, PUT and DELETE at the API, which stops accidental mutation. What it does not do is constrain which read endpoints the agent reaches, how much it pulls, or which fields reach the model — config contexts, journal entries and custom fields can carry sensitive data that a read-only token happily returns. Pair it with a narrow tool surface and field scoping.

What is the difference between read-only by policy and read-only by construction?

Read-only by policy means write operations exist in the agent's world and something is expected to refuse them: a token flag, a permission check, a proxy rule, or a prompt asking the model not to. Read-only by construction means the write tools are never offered to the agent at all — no call to attempt, no rule to misconfigure. The test is what happens when one layer is wrong. Under policy, a bad flag exposes writes — provided the account behind it can write at all, which is exactly why the scoped view-only account is its own layer and not a nicety. Under construction, a tool that was never offered cannot be called.

One limit, stated plainly: an allow-list matches tool names, not behaviour. A custom or wrapped server implementing writes behind an allowed name would still pass, which is why the layer that holds regardless of what your server does is a write-disabled NetBox token underneath. Stack both.

Can I connect NetBox Cloud's hosted MCP server to NetPilot?

The verified path today is a NetBox MCP server that you host. NetPilot v1 connectors authenticate with a single static bearer token over HTTPS, and the hosted NetBox Cloud Platform MCP has not been tested against NetPilot — so we do not claim it works. If you run your own NetBox MCP server, that is the supported configuration.

Does connecting NetBox to an AI agent send my network data to a third party?

Be precise about what the question means. With any hosted AI agent, the records your server returns do travel to the service — the model has to see them to answer. NetPilot is no exception: the connector queries your own NetBox instance, and the returned records enter the agent's working context for that conversation. What NetPilot does not do is ingest or copy your source of truth into a separate store of its own.

Those are different claims, and a vendor answering only the second one is not answering the first. Whatever platform you evaluate — including this one — ask three specific things: what leaves my environment, how long is it retained, and is it used for training. Get the answers in writing.

How do I let an AI agent read live device state without giving it write access?

Separate the two capabilities into different connectors and keep the live path show-only. NetPilot's Nornir connector exposes exactly three tools — list_devices, get_device, run_show — and the server enforces a show-only guard on run_show. Device credentials are brokered from your Nautobot at call time rather than stored. That keeps live reads opt-in and separate from source-of-truth reads.

What should I audit before pointing any AI agent at my source of truth?

Four things. One: the exact list of tools or endpoints the agent can call, by name — not a category description. Two: how credentials are stored, and whether they can be read back after entry. Three: whether the connection is verified before it is enabled, so a misconfigured endpoint fails closed. Four: what is logged, so you can answer what the agent queried and when. If a vendor cannot give you the tool list by name, that is the answer.


Read-only is a design decision, not a setting — and the difference shows up on the day a flag is wrong. See the full connector catalog and security model on the integrations hub, or start a lab and connect your source of truth in about two minutes.

Try NetPilot Free

Build enterprise-grade network labs in seconds with AI assistance

Get Started Free