Nautobot Integration Walkthrough
Date: May 2026
FMCP Version: 0.6
Agent: Claude.ai (Web based)
Claim: A Nautobot instance with three installed plugins was fully accessible to Claude.ai, Claude Code, Cursor, and GPT via MCP — with zero changes to Nautobot's source code.
The Standard We Are Held To
Nautobot follows the Network to Code (NTC) contribution standards. The core rule relevant here: do not modify the framework. Extensions go in plugins, configuration goes in nautobot_config.py, and the framework code stays as shipped. Any integration that edits nautobot/core/, monkey-patches a view, or injects middleware without a sanctioned hook violates this standard.
The integration documented here does not do any of those things.
What Was Running
Server: AWS EC2 T3.medium (2 vCPU, 4 GB RAM)
Nautobot Version: 3.x
MCP Endpoint: https://<url>/api/mcp
frisian-mcp Branch: dispatcher_enhancements
Installed Nautobot Plugins (unmodified):
nautobot-bgp-modelsnautobot-golden-confignautobot-dns-models
None of these plugin packages were modified. None of Nautobot's core packages were modified.
The Complete Integration — Two Files
The entire integration lives in Nautobot's configuration file. No Nautobot source file was edited. No plugin source file was edited. No Django app was created inside the Nautobot repository.
Step 1: INSTALLED_APPS — Two Lines
# nautobot_config.py (development)
# nautobot_config.prod.py (production)
INSTALLED_APPS.append("frisian_mcp")
INSTALLED_APPS.append("frisian_mcp.contrib.oauth")
INSTALLED_APPS is a standard Django list. Nautobot exposes it for exactly this purpose — adding apps without touching the framework. frisian_mcp is the core package. frisian_mcp.contrib.oauth adds the OAuth 2.0 token store so agents (Claude.ai, GPT) can authenticate via the standard PKCE flow.
Step 2: Settings — Configuration, Not Code
# MCP endpoint path — mounted at /api/mcp alongside Nautobot's own /api/
FRISIAN_MCP_PATH = "api/mcp"
# Anonymous callers see read-tier tools; authenticated callers get read_write
FRISIAN_MCP_UNAUTHENTICATED_TIER = "read"
# OAuth issuer — must match the public URL clients use to reach this server
FRISIAN_MCP_OAUTH_ISSUER = "https://thehealthygeek.com" # production
# FRISIAN_MCP_OAUTH_ISSUER = "http://localhost:8080" # development
# Authentication stack:
# 1. OAuth bearer token (Claude.ai, GPT PKCE flow)
# 2. frisian-mcp static API key (testing, scripts)
# 3. Nautobot's own token (admin/curl access)
FRISIAN_MCP_AUTHENTICATION_CLASSES = [
"frisian_mcp.contrib.oauth.authentication.OAuthTokenAuthentication",
"frisian_mcp.contrib.tokens.authentication.FrisianMcpApiKeyAuthentication",
"nautobot.core.api.authentication.TokenAuthentication",
]
# All callers must be authenticated at the MCP gateway layer
FRISIAN_MCP_PERMISSION_CLASSES = [
"rest_framework.permissions.IsAuthenticated",
]
# PKCE flow for Claude.ai and GPT:
# - Auto-approve: skip the HTML consent screen (agents cannot render HTML)
# - Auto-register: accept agent-generated client_ids without pre-registration
FRISIAN_MCP_OAUTH_AUTO_APPROVE = True
FRISIAN_MCP_OAUTH_PKCE_AUTO_REGISTER = True
FRISIAN_MCP_OAUTH_PKCE_DEFAULT_PERMISSION = "read_write"
# Map Nautobot user roles to MCP tiers
FRISIAN_MCP_TOKEN_TIER_MAP = {
"superuser": "read_write",
"staff": "read_write",
"default": "read",
}
Every line here is a setting assignment in nautobot_config.py. The Nautobot documentation explicitly describes this file as the place to configure installed apps. Nothing above touches a Nautobot class, view, serializer, or model.
Step 3: Dispatcher Groups — Tool Surface Configuration
Nautobot's REST API exposes over 1,500 resources across 15+ app boundaries. Sending all of them to an AI client's context window as individual tools would consume more tokens than the context window holds before a single tool call is made. The dispatcher configuration collapses this surface into named groups. This is also a setting — no code:
FRISIAN_MCP_DISPATCH_GROUPS = {
"dcim": [
"device", "rack", "rackgroup", "rackreservation",
"interface", "interfacetemplate",
"cable", "location", "locationtype",
"manufacturer", "devicetype", "devicefamily",
"platform", "inventoryitem",
"consoleport", "powerport", "powerpanel",
"module", "modulebay", "modulefamily", "moduletype",
"controller", "virtualchassis",
"softwareimagefile", "softwareversion",
# ... 30+ additional DCIM resources
],
"ipam": [
"ipaddress", "prefix", "vlan", "vlangroup",
"vrf", "routetarget", "iprange", "namespace",
"rir", "service",
# ... additional IPAM resources
],
"circuits": ["circuit", "circuittype", "circuittermination", "provider", "providernetwork"],
"tenancy": ["tenant", "tenantgroup", "contact", "contactassociation", "team"],
"virtualization": ["cluster", "clustergroup", "virtualmachine", "vminterface"],
"wireless": ["wirelessnetwork", "radioprofile", "supporteddatarate"],
"cloud": ["cloudaccount", "cloudnetwork", "cloudresourcetype", "cloudservice"],
"vpn": ["vpn", "vpnphase1policy", "vpnphase2policy", "vpntunnel"],
"load_balancers": ["loadbalancerpool", "virtualserver", "certificateprofile"],
"users": ["user", "group", "token", "objectpermission"],
"approvalworkflow":["approvalworkflow", "approvalworkflowdefinition", "approvalworkflowstage"],
"data_validation": ["minmaxvalidationrule", "regularexpressionvalidationrule", "uniquevalidationrule"],
"extras": ["tag", "customfield", "relationship", "dynamicgroup", "job", "webhook",
"role", "status", "secret", "note", "objectchange"],
# Plugin resources — configured the same way as core resources
"golden_config": ["goldenconfig", "goldenconfigsetting", "compliancefeature",
"compliancerule", "configcompliance", "configremove", "configreplace",
"remediationsetting", "configplan"],
"bgp": ["autonomoussystem", "autonomoussystemrange", "peergroup",
"peerendpoint", "peering", "bgproutinginstance"],
"dns": ["dns_views", "dns_zones", "ns_records", "a_records",
"aaaa_records", "cname_records", "mx_records", "txt_records"],
}
The plugin groups (golden_config, bgp, dns) are configured identically to the core groups. The plugin packages were installed by Nautobot's normal plugin mechanism. frisian-mcp reads their OpenAPI schemas at startup the same way it reads Nautobot core's schemas.
What Agents Could Do After Configuration
The following results were produced on 2026-05-06, against the live server, with no human writing or guiding the operations.
Dispatchers Exposed
| Group | Resources | Tools Available |
|---|---|---|
Nautobot:dcim |
53 | 530+ |
Nautobot:bgp |
10 | 100 |
Nautobot:golden_config |
9 | 89 |
Nautobot:approvalworkflow |
7 | 53 |
Nautobot:circuits |
5 | 51 |
| Total | 84+ | 634+ |
634 flat Nautobot API resources, accessible as 5 logical dispatcher groups (plus all other configured groups above). Every CRUD operation available via Nautobot's REST API is available via MCP.
GPT: Infrastructure Build From Scratch
GPT was given the following task (verbatim from nautobot_gpt_build_instructions.md):
"You have access to an MCP service called Nautobot MCP Tool. Your task is to use this tool to construct a complete integrated environment from scratch. You start with nothing."
No API keys were given to GPT. No Nautobot topology was pre-loaded. GPT used the dispatcher's help action to discover what resources existed, then planned and executed a full build. Result (validated by a second agent):
| Metric | Result |
|---|---|
| Geographic locations | 3 (NYC Metro Core, Chicago Regional DR, Denver Edge Campus) |
| Total devices | 65 |
| Device types | Routers, core switches, aggregation switches, access switches |
| WAN circuits | 2 (NYC↔Chicago, Chicago↔Denver) |
| Build errors | 0 |
| Build completeness | 100% specification compliance |
Claude.ai: Dispatcher Validation and Plugin Object Creation
Claude.ai (claude-sonnet-4-6) ran a separate validation session on the same server. Session duration: 30 minutes. Error count: zero.
Objects created via MCP (BGP Models plugin):
| ASN | Description | UUID |
|---|---|---|
| 65001 | NYC Enterprise | b0e5b9c9-6e88-4c76-8897-676b5ea6ab0b |
| 65002 | NYC Transit | e71837d5-7d7e-45e3-9761-14be762b37c5 |
| 65003 | Chicago Enterprise | 0b2722b5-a030-4ca4-93b7-33153ba31aa1 |
| 65004 | Chicago Transit | 4470ca72-d068-449e-b28c-19022dcd8948 |
| 65005 | Denver Enterprise | 11dd192d-8f11-4f48-8675-7a273d04e7df |
| 65006 | Denver Transit | 42c113bd-beb6-4f44-9c81-74ca91e24a96 |
Objects created via MCP (Golden Config plugin):
- Compliance Feature: "NTP Configuration Check" (
750ad595-c24d-4f24-92c8-11f25645706d)
All 8 objects created on first attempt. The plugin objects were created exactly the same way as core Nautobot objects — same dispatcher pattern, same tool call format, same authentication flow.
Token efficiency measured:
| Dataset | Without pagination | With @mcp_heavy |
Savings |
|---|---|---|---|
| 65 devices (tested) | ~40,300 tokens | ~31,000 tokens | 23% |
| 500 devices (projected) | ~310,000 tokens | ~31,000 tokens | 90% |
| 2,000 devices (projected) | ~1,240,000 tokens | ~31,000 tokens | 97% |
At 500+ devices, an unpaginated response would consume the entire context window before a single operation could be performed. The @mcp_heavy decorator — configured, not coded — applies automatic pagination to prevent this.
What Was Not Touched
The following directories contain only unmodified, as-shipped code:
nautobot/nautobot/ — Nautobot core framework
nautobot/nautobot/core/ — Core views, models, authentication, API
nautobot/nautobot/dcim/ — DCIM app
nautobot/nautobot/ipam/ — IPAM app
nautobot/nautobot/circuits/ — Circuits app
nautobot/nautobot/extras/ — Extras app
No file in any of these directories was opened for editing during integration. The git history of the Nautobot repository is clean — no frisian-mcp related commits in the core packages.
Plugin packages (nautobot-bgp-models, nautobot-golden-config, nautobot-dns-models) were also unmodified. They were installed via the standard Nautobot plugin mechanism and registered in PLUGINS in nautobot_config.py, as their own documentation requires.
Why This Matters
Nautobot is a shared platform. NTC and enterprise operators run it across teams. An integration that modifies core code creates an upgrade dependency — every Nautobot release requires the integration patch to be rebased, tested, and re-applied. A misconfigured patch breaks everyone on that instance.
An integration that only touches nautobot_config.py carries no upgrade risk. When Nautobot releases a new version, pip install --upgrade nautobot runs clean. The frisian-mcp settings in nautobot_config.py remain in place. The MCP endpoint re-registers at startup by reading the new version's OpenAPI schema.
This is the difference between a plugin and a patch. The integration demonstrated here is a plugin.
Summary
| What | How | Where |
|---|---|---|
| Package install | pip install frisian-mcp |
Standard Python packaging |
| App registration | INSTALLED_APPS.append(...) |
nautobot_config.py only |
| MCP endpoint | FRISIAN_MCP_PATH = "api/mcp" |
nautobot_config.py only |
| Authentication | FRISIAN_MCP_AUTHENTICATION_CLASSES |
nautobot_config.py only |
| Tool surface | FRISIAN_MCP_DISPATCH_GROUPS |
nautobot_config.py only |
| Nautobot core changes | None | — |
| Plugin source changes | None | — |
Claude.ai, Claude Code, Cursor, and GPT connected to the same endpoint using the same OAuth PKCE flow. All four clients used the tools/list → tools/call protocol without any client-specific configuration on the server side. The server does not know or care which client is calling — it authenticates the token, checks the tier, and dispatches the tool.
Source data: — session reports, validation logs, and build task outputs from live server runs on 2026-05-06.
Configuration source: nautobot/development/nautobot_config.py and nautobot/development/nautobot_config.prod.py — the only files modified during integration.