Skip to main content
Glama

nautobot-mcp

CI Python License

Nautobot용 MCP 서버로, API가 너무 커서 전체를 열거할 수 없는 인스턴스를 위해 설계되었습니다. Nautobot 3.2는 477개 경로에 걸쳐 1,673개의 REST 연산을 제공하며, 설치된 앱마다 더 늘어납니다. 이 서버는 엔드포인트마다 하나의 도구를 만드는 대신 15개의 스키마 기반 도구를 노출하므로, 코어와 플러그인을 포함한 전체 API를 에이전트의 컨텍스트를 넘치게 하지 않고 사용할 수 있습니다.

작동 원리

런타임 파싱이 아닌 빌드 단계. Nautobot의 OpenAPI 문서는 18MB이고 GraphQL introspection은 추가로 10MB입니다. 빌드 스크립트가 이 둘을 FTS5 검색 테이블이 포함된 약 1.1MB의 SQLite 인덱스로 융합합니다. 서버는 이를 읽기 전용으로 열고 마이크로초 단위로 조회에 응답합니다. 시작 시간은 API 크기에 의존하지 않습니다.

GraphQL에서 복원된 외래 키. OpenAPI만으로는 Nautobot의 관계를 설명할 수 없습니다. 모든 관련 필드는 동일한 불투명 객체로 직렬화되기 때문입니다:

// dcim.device: device_type, role, status and location are indistinguishable here
"device_type": { "id": {...}, "object_type": {"pattern": "^[a-z]+\\.[a-z]+$"}, "url": {...} }

GraphQL의 타입 시스템은 대상을 명시적으로 지정하므로(device_type → DeviceTypeType), OpenAPI 컴포넌트 이름을 기준으로 두 시스템을 조인하여 441개의 타입이 지정된 FK 엣지를 복원합니다. 이 그래프가 의존성 계획을 가능하게 합니다.

필터 압축. dcim.device는 250개의 필터 파라미터를 노출하는데, 이는 실제로 약 74개의 기본 필드에 조회 접미사 계열(__ic, __n, __isnull, __gte, …)을 곱한 것입니다. 인덱스는 기본 필드와 해당 접미사 집합을 저장하고 어휘를 한 번만 설명합니다.

Related MCP server: mcp-graphql

설치

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 --probe

또는 체크아웃 없이 컨테이너에서 바로 실행할 수 있습니다 — Docker 참조.

빌드 단계는 스키마를 가져와 var/index.sqlite를 작성합니다. Nautobot 앱을 설치하거나 업그레이드한 후 다시 실행하거나, nautobot_refresh_schema 도구를 호출하세요.

구성

변수

기본값

용도

NAUTOBOT_URL

—

기본 URL, 예: http://nautobot.example.com:8080

NAUTOBOT_TOKEN

—

API 토큰

NAUTOBOT_ALLOW_WRITE

false

생성/수정/삭제의 마스터 게이트

NAUTOBOT_VERIFY_SSL

true

TLS 검증

NAUTOBOT_TIMEOUT

30

요청당 타임아웃(초)

NAUTOBOT_CACHE_DIR

./var

스키마 소스와 인덱스가 저장되는 위치

NAUTOBOT_MAX_PAGE

1000

fetch_all 페이지네이션 상한

서버가 아닌 엔트리포인트가 읽는 컨테이너 전용 설정:

변수

기본값

용도

MCP_TRANSPORT

streamable-http

컨테이너가 제공하는 전송 방식(stdio는 클라이언트가 생성한 컨테이너용)

MCP_HOST

0.0.0.0

HTTP 전송의 바인드 주소

MCP_PORT

8000

HTTP 전송의 바인드 포트

NAUTOBOT_AUTO_INDEX

true

실행을 거부하는 대신 시작 시 누락된 스키마 인덱스를 빌드

클라이언트에 등록

{
  "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 전송도 사용할 수 있습니다: 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-up

첫 시작 시 인스턴스에 대해 스키마 인덱스를 빌드하여 index 볼륨에 저장하고, 이후 시작에서는 이를 재사용합니다. 서버는 127.0.0.1:8000/mcp에서 수신합니다.

인덱스는 이미지에 포함되지 않으며 포함할 수도 없습니다. 특정 Nautobot 인스턴스의 스키마(해당 인스턴스에 설치된 모든 앱 포함)에서 융합되기 때문입니다. 앱을 설치하거나 업그레이드한 후 재빌드하세요 — make docker-index 또는 동일한 볼륨에 쓰는 nautobot_refresh_schema 도구를 사용합니다.

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 only

컨테이너를 클라이언트에 등록

HTTP를 통해 클라이언트를 게시된 포트로 지정합니다:

{
  "mcpServers": {
    "nautobot": { "url": "http://127.0.0.1:8000/mcp" }
  }
}

또는 클라이언트가 세션마다 stdio를 통해 컨테이너를 생성하고 동일한 인덱스 볼륨을 재사용하게 합니다:

{
  "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"
      ]
    }
  }
}

