nautobot-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nautobot-mcpPlan the dependencies to create a device"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
nautobot-mcp
An MCP server for Nautobot, built for instances whose API is too large to enumerate: Nautobot 3.2 ships 1,673 REST operations across 477 paths, and every installed app adds more. This server exposes 15 schema-driven tools instead of one tool per endpoint, so the whole API — core and plugins alike — is reachable without flooding an agent's context.
What makes it work
A build step, not runtime parsing. Nautobot's OpenAPI document is 18 MB and its GraphQL introspection another 10 MB. A build script fuses them into a ~1.1 MB SQLite index with an FTS5 search table. The server opens it read-only and answers lookups in microseconds; start-up does not depend on the size of the API.
Foreign keys recovered from GraphQL. OpenAPI alone cannot describe Nautobot's relationships — every related field serialises as an identical opaque object:
// dcim.device: device_type, role, status and location are indistinguishable here
"device_type": { "id": {...}, "object_type": {"pattern": "^[a-z]+\\.[a-z]+$"}, "url": {...} }GraphQL's type system names the targets outright (device_type → DeviceTypeType), so the
two are joined on the OpenAPI component name to recover 441 typed FK edges. That graph
is what makes dependency planning possible.
Filters compressed. dcim.device exposes 250 filter parameters, which are really ~74
base fields times a family of lookup suffixes (__ic, __n, __isnull, __gte, …).
The index stores base fields plus their suffix sets and describes the vocabulary once.
Related MCP server: Advanced Hasura GraphQL MCP Server
Install
uv venv && uv pip install -e ".[dev]"
cp .env.example .env # then set NAUTOBOT_URL and NAUTOBOT_TOKEN
cp .mcp.json.example .mcp.json # optional: for stdio-based clients
python -m nautobot_mcp.schema.build --probeOr skip the checkout entirely and run it in a container — see Docker.
The build step fetches the schemas and writes var/index.sqlite. Re-run it after
installing or upgrading a Nautobot app — or call the nautobot_refresh_schema tool.
Configuration
Variable | Default | Purpose |
| — | Base URL, e.g. |
| — | API token |
|
| Master gate for create/update/delete |
|
| TLS verification |
|
| Per-request timeout (seconds) |
|
| Where schema sources and the index live |
|
| Ceiling on |
Container-only knobs, read by the entrypoint rather than the server:
Variable | Default | Purpose |
|
| Transport the container serves ( |
|
| Bind address for HTTP transports |
|
| Bind port for HTTP transports |
|
| Build a missing schema index on start instead of refusing to run |
Register with a client
{
"mcpServers": {
"nautobot": {
"command": "/path/to/nautobot-mcp/.venv/bin/python",
"args": ["-m", "nautobot_mcp"],
"env": {
"NAUTOBOT_URL": "http://nautobot.example.com:8080",
"NAUTOBOT_TOKEN": "...",
"NAUTOBOT_CACHE_DIR": "/path/to/nautobot-mcp/var"
}
}
}
}HTTP transports are available too: python -m nautobot_mcp --transport streamable-http --port 8000.
Docker
cp .env.example .env # then set NAUTOBOT_URL and NAUTOBOT_TOKEN
docker compose up -d # or: make docker-upThe first start builds the schema index against your instance and stores it on the
index volume; later starts reuse it. The server listens on 127.0.0.1:8000/mcp.
The index is not baked into the image, and cannot be: it is fused from the schemas of one
specific Nautobot instance, including whatever apps that instance has installed. Rebuild
it after installing or upgrading an app — make docker-index, or the
nautobot_refresh_schema tool, which writes to the same volume.
make docker-index # rebuild the index in place
make docker-logs # follow the server log
make docker-down # stop; VOLUMES=1 also drops the index
docker compose run --rm server index --offline # rebuild from cached sources onlyRegistering the container with a client
Over HTTP, point the client at the published port:
{
"mcpServers": {
"nautobot": { "url": "http://127.0.0.1:8000/mcp" }
}
}Or let the client spawn a container per session over stdio, reusing the same index volume:
{
"mcpServers": {
"nautobot": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"--env-file", "/path/to/nautobot-mcp/.env",
"-e", "MCP_TRANSPORT=stdio",
"-v", "nautobot-mcp_index:/data",
"nautobot-mcp:latest"
]
}
}
}Anything passed after the image name goes straight to python -m nautobot_mcp, so
docker run ... nautobot-mcp:latest --transport sse --host 0.0.0.0 --port 8000 works too.
What the compose file assumes
The port is published on loopback only. Section Security applies in full: this is an unauthenticated proxy holding a token with your permissions, so reaching it from another host means putting authentication in front of it, not widening the port mapping.
Writes stay off unless
NAUTOBOT_ALLOW_WRITE=trueis in your.env.The container is hardened by default — non-root (uid 1000), read-only root filesystem, all capabilities dropped,
no-new-privileges. The only writable path is the/datavolume, which is where the index and its cached sources belong.Health is a TCP connect, not an MCP request: an unsessioned request to
/mcpmakes the session manager allocate a transport that nothing reaps, so probing the protocol every 30s would leak a session per probe..envis read verbatim by compose. Keep comments on their own line; a trailing# commentis not reliably stripped from a value.
Tools
Tool | Purpose |
| Find models by name, description or field name |
| Fields, required fields, FK targets, filters, actions |
| App namespaces (core and plugin), versions, index state |
| Ordered prerequisites for creating an object |
| Human name → UUID, scoped to the referring model |
| Read any model, slimmed or projected |
| Gated writes |
| Arbitrary GraphQL queries |
| Introspection, a type at a time |
| Non-CRUD endpoints ( |
| Any REST endpoint — plugins, bulk ops, custom actions |
| Re-fetch schemas and rebuild the index |
Model references are forgiving: dcim.device, device, devices, Device,
/dcim/devices/ and DeviceType all resolve, and typos get suggestions
(dvice → "Did you mean: dcim.device?").
Dependency planning
Creating a Device on an empty instance means creating four other objects first.
nautobot_plan_create("dcim.device") walks the FK graph, checks the live instance for
what already exists, and returns them in order:
dcim.manufacturer → dcim.devicetype → dcim.locationtype → dcim.location → extras.role → dcim.deviceIt also handles Nautobot's content-type scoping. Role, Status and Tag are only
assignable to models listed in their content_types. A global count is the wrong
question — an instance can hold 20 Roles while none apply to a Device:
{
"model": "extras.role",
"action": "create", // not "use_existing", despite 20 existing
"content_type_scoped": true,
"by_referrer": { "dcim.device": { "valid_count": 0 } },
"note": "No extras.role is assignable to dcim.device yet. Create one with
content_types including ['dcim.device'] ..."
}Which models scope this way is discovered, not hardcoded: content_types means
"what may live here" on LocationType and "who may reference me" on Role. The planner
tries the scoped query and treats a 400 as proof that scoping does not apply — so
plugin models behave correctly with no extra code.
Writes
Writes are off until NAUTOBOT_ALLOW_WRITE=true. Even then, mutations are two-step: the
first call returns a preview and a confirm_token, and the call is repeated with that
token to apply it. Tokens are derived from the payload, so one issued for one body cannot
be replayed against another. nautobot_update previews a field-level diff;
nautobot_delete previews the object and everything that references it.
Security
This server is an unauthenticated privileged proxy to Nautobot. It holds an API token and performs no authentication of its own: any client that can reach it acts with that token's full permissions, without ever possessing the token.
Defaults are deliberately safe — --host binds 127.0.0.1 and NAUTOBOT_ALLOW_WRITE is
false. The risky configuration is combining a non-loopback bind with writes enabled,
which grants unauthenticated create/update/delete over your source of truth to anything
that can route to the port.
The confirm-token flow is an accident guard, not an access control — any client can read the token from the preview response and confirm immediately.
If the server must be reachable by other hosts, put authentication in front of it (a reverse proxy with mTLS, an OAuth-aware gateway, or an SSH tunnel) and give it a Nautobot token scoped to only what the agent needs. See SECURITY.md.
Responsiveness
One pooled HTTP/2 client is shared across tools; the planner fans out existence checks concurrently.
Responses are slimmed before reaching the agent. Nautobot has no sparse-fieldset support (
?fields=is rejected as an unknown filter), sourl,natural_slug,notes_url, timestamps and empty custom-field blocks are dropped client-side, and nested related objects are reduced to identity. Passfields=[...]to project, orfull=trueto opt out.
Extending
Each toolset is a module exposing register(server, ctx), listed in
tools/__init__.py::TOOLSETS. Registration is wrapped so every tool returns a structured
error instead of raising — an uncaught exception would reach the agent as an opaque
"Error executing tool X".
Plugin endpoints need no code: they appear in /api/swagger.json, so rebuilding the
index makes them available to every tool.
Tests
pytest82 tests run against fixtures carved from the live schema, with HTTP mocked via respx.
They pin the traps found while building this: the slug collision that maps
virtualization.vminterface onto DCIM's InterfaceType, the content_types scoping
that silently produces unusable plans, and the FK heuristic that resolves
DynamicGroupMembership.group to Django's auth.Group instead of extras.DynamicGroup.
Agent configuration
AGENT.md contains a ready-to-use system prompt and registry description for an agent driving this server, including the write protocol and the content-type scoping rule that most commonly causes a create to fail.
Contributing
Issues and pull requests are welcome. pytest must pass and ruff check / ruff format --check must be clean; CI enforces both across Python 3.11-3.13. The suite needs no
Nautobot instance and no network — it runs against schema fixtures in tests/fixtures
with HTTP mocked by respx.
License
Apache 2.0 - see LICENSE.
Layout
src/nautobot_mcp/
schema/build.py fuses OpenAPI + GraphQL + content types into the index
schema/index.py read-only query layer (lookup, FTS search, graph)
client.py pooled async HTTP, slimming, error normalisation
depgraph.py creation planning and reference resolution
safety.py write gate, confirm tokens, diffs
tools/ one module per toolset, registered through a guard
server.py MCP server assemblyThis server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables comprehensive interaction with NetBox infrastructure management through both read and write operations. Supports full CRUD operations for devices, IP addresses, sites, racks, and other NetBox objects through natural language commands.916Apache 2.0
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to interact with Hasura GraphQL endpoints to discover schema structures and execute queries or mutations. It provides specialized tools for table introspection, data previewing, and performing data aggregations through natural language.
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to interact with any GraphQL API by introspecting the schema and exposing queries and mutations as MCP tools, with built-in pagination, semantic search, and framework adapters.13MIT
- AlicenseNot gradedqualityDmaintenanceEnables LLMs to interact with GraphQL APIs through schema introspection and query execution.1,5161MIT
Related MCP Connectors
Gateway between LLM agents and world data through eight tools and a bundled endpoint catalog.
SaaS intelligence for AI agents. 5 unified tools cover 1,000+ services with 91-96% token savings.
Provides cloud browser automation capabilities using Stagehand and Browserbase, enabling LLMs to i…
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/shamalawy/nautobot-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server