network-mcp

network-mcp

Enables read-only access to managed network devices over SSH or Telnet via MCP, allowing users to probe devices, retrieve redacted running or startup configs, gather facts and interfaces, back up multiple devices, and compare configs for drift or unsaved changes.

Category
访问服务器

README

network-mcp

Read-only CLI access to managed network devices, over SSH or Telnet, exposed as an MCP connector. Give it an IP address; it logs in with the house credentials, works out whether it is a Cisco or an ADTRAN, pulls the config, strips the secrets out, and hands it back.

Built on the house MCP pattern (fastmcp resource-server + Entra OAuth, behind NPM on Portainer) — see ~/.claude/context/mcp-server-playbook.md.

"pull the config from 10.20.30.1"   ->  net_running_config
"is 10.20.30.1 up, and what is it?" ->  net_probe_device
"unsaved changes on 10.20.30.1?"    ->  net_config_diff
"back up these six routers"         ->  net_backup_configs

Status

Working end to end against real hardware, 2026-08-17. Pulled the running config from an ADTRAN Total Access 924e (AOS R10.9.5.E) at 192.0.2.90 via the relay: platform auto-detected, 454 lines, 47 secrets redacted, 4.8 seconds, no credential leakage. The relayuser relay account exists.

Not deployed — no Entra app, no Portainer stack, no NPM host, no GitHub repo. See PORTAINER_DEPLOY.md.

How it reaches devices

Neither this workstation nor the docker host can route to customer management networks. A relay can — so the server does exactly what an engineer does:

ssh relayuser@203.0.113.24     ->  telnet 192.0.2.90  ->  show running-config

Nothing is configured on the relay. No sshd_config edit, no reload, no TCP forwarding, no per-device rules. Create a user, and a new device IP works immediately. Mechanically this is netmiko's terminal-server pattern: connect with the terminal_server driver, drive the hop by hand, then redispatch() the live session onto the Cisco or ADTRAN driver (network_mcp/relay_shell.py). Vendor re-detection is another redispatch on the same session — no second login.

A tunnelled mode (NETMCP_JUMP_MODE=forward, network_mcp/jump.py) is also implemented and is technically cleaner, but it needs port forwarding enabled on a public-facing box. Shell mode is the default for that reason.

net_relay_status checks the relay on its own — login, telnet client present, and whether the relay can actually open a path to a given device. That's what separates "the relay is down" from "that router is down", which are otherwise identical from a device error.

This raises the stakes on NETMCP_ALLOWED_CIDRS. The relay is a shell; it reaches whatever it can reach. The MCP server's allowlist is the only thing bounding which addresses get logged into.

What it can reach

Cisco IOS / IOS-XE, NX-OS, IOS-XR, ASA; ADTRAN AOS (NetVanta, Total Access); Arista EOS; Juniper Junos; HP/Aruba ProCurve. The platform is detected from show version, so callers never have to say what a box is.

Tools

Tool What it does
net_capabilities What this connector is for — the front door
net_probe_device Reachable? over what? which login? what platform? No config returned
net_running_config The running config, redacted. section= filter for big ones
net_startup_config The saved config
net_show Any single show-style command
net_facts Model, serial, software version, uptime
net_interfaces Interface / IP / status summary
net_backup_configs Many devices at once; status + hash per device, text on request
net_config_diff Running vs startup (unsaved changes), or device A vs device B

Five prompts ship as the discovery surface in claude.ai (+ → Connectors → Add from network-mcp): Pull Device Config, Site Config Backup, Unsaved Changes Check, Device Triage, Compare Two Devices.

The three things that make this safe

This server is different from the others we run: it holds a shared admin credential for customer network equipment, and it is driven by a language model that reads untrusted text (tickets, emails, device banners, interface descriptions). "Now check 203.0.113.9" or "run configure terminal" can appear in any of those. So none of this is left to prompting.