이미지 이름 뒤에 전달되는 모든 것은 python -m nautobot_mcp로 직접 전달되므로, docker run ... nautobot-mcp:latest --transport sse --host 0.0.0.0 --port 8000도 작동합니다.

compose 파일이 가정하는 것

  • 포트는 루프백에서만 게시됩니다. 보안 섹션이 전체적으로 적용됩니다: 이는 사용자의 권한을 가진 토큰을 보유한 인증되지 않은 프록시이므로, 다른 호스트에서 접근하려면 포트 매핑을 넓히는 것이 아니라 앞에 인증을 배치해야 합니다.

  • .env에 NAUTOBOT_ALLOW_WRITE=true가 없는 한 쓰기는 비활성화됩니다.

  • 컨테이너는 기본적으로 강화됩니다 — 비루트(uid 1000), 읽기 전용 루트 파일시스템, 모든 capabilities 제거, no-new-privileges. 유일한 쓰기 가능 경로는 인덱스와 캐시된 소스가 속한 /data 볼륨입니다.

  • 헬스 체크는 TCP 연결이지 MCP 요청이 아닙니다: /mcp에 대한 세션 없는 요청은 세션 관리자가 아무도 회수하지 않는 전송을 할당하게 하므로, 30초마다 프로토콜을 프로빙하면 프로브마다 세션이 누출됩니다.

  • compose는 .env를 그대로 읽습니다. 주석은 별도 줄에 유지하세요. 값 끝의 # comment는 안정적으로 제거되지 않습니다.

도구

도구

용도

nautobot_search_schema

이름, 설명 또는 필드 이름으로 모델 검색

nautobot_describe_model

필드, 필수 필드, FK 대상, 필터, 작업

nautobot_list_apps

앱 네임스페이스(코어 및 플러그인), 버전, 인덱스 상태

nautobot_plan_create

객체 생성의 순서화된 전제 조건

nautobot_resolve

사람이 읽는 이름 → UUID, 참조 모델 범위로 한정

nautobot_list / nautobot_get

모든 모델 읽기, 슬림 또는 프로젝션

nautobot_create / nautobot_update / nautobot_delete

게이트된 쓰기

nautobot_graphql

임의의 GraphQL 쿼리

nautobot_graphql_schema

한 번에 한 타입씩 introspection

nautobot_model_actions

비-CRUD 엔드포인트(trace, napalm, notes, …)

nautobot_call

모든 REST 엔드포인트 — 플러그인, 벌크 연산, 사용자 정의 작업

nautobot_refresh_schema

스키마 재조회 및 인덱스 재빌드

모델 참조는 유연합니다: dcim.device, device, devices, Device, /dcim/devices/, DeviceType 모두 해석되며, 오타에는 제안이 제공됩니다(dvice → "혹시 dcim.device를 찾으셨나요?").

의존성 계획

빈 인스턴스에서 Device를 생성하려면 먼저 다른 네 개의 객체를 생성해야 합니다. nautobot_plan_create("dcim.device")는 FK 그래프를 탐색하고, 라이브 인스턴스에 이미 존재하는 항목을 확인한 후 순서대로 반환합니다:

dcim.manufacturer → dcim.devicetype → dcim.locationtype → dcim.location → extras.role → dcim.device

또한 Nautobot의 content-type 범위 지정을 처리합니다. Role, Status, Tag는 content_types에 나열된 모델에만 할당할 수 있습니다. 전역 개수는 잘못된 질문입니다 — 인스턴스에 20개의 Role이 있어도 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'] ..."
}

어떤 모델이 이런 방식으로 범위가 지정되는지는 하드코딩이 아니라 발견됩니다: content_types는 LocationType에서는 "여기에 무엇이 존재할 수 있는지"를 의미하고 Role에서는 "누가 나를 참조할 수 있는지"를 의미합니다. 플래너는 범위 지정된 쿼리를 시도하고 400 응답을 범위 지정이 적용되지 않는다는 증거로 처리합니다 — 따라서 플러그인 모델도 추가 코드 없이 올바르게 동작합니다.

쓰기

NAUTOBOT_ALLOW_WRITE=true가 설정될 때까지 쓰기는 비활성화됩니다. 그 경우에도 변형은 두 단계로 이루어집니다: 첫 번째 호출은 미리보기와 confirm_token을 반환하고, 해당 토큰으로 호출을 반복하여 적용합니다. 토큰은 페이로드에서 파생되므로 한 본문에 대해 발급된 토큰을 다른 본문에 재생할 수 없습니다. nautobot_update는 필드 수준 diff를 미리보고, nautobot_delete는 객체와 이를 참조하는 모든 것을 미리봅니다.

보안

이 서버는 Nautobot에 대한 인증되지 않은 권한 있는 프록시입니다. API 토큰을 보유하며 자체 인증을 수행하지 않습니다: 서버에 도달할 수 있는 모든 클라이언트는 토큰을 소유하지 않고도 해당 토큰의 전체 권한으로 작동합니다.

기본값은 의도적으로 안전합니다 — --host는 127.0.0.1에 바인드되고 NAUTOBOT_ALLOW_WRITE는 false입니다. 위험한 구성은 루프백이 아닌 바인드와 쓰기 활성화를 결합하는 것으로, 포트에 라우팅할 수 있는 모든 것에 소스 오브 트루스에 대한 인증되지 않은 생성/수정/삭제 권한을 부여합니다.

confirm-token 흐름은 사고 방지 장치이지 접근 제어가 아닙니다 — 모든 클라이언트가 미리보기 응답에서 토큰을 읽고 즉시 확인할 수 있습니다.

서버를 다른 호스트에서 접근 가능하게 해야 한다면 앞에 인증을 배치하고(mTLS가 있는 리버스 프록시, OAuth 인식 게이트웨이, 또는 SSH 터널), 에이전트가 필요한 것에만 범위가 지정된 Nautobot 토큰을 부여하세요. SECURITY.md 참조.

응답성

  • 풀링된 하나의 HTTP/2 클라이언트가 도구 간에 공유되며, 플래너는 존재 확인을 동시에 분산합니다.

  • 응답은 에이전트에 도달하기 전에 슬림화됩니다. Nautobot은 희소 필드셋을 지원하지 않으므로(?fields=는 알 수 없는 필터로 거부됨), url, natural_slug, notes_url, 타임스탬프 및 빈 사용자 정의 필드 블록은 클라이언트 측에서 제거되고, 중첩된 관련 객체는 식별자로 축소됩니다. 프로젝션하려면 fields=[...]를 전달하고, 옵트아웃하려면 full=true를 전달하세요.

확장

각 도구 세트는 tools/__init__.py::TOOLSETS에 나열된 register(server, ctx)를 노출하는 모듈입니다. 등록은 모든 도구가 예외를 발생시키는 대신 구조화된 오류를 반환하도록 래핑됩니다 — 처리되지 않은 예외는 에이전트에게 불투명한 "Error executing tool X"로 도달할 수 있습니다.

플러그인 엔드포인트는 코드가 필요 없습니다: /api/swagger.json에 나타나므로 인덱스를 재빌드하면 모든 도구에서 사용할 수 있습니다.

테스트

pytest

라이브 스키마에서 추출한 픽스처에 대해 82개의 테스트가 실행되며, HTTP는 respx로 모킹됩니다. 이 테스트는 이 프로젝트를 구축하면서 발견한 함정들을 고정합니다: virtualization.vminterface를 DCIM의 InterfaceType에 매핑하는 slug 충돌, 조용히 사용할 수 없는 계획을 생성하는 content_types 범위 지정, 그리고 DynamicGroupMembership.group을 extras.DynamicGroup 대신 Django의 auth.Group으로 해석하는 FK 휴리스틱.

에이전트 구성

AGENT.md에는 이 서버를 구동하는 에이전트를 위한 바로 사용 가능한 시스템 프롬프트와 레지스트리 설명이 포함되어 있으며, 쓰기 프로토콜과 생성 실패의 가장 흔한 원인인 content-type 범위 지정 규칙이 포함되어 있습니다.

기여

이슈와 풀 리퀘스트를 환영합니다. pytest가 통과하고 ruff check / ruff format --check가 깨끗해야 합니다. CI는 Python 3.11-3.13에서 둘 다 강제합니다. 테스트 스위트는 Nautobot 인스턴스나 네트워크가 필요 없습니다 — tests/fixtures의 스키마 픽스처에 대해 respx로 HTTP를 모킹하여 실행됩니다.

라이선스

Apache 2.0 - LICENSE 참조.

구조

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 assembly

Related MCP Connectors

Related MCP Servers