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).
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
enablepassword (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.tomlcan 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/secretvalues can be stored encrypted (Fernet symmetric encryption) import_inventory.pylets 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_typeshows partial-match suggestions. Enterqat 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>.bakbackup first. - Entering group names (comma-separated) per device automatically updates the
[groups]table. - If
NETMIKO_MCP_SERVER_INVENTORY_KEYis set,password/secretare 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 forpre_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 pullfails 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 haspackages: writepermission 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_typeto a name supported bynetmiko. - If
secretis set,enable()is attempted automatically. - Without
--commands-file,send_command_and_get_outputis always denied (deny-by-default). set_config_commands_and_commit_or_saveis always denied unless--enable-configis passed.send_command_and_get_output/send_command_to_group(show-style commands) are governed byallowed_commands/denied_commands;set_config_commands_and_commit_or_save(configuration changes) is governed byconfig_allowed_commands/config_denied_commands. Everything is denied if--commands-file(orconfig_allowed_commands) is not set.shutdownandclear*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 ofconfig_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
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。