1. It is read-only by construction, not by policy — device.py calls five netmiko methods and none of them write. There is no send_config_set, no config mode, no save/copy/erase/reload anywhere in the package. tests/test_readonly_surface.py parses the AST and fails if one appears. A change tool, if ever wanted, belongs in a separate server with its own Entra app and access group — the way tactical-rmm-mcp and tactical-rmm-audit-mcp are split.

2. guard.py decides what it may log in to — it resolves the target first, requires every address a name resolves to be permitted, and connects to the address it checked (no DNS-rebinding gap).

The allowlist is currently OPEN (NETMCP_ALLOWED_CIDRS=0.0.0.0/0,::/0) — John's call, 2026-08-17, because managed devices sit on private and public addresses across many customers and a hand-maintained list would be wrong more often than right during build-out. Loopback, link-local (incl. the 169.254.169.254 metadata address), multicast and reserved space are still refused.

Know what that trades away: the credentials are shared across the estate and Telnet sends them in cleartext, so an address arriving from a ticket, an email, an interface description or a device banner is acted on exactly like one an engineer typed. net_capabilities says so out loud and the server logs a warning at startup, so the posture is visible rather than buried in a value. Narrowing later: our own public blocks + RFC1918, or — better — allow only addresses documented as configurations in IT Glue, which is tighter than any CIDR list and makes documentation load-bearing.

3. Configs come back redacted — redact.py replaces enable secrets, local user hashes, SNMP communities, RADIUS/TACACS keys, IPSec PSKs, WiFi passphrases, BGP/OSPF authentication and inline private keys, keeping the left-hand side so config review and drift comparison still work. There is no tool parameter that turns this off; only the NETMCP_REDACT_SECRETS environment variable, changed deliberately by a person. An MCP result lands in a transcript permanently.

Plus: command allowlist (show, display, dir, ping, traceroute only — no chaining, no | tee/redirect/append), credentials selectable only by profile name and never returned, every Telnet session flagged in its own result as cleartext, and a per-call audit line carrying the Entra identity of the human who asked.

Local development

python -m venv .venv && .venv/Scripts/pip install -r requirements.txt pytest
cp .env.example .env

Fill in NETMCP_ALLOWED_CIDRS and one credential profile, then run the tests:

<your-workspace>/network-mcp/.venv/Scripts/python.exe -m pytest tests -q

Run it over stdio in Claude Code (MCP_TRANSPORT=stdio, the default):

<your-workspace>/network-mcp/.venv/Scripts/python.exe <your-workspace>/network-mcp/run_server.py

MCP_AUTH_ENABLED=false is for local stdio and MCP Inspector only. Never expose it — this server holds device credentials.

Layout

network_mcp/
  server.py        MCP tools and prompts
  device.py        netmiko sessions — the only module that touches a device
  guard.py         where it may log in, and what it may type
  redact.py        what is removed before output leaves the process
  credentials.py   profiles, held server-side, never returned
  vendors.py       per-platform commands, drivers, detection signatures
  archive.py       optional on-disk config archive
  config.py        environment -> settings
tests/             the security spec: guard, redaction, read-only surface
scripts/           setup_entra_app.ps1
docs/              ENTRA_SETUP_CHECKLIST.md

Known limits

  • show archive and show boot are refused as a side effect of the filter rules. show bootvar works. Nobody has needed the other two yet.
  • Vendor detection needs a reachable show version. A device with a login banner that swallows the first command, or an unusual platform, falls back to generic IOS-style commands and says so in warnings.
  • Telnet is slow and fragile on old hardware. Raise NETMCP_GLOBAL_DELAY_FACTOR to 2 or 4 if output comes back truncated.
  • The on-disk archive is unredacted by default (a redacted config cannot be restored, so it is not a backup). The volume is a credential store — back it up and treat it as one.
  • Structured parsing is deliberately shallow. net_facts regexes a few fields and always returns the raw output alongside; anything richer is better done by the model reading the text than by a regex here that rots silently.

推荐服务器

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 模型以安全和受控的方式获取实时的网络信息。

官方
精选