Skip to main content

Nornir MCP server for network automation

Project description

🌐 Nornir MCP Server

License: MIT

A FastMCP server providing network automation tools powered by Nornir and NAPALM.

This server acts as a bridge, exposing Nornir/NAPALM network operations as MCP (Massively Concurrent Processing) tools, making them easily accessible from compatible MCP clients.

✨ Key Features

  • Concurrent & Multi-vendor: Leverages Nornir for inventory management and concurrent task execution against network devices using NAPALM for multi-vendor support.
  • Expanded Toolset: Provides over 20 tools, including a wide range of NAPALM getters (get_facts, get_interfaces), execution commands (ping, traceroute), and inventory management (list_all_hosts).
  • Robust Input Validation: Uses Pydantic models to validate all incoming data for tools, ensuring type safety and preventing errors from invalid inputs.
  • Secure Command Execution: Features a configurable command blacklist (conf/blacklist.yaml) to prevent accidental or malicious execution of dangerous commands like reload or erase startup-config via the send_command tool.
  • Containerized & Fast: Containerized with Docker 🐳 for easy setup and uses uv for lightning-fast Python dependency management within the container ⚡.

🔧 Prerequisites

Before you begin, ensure you have the following installed:

⚙️ Configuration

Before running the server, you must configure your network inventory and device credentials:

  1. Navigate to the conf/ directory in the project.
  2. Edit hosts.yaml: Define your network devices, including their management IP, platform, credentials, and groups.
  3. Edit groups.yaml: Define device groups with shared properties.
  4. Edit defaults.yaml: Set default credentials and connection options.
    • ⚠️ Important Security Note: For production, strongly consider using Nornir's secrets management features to avoid storing plaintext credentials in YAML files.
  5. Review blacklist.yaml: Customize the list of blocked commands and patterns to fit your security policies.

▶️ Running the Server

Once configured, you can easily run the server using Docker Compose:

docker-compose up --build -d

This command starts the Nornir MCP server in a Docker container, accessible on port 8000 of your host machine. The container now uses run.py as its entrypoint, which supports both development and production modes.

To run the server locally (without Docker), use:

python run.py --dev

or simply:

python run.py

This will start the server on 0.0.0.0:8000 using the new entrypoint logic.

🔌 How to connect an MCP client

This project exposes FastMCP over HTTP using the streamable-http transport. The server provides two useful endpoints:

  • HTTP API endpoint: http://:/mcp
  • SSE endpoint (events): http://:/sse

Notes on transports and client setup:

  • The MCP server itself is an HTTP application (FastAPI/Starlette) served by Uvicorn. You should connect MCP clients directly to the /mcp (primary) endpoints using a client that supports the streamable-http transport.
  • You do NOT need to run this project in stdio mode for typical HTTP or SSE clients. Previously included instructions referencing running the server in "stdio mode" and proxying it with Supergateway were inaccurate for the normal usage of this repository.

Example MCP client JSON configuration (HTTP/streamable-http):

{
  "name": "Nornir MCP (HTTP)",
  "url": "http://localhost:8000/mcp",
  "transport": "http"
}

🧠 Prompts (custom prompt functions)

This server supports registering custom prompt functions that return a list of messages (MCP Prompt format). Prompts let you predefine conversational inputs that MCP clients or LLM-driven agents can call as named prompts. The FastMCP API exposes a @server.prompt() decorator to register functions.

Key features:

  • Register synchronous or async prompt functions using @server.prompt().
  • Optionally provide a name, title, and description to make prompts discoverable in MCP clients.
  • Prompts can return structured messages including resource references (useful for returning file contents or inventory snippets).

How to add a prompt (example):

@server.prompt(name="list-host-names", title="List Host Names", description="Return a short list of host names from inventory")
def prompt_list_hosts() -> list:
    hosts = nr_mgr.list_hosts()
    return [{"role": "user", "content": f"Available hosts: {', '.join(h['device_name'] for h in hosts)}"}]

Async example with a resource:

@server.prompt()
async def show_topology() -> list:
    topo = await server.read_resource("resource://topology")
    return [{"role": "user", "content": {"type": "resource", "resource": topo}}]

Usage notes:

  • After registering prompts, clients can discover them via the MCP ListPrompts request and call them by name.
  • Keep prompt functions lightweight and deterministic; avoid long-running operations inside prompts. If you need data gathered from devices, consider registering a tool and calling it from the prompt or returning a short resource reference that the client can fetch.

Security:

  • Prompts run inside the server process; do not perform unsafe file operations or execute untrusted code in prompt functions.

Quick start — Docker (recommended)

  1. Build and run with docker-compose (from repo root):
docker-compose up --build -d

This starts the server in the container and exposes it on port 8000 by default.

Quick start — Local

  1. Create and activate a virtual environment.
& .venv\Scripts\Activate.ps1
# or on Unix: python -m venv .venv; source .venv/bin/activate
  1. Install runtime dependencies (example):
pip install -U pip
pip install nornir==3.5.0 nornir-napalm mcp[cli]==1.15.0 sse-starlette
  1. Run the server locally (binds to 0.0.0.0:8000 by default):
python run.py

Or with uv (recommended when using the included runner):

uv run .\run.py

If you need to change host/port use the --host and --port flags when running run.py.


Resources provided by the server
- `resource://inventory/hosts` — returns JSON array of hosts with sanitized fields (name, hostname, platform, groups, data). Sensitive keys such as `username`, `password`, and `secret` are removed.
- `resource://inventory/hosts/{keyword}` — same output filtered by a keyword (case-insensitive) that matches name, hostname, platform, group names, or data values.
- `resource://inventory/groups` — returns groups mapping (sanitized).
- `resource://topology` — parsed `resources/topology.json`.
- `resource://cisco_ios_commands` — parsed `resources/cisco_ios_commands.json`.

How to add your own resources
1. Edit `resources.py` and add a function named `resource_<name>` (e.g., `resource_my_tools`).
2. If your function needs the Nornir manager, accept a single parameter named `nr_mgr`.
3. Add an entry to `RESOURCE_MAP` if you want a custom URI; otherwise a default URI `resource://user/<name>` is used.

Example `resources.py` snippet

```python
def resource_my_static():
  return {"hello": "world"}

def resource_my_hosts(nr_mgr):
  # returns a JSON-serializable list of hosts
  return nr_mgr.list_hosts()

Security notes

  • Inventory YAML files may contain credentials. For production, prefer secrets management (Vault, environment variables, or Nornir secrets plugins) over plaintext YAML.
  • The server strips common sensitive keys (username, password, secret) from resources served via resource://inventory/*.

Contributing

  • Open an issue or PR for changes. Keep changes small and include tests where appropriate.

License

  • MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

iflow_mcp_yhvh_chen_nornir_mcp-0.2.0.tar.gz (112.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

iflow_mcp_yhvh_chen_nornir_mcp-0.2.0-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

Details for the file iflow_mcp_yhvh_chen_nornir_mcp-0.2.0.tar.gz.

File metadata

  • Download URL: iflow_mcp_yhvh_chen_nornir_mcp-0.2.0.tar.gz
  • Upload date:
  • Size: 112.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for iflow_mcp_yhvh_chen_nornir_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a6df6057dae2430fab07250c1d75fede4eadbf9f412f3c805e8352ef8b241e7a
MD5 32ee839a4c9a929e26a05f37c65cedf2
BLAKE2b-256 830cfab994f150e28a191f658d45ae1ba4e3d1d74d2f087ce32f978ba0b734bd

See more details on using hashes here.

File details

Details for the file iflow_mcp_yhvh_chen_nornir_mcp-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: iflow_mcp_yhvh_chen_nornir_mcp-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 15.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for iflow_mcp_yhvh_chen_nornir_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c2c335b8692234977914c8694db3116c317490bd6edb3f7b42c786394f0338b8
MD5 18d484454b3a2edfa77dbde34c180e6a
BLAKE2b-256 e6be256b634695cf5262b07981593cc5b3bec22adf58165bddacc05ebe63154c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page