netmiko-mcp-server

netmiko-mcp-server

An MCP server that enables operating network devices through chat, using netmiko for SSH/Telnet connections with command allowlists, enable password support, and configuration-change tools (disabled by default).

Category
访问服务器

README

English | 日本語

netmiko-mcp-server

An MCP server for operating network devices through chat. It uses netmiko for SSH / Telnet connectivity and supports enable passwords.

Warning: This tool sends commands directly to network devices. Configuration-change tools are disabled by default, and even show-style commands are all denied unless a commands file is supplied. Always verify behavior in a lab environment before connecting to production devices.

Features

  • SSH / Telnet support
  • enable password (secret) support
  • SSH public-key authentication (use_keys, key_file) support
  • MCP stdio / SSE modes
  • Commands are denied by default; only commands explicitly allowed by the allow/deny lists in commands.toml can run
  • The configuration-change tool (set_config_commands_and_commit_or_save) is disabled by default; enable it explicitly with --enable-config
  • SSE mode requires Bearer token authentication (can be explicitly disabled with --no-http-auth, not recommended)
  • Every command attempt and connection result is recorded to a JSON audit log (fail-closed: the operation itself fails if the log write fails)
  • Large output is automatically saved to a file and can be read back with paging (keeps the LLM's context from being overwhelmed)
  • Parallel command execution across multiple devices grouped via [groups]
  • Structured (JSON) output via ntc-templates with use_textfsm=True
  • Inventory password/secret values can be stored encrypted (Fernet symmetric encryption)
  • import_inventory.py lets you build or append to the inventory interactively (no LLM tokens needed, with per-field validation)

Usage

1. Define devices (TOML)

Edit network_devices.toml to define [default] and individual devices.

[default]
username = "netops"
password = "password"
secret = "enablepassword"

[router_telnet]
hostname = "192.0.2.10"
device_type = "cisco_ios_telnet"

[switch_ssh]
hostname = "192.0.2.11"
device_type = "cisco_ios"
use_keys = true
key_file = "/home/user/.ssh/id_rsa"

[c1200coreSW]
hostname = "192.0.2.12"
device_type = "cisco_ios"
pre_commands = ["terminal datadump"]
ansi_escape_codes = true

Build the inventory interactively (import_inventory.py)

Instead of editing the TOML by hand, you can build or append to the inventory with an interactive script.

uv run python import_inventory.py                  # default: network_devices.toml
uv run python import_inventory.py -f my_devices.toml
  • Prompts for each field (device name, hostname/IP with IPv4/IPv6/FQDN support, device_type from netmiko's platform list, etc.) with validation and re-prompting on invalid input. A wrong device_type shows partial-match suggestions. Enter q at the device-name prompt to finish and move to the save/confirm step.
  • If the target file already exists, choose append / overwrite / abort. Append mode preserves existing comments, key order, and already-encrypted (enc:) values untouched. Overwrite mode creates a <filename>.bak backup first.
  • Entering group names (comma-separated) per device automatically updates the [groups] table.
  • If NETMIKO_MCP_SERVER_INVENTORY_KEY is set, password/secret are encrypted automatically before saving (see Encrypting credentials). If it isn't set, you can choose to save in plaintext or abort.
  • The file is written atomically with owner-only permissions (0600), and the saved file is round-trip verified through the inventory loader after writing.
  • Out of scope: editing/deleting existing devices, creating the [default] section, and prompting for pre_commands / ansi_escape_codes / timeout fields (edit these by hand). Note that in append mode, if an existing [groups] table is at the end of the file, new device tables are appended after it — this is still valid TOML and loads correctly.

2. Command allowlist (TOML)

Create commands.toml to explicitly list the commands the LLM is allowed to run. If this file is not provided, every command is denied.

allowed_commands = [
  "show version",
  "show ip interface brief",
  "show ip interface*",   # trailing glob: matches "show ip interface" plus anything after it
  "show ip route *",      # space + glob: "show ip route" alone is not allowed; an argument is required
]

denied_commands = [
  "show running-config",  # deny always wins, even if it also matches allowed_commands
]

Allowlist for configuration-change commands (when using --enable-config)

Adding config_allowed_commands/config_denied_commands to the same commands.toml applies allow/deny checks to each line passed to set_config_commands_and_commit_or_save (denied entirely if unset). If even one command in the batch is denied, nothing is sent to the device.

config_allowed_commands = [
  "interface *",
  "description *",
  "ip address *",
  "no shutdown",
]

config_denied_commands = [
  "no ip address*",
]

In addition, shutdown (bringing an interface down) and clear* are always denied regardless of the above configuration (a hardcoded baseline protection — see BASELINE_CONFIG_DENIED_COMMANDS in security.py). Listing them in config_allowed_commands cannot override this. no shutdown (bringing an interface back up) is not on the dangerous side of the operation, so it is not included in the baseline deny list.

3. Device groups (optional)

Add a [groups] table to network_devices.toml to run commands in parallel across a set of devices with send_command_to_group.

[groups]
core_switches = ["switch_ssh", "c1200coreSW"]

Use all instead of a group name to target every device in the inventory.

4. Encrypting credentials (optional)

If plaintext passwords in the TOML file are a concern, password/secret can be encrypted.

# 1. Generate a key and set it as an environment variable (the server needs the same key at startup)
export NETMIKO_MCP_SERVER_INVENTORY_KEY=$(uv run --with cryptography main.py --generate-key)

# 2. Encrypt the password and paste the result into the TOML file
uv run --with cryptography main.py --encrypt-value "mypassword"
# => enc:gAAAAA...
[router1]
hostname = "192.0.2.10"
device_type = "cisco_ios"
password = "enc:gAAAAA..."

If NETMIKO_MCP_SERVER_INVENTORY_KEY is not set while an encrypted value is being loaded, the server fails at startup. Keep the key out of the TOML file and manage it only through the environment variable.

5. Starting the server

stdio (local)

uv run --with "mcp[cli]" --with netmiko --with uvicorn main.py /path/to/devices.toml \
  --commands-file /path/to/commands.toml

Add --enable-config if you also want to use the configuration-change tool (disabled by default).

SSE (for remote connections)

SSE mode requires Bearer token authentication. First set the token as an environment variable.

export NETMIKO_MCP_SERVER_BEARER_TOKEN="$(openssl rand -hex 32)"
uv run --with "mcp[cli]" --with netmiko --with uvicorn main.py /path/to/devices.toml \
  --commands-file /path/to/commands.toml \
  --sse --bind 10.70.72.1 --port 10000

Example SSE URL: http://<server-ip>:10000/sse (the client must send an Authorization: Bearer <token> header)

Starting --sse without NETMIKO_MCP_SERVER_BEARER_TOKEN set stops the server with a startup error. Only pass --no-http-auth explicitly if you want to run without authentication (not recommended).

Restricting access to a specific subnet (e.g. 10.70.72.0/24)

In SSE mode, --allowed-subnet lets you specify allowed subnets (comma-separated). The default is 0.0.0.0/0. Combining this with Bearer token authentication provides defense in depth.

uv run --with "mcp[cli]" --with netmiko --with uvicorn main.py /path/to/devices.toml \
  --commands-file /path/to/commands.toml \
  --sse --bind 10.70.72.1 --allowed-subnet 10.70.72.0/24,127.0.0.1/32 --port 10000

Audit log

By default, entries are recorded in JSON Lines format at ~/.netmiko_mcp_server_audit.log. Use --audit-log-file /path/to/audit.log to change the path.

Handling large output

By default, output exceeding 1000 lines is automatically saved under ~/.netmiko_mcp_server_outputs/<device>/ and can be read back with paging via the list_device_outputs/read_device_output tools. The threshold is configurable with --output-save-threshold, and the save location with --output-dir.

Parallelism for group execution

send_command_to_group defaults to 10 concurrent connections. Change this with --max-workers.

Running with Docker

Use the published image (recommended)

GitHub Actions automatically builds and publishes an image to the GitHub Container Registry (GHCR) on every push to main or on v*.*.* tag pushes (the publish job in .github/workflows/ci.yaml). The image is only published once lint, type-check, and tests all pass.

docker pull ghcr.io/nagayon-935/netmiko_mcp_server:latest

Available tags:

Tag Meaning
latest latest commit on the main branch
sha-<short-sha> build for a specific commit (for traceability)
v1.2.3 / 1.2 semver tags, generated only when a v*.*.* git tag is pushed

Supported architectures: linux/amd64, linux/arm64 (also works with Docker/Podman on a Raspberry Pi or Apple Silicon).

Note for first-time publishing: GHCR packages can default to Private even when the repository is public. If docker pull fails with a 403, go to the repository's GitHub page → Packages → the package's Package settings, and change Visibility to Public. Also check that Actions has packages: write permission under Settings → Actions → General → Workflow permissions ("Read and write permissions" must be enabled).

Building it yourself

docker build -t netmiko-mcp-server .

Then mount the device config file and the command allowlist and start the container (replace netmiko-mcp-server with ghcr.io/nagayon-935/netmiko_mcp_server:latest if you're using the published image).

docker run -d -p 10000:10000 \
  -v $(pwd)/network_devices.toml:/app/config.toml \
  -v $(pwd)/commands.toml:/app/commands.toml \
  -e NETMIKO_MCP_SERVER_BEARER_TOKEN="$(openssl rand -hex 32)" \
  --name netmiko-mcp netmiko-mcp-server \
  --sse --port 10000 --commands-file /app/commands.toml

MCP tools

Tool Description
get_network_device_list Returns the list of all devices in the inventory (no credentials included)
send_command_and_get_output Sends a command to a single device, with use_textfsm and save_output options
send_command_to_group Runs a command in parallel across a device name, group name, or all, with use_textfsm and save_output options
list_device_outputs Lists saved output files
read_device_output Reads a saved output file with paging
set_config_commands_and_commit_or_save Sends configuration-change commands (requires --enable-config)

Using it from the Gemini CLI (example)

Register the server in the Gemini CLI's MCP configuration.

{
  "mcpServers": {
    "netmiko server": {
      "url": "http://<server-ip>:10000/sse"
    }
  }
}

If Bearer token authentication is enabled (the default), the client also needs to be configured to send an Authorization: Bearer <token> header. How to set headers varies by AI client, so check your client's MCP server configuration documentation. This is not needed if authentication was disabled with --no-http-auth (not recommended).

Notes

  • Set device_type to a name supported by netmiko.
  • If secret is set, enable() is attempted automatically.
  • Without --commands-file, send_command_and_get_output is always denied (deny-by-default).
  • set_config_commands_and_commit_or_save is always denied unless --enable-config is passed.
  • send_command_and_get_output/send_command_to_group (show-style commands) are governed by allowed_commands/denied_commands; set_config_commands_and_commit_or_save (configuration changes) is governed by config_allowed_commands/config_denied_commands. Everything is denied if --commands-file (or config_allowed_commands) is not set.
  • shutdown and clear* are always denied regardless of configuration (baseline protection; see "Command allowlist" above for details). For other configuration commands, only trusted operators should use them, and only within the scope of config_allowed_commands.

Migrating from older versions

The --secured and --disable-config flags from earlier versions have been removed.

  • --secured (prefix-based blocklist) → replaced by the allow/deny list in --commands-file
  • --disable-config (enabled by default, opt-out) → replaced by --enable-config (disabled by default, opt-in)

License

MIT License. See LICENSE for details.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选