Junos MCP

io.github.shigechika由 GitHub 账号 shigechika 发布v0.23.0更新于 Nov 1, 2026

MCP server for Juniper Networks device operations via junos-ops

概览

AI 生成的概览

通过 junos-ops 让 AI 助手经 NETCONF 查看、配置、升级和诊断 Juniper Junos 网络设备的 MCP 服务器。

可以做什么
  • 读取设备基本信息、版本、路由器清单与健康状态
  • 执行单条、多条或跨设备批量 CLI show 命令,支持标签过滤与正则输出筛选
  • 读取配置、查看相对回滚版本的配置差异,并以 commit confirmed 加健康检查方式推送配置
  • 检查升级就绪状态、比较版本、复制并安装固件包、回滚软件包以及安排重启
  • 收集 RSI/SCF 诊断信息,并执行连通性、本地清单与远程包校验等预检
  • 生成跨设备的每日简报,汇总告警、接口状态、syslog 模式与路由数量
适用场景
适合已用 junos-ops 管理 Juniper 设备的网络工程师,希望让助手执行 show 命令、收集诊断、做每日健康巡检,或在多台路由器上准备配置与固件变更。
运行要求
以本地 Python 包运行(Python 3.12+、MCP Python SDK 1.0+),需安装 junos-ops。需要可用的 config.ini 设备清单及设备凭据,通过 JUNOS_OPS_CONFIG、./config.ini 或 ~/.config/junos-ops/config.ini 定位。需能访问设备并具备 NETCONF 权限;可选 JUNOS_MCP_POOL 与 JUNOS_MCP_POOL_IDLE 调整连接池。
安装前请注意
有五个工具会改变设备状态:push_config、copy_package、install_package、rollback_package 和 schedule_reboot。它们默认 dry_run=True,但设为 dry_run=False 会真正提交配置、安装固件或重启设备。设备凭据存放在 config.ini 中,账号的 Junos 登录类别决定可执行的操作。连接池是长时 SSH 会话,可能与会话时长策略冲突。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Junos MCP,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

junos-mcp

English | 日本語

MCP (Model Context Protocol) server for junos-ops.

Exposes Juniper Networks device operations to MCP-compatible AI assistants (Claude Desktop, Claude Code, OpenAI Codex, etc.) via STDIO transport. While junos-ops is the CLI tool for humans, junos-mcp is the AI-facing interface to the same powerful engine.

Features

Device Information

ToolDescriptionConnection
get_device_factsGet basic device information (model, hostname, serial, version)Yes
get_versionGet JUNOS version with upgrade statusYes
get_router_listList routers from config.ini (optionally filtered by tags)No
health_checkReport server version + config status (router count, distinct tags). Lightweight; does NOT connect to any deviceNo

CLI Command Execution

ToolDescriptionConnection
run_show_commandRun a single CLI show command (output_format: text/json/xml)Yes
run_show_commandsRun multiple CLI commands in a single session (output_format: text/json/xml)Yes
run_show_command_batchRun a command on multiple devices in parallel (supports tag filter and grep_pattern)Yes

Configuration Management

ToolDescriptionConnection
get_configGet device configuration (text/set/xml format)Yes
get_config_diffShow config diff against a rollback versionYes
push_configPush config with commit confirmed + health checkYes
push_config_start / push_config_resultThe same push as a background job, for hosted clients that cut a call at about 60 s and for slow-committing models (SRX branch): push_config_start returns the diff and a job_id without waiting for the commit, which runs on the same locked session; poll push_config_result until it is no longer runningYes

Upgrade Operations

ToolDescriptionConnection
check_upgrade_readinessCheck if device is ready for upgradeYes
compare_versionCompare two JUNOS version stringsNo
get_package_infoGet model-specific package file and hashNo
list_remote_filesList files on remote device pathYes
copy_packageCopy firmware package via SCP with checksumYes
install_packageInstall firmware with pre-flight checks (unlink flag for EX2300/EX3400)Yes
rollback_packageRollback to previous package versionYes
schedule_rebootSchedule device reboot at specified timeYes

Diagnostics

ToolDescriptionConnection
collect_rsiCollect RSI/SCF with model-specific timeoutsYes
collect_rsi_batchCollect RSI/SCF from multiple devices in parallel (supports tag filter)Yes

Pre-flight Checks

Equivalent to the junos-ops check subcommand modes. All three reuse the junos-ops display layer for table rendering.

ToolDescriptionConnection
check_reachabilityProbe NETCONF reachability + available disk space per host (fast: no facts, 5s TCP probe)Yes
check_local_inventoryVerify local firmware checksums against config.ini inventoryNo
check_remote_packagesVerify staged firmware checksum + available disk space on devices (post-SCP verification)Yes

Daily Operations

ToolDescriptionConnection
daily_briefMorning health check across multiple devices in parallel — alarms, interface up/down, syslog alert patterns within a look-back window (since_hours, default 18 h), dual-RE faults ([RE_FAULT]; skipped on SRX chassis clusters, whose facts misreport RE status — a failed cluster node surfaces via chassis alarms instead), and an optional inet.0 route-count baseline (route_baseline, e.g. tags=["main"], route_baseline=152). Returns a CRITICAL/WARNING/OK Markdown summary.Yes
daily_brief_start / daily_brief_resultRun the same sweep as a background job for fleets too large for one call (a hosted client's per-call limit is about 60 s): daily_brief_start returns a job_id at once, poll daily_brief_result until done. The synchronous daily_brief now stops after JUNOS_DEADLINE seconds (default 45; 0 disables) and lists unfinished hosts under NOT CHECKED.Yes

Safety by Design

All destructive operations (push_config, copy_package, install_package, rollback_package, schedule_reboot) default to dry-run mode (dry_run=True). The AI assistant must explicitly set dry_run=False to make changes.

push_config provides additional safety features not found in other Junos MCP servers:

  • commit confirmed with configurable timeout (auto-rollback if not confirmed)
  • Fallback health check after commit (ping, NETCONF uptime probe, or any CLI command)
  • Automatic rollback if health check fails (commit is not confirmed, timer expires)
  • no_commit=True — issues commit confirmed but intentionally skips the final commit. JUNOS auto-rolls back after confirm_timeout minutes. Useful for restarting services that lack a request ...restart command (e.g. syslog daemon on EX3400 post-upgrade).

RPC timeout and the per-call deadline. Every load/commit RPC uses timeout from the host's config.ini section, else the junos-ops model default: 180 s on SRX branch models (SRX300/320/340/345 and the like, whose commits can exceed 30 s), 120 s otherwise. It is never shortened. With JUNOS_DEADLINE set (default 45 s; 0 disables), push_config runs the push as a background job and waits for it until shortly before the deadline: if it finishes, the answer is the same as without a deadline; if not, the answer says Still running with a job_id to poll with push_config_result. push_config_start returns the diff and the job_id without waiting for the commit; the job loads and diffs the change and then commits on the same locked session, so what is committed is exactly the diff returned. A failure after commit confirmed has been sent is reported as a possibly pending commit that rolls back on the device after confirm_timeout minutes. Only one push per host runs at a time; a second one is refused, and other tools on that host wait at most until the deadline and then answer host busy.

Requirements

Installation

bash
pip install junos-mcp

Or for development:

bash
git clone https://github.com/shigechika/junos-mcp.gitcd junos-mcppython3 -m venv .venv. .venv/bin/activatepip install -e ".[test]"

CLI options

bash
python -m junos_mcp --help
OptionDescription
-V, --versionPrint version and exit
--checkLoad config.ini, list routers, and exit (exit code 1 on error)
--check-host HOSTNAMEWith --check, also open a NETCONF session to verify reachability/auth
--transport {stdio,streamable-http}Transport protocol (default: stdio)
--host HOST / --port PORTstreamable-http only: address and port to listen on (default: 127.0.0.1 / 8000). Use these rather than FASTMCP_HOST / FASTMCP_PORT, which MCP Python SDK 2.x no longer reads

--check is handy to verify JUNOS_OPS_CONFIG and config.ini are reachable before registering the server with an AI assistant. Combine with --check-host rt1 to also confirm that credentials actually authenticate against a real device.

Tag-based host filtering

run_show_command_batch, collect_rsi_batch, and get_router_list accept an optional tags argument. The grammar matches the junos-ops --tags CLI flag (since junos-mcp 0.9.0 / junos-ops 0.16.6):

  • Each list element is one tag group. Comma-separated tags inside a group AND together.
  • Multiple list elements OR together across groups.
  • When combined with hostnames on batch tools, the result is the intersection (tags filter further narrowed by names). An empty intersection returns an error.
python
# 1 group, 1 tag — hosts tagged "main"run_show_command_batch(command="show route summary", tags=["main"])
# 1 group, 2 tags — AND within the group: tokyo AND edgecollect_rsi_batch(tags=["tokyo,edge"])
# 2 groups — OR across groups: main OR backupget_router_list(tags=["main", "backup"])
# Mixed: (tokyo AND core) OR backuprun_show_command_batch(command="show version", tags=["tokyo,core", "backup"])
# Intersection: among backup-tagged hosts, only rt1/rt2run_show_command_batch(    command="show version",    hostnames=["rt1.example.jp", "rt2.example.jp"],    tags=["backup"],)

See the junos-ops tag documentation for how to tag sections in config.ini and for the matching CLI grammar.

Structured output format

run_show_command and run_show_commands accept an optional output_format parameter:

ValueDescription
"text"Default. Plain-text CLI output (same as typing the command)
"json"NETCONF JSON output — device returns a structured dict
"xml"NETCONF XML output — device returns pretty-printed XML

Note: CLI pipe stages (| match, | last, | count, etc.) are silently dropped regardless of output_format. PyEZ's Device.cli() sends the command over NETCONF RPC, which JunOS does not pipe-process. Run the command without pipes and filter client-side instead. For a single command, run_show_command_batch's grep_pattern argument (see below) offers server-side-style filtering — even against a single host, by passing a one-element hostnames list — but it always fetches plain-text output internally (it cannot be combined with output_format="json"/"xml"), and it only accepts one command at a time, so it isn't a drop-in workaround for run_show_commands' multi-command case.

python
# Get structured BGP summary datarun_show_command("router-a", "show bgp summary", output_format="json")

Server-side output filtering

run_show_command_batch accepts an optional grep_pattern argument (Python re pattern). When set, only lines matching the pattern are kept from each host's output. Header lines (starting with #) are always preserved. Hosts with no matching lines show (no match).

This reduces large batch results — for example, 93 routers × show route summary — from hundreds of KB to a few hundred bytes by extracting just the relevant lines:

python
# Extract only the inet.0 destination count from 93 routersrun_show_command_batch(    command="show route summary",    tags=["main"],    grep_pattern=r"inet\.0:\s+\d+ destinations",)

Connection pool

junos-mcp maintains a per-host NETCONF connection pool. Reusing an idle Device avoids the TCP/NETCONF handshake on every tool call; the pool serialises concurrent operations on the same host through a per-host lock.

Environment variableDefaultDescription
JUNOS_MCP_POOL1 (enabled)Set to 0 to disable the pool and open a fresh connection per call
JUNOS_MCP_POOL_IDLE60Idle timeout in seconds. Connections unused longer than this are closed on the next call. Set to 0 to disable eviction

Security note: pooled connections are long-lived SSH sessions. In environments where session duration is restricted by policy, set JUNOS_MCP_POOL_IDLE to a value shorter than the inactivity limit, or set JUNOS_MCP_POOL=0 to disable the pool entirely.

Configuration

This server uses the same config.ini as junos-ops. See junos-ops README for details.

Each tool accepts an optional config_path parameter. If omitted, the default search order is used:

  1. Environment variable JUNOS_OPS_CONFIG
  2. ./config.ini
  3. ~/.config/junos-ops/config.ini

config.ini is not optional in practice: every tool — including get_router_list and health_check, which never open a device connection — reads from it at startup, and there is no degrade-gracefully path if it can't be found. Put a working config.ini in one of the three locations above before registering the server with any MCP client.

Write operations

Five tools change device state. Everything else only reads. These are the same five that default to dry_run=True — see Safety by Design for the dry-run and commit-confirmed mechanics; this table is about what each one calls and the device-side privilege that gates it.

ToolAPI callPermission gate
push_config (and its background form push_config_start)jnpr.junos.utils.config.Config: lock → load(format="set") → diff → commit_check → commit(confirm=confirm_timeout) → health check → final commit → unlockThe config.ini account for the target host needs a JUNOS login class permitting configuration mode and commit — not a read-only/operator class. The exact class name is whatever was provisioned per device in config.ini.
copy_packagejunos_ops.upgrade.copy() — SCPs the firmware package to the device with checksum verification and pre-copy storage cleanupSame account needs file-copy / storage-write access (SCP to device flash).
install_packagejunos_ops.upgrade.install() — version check, pending-rollback check, copy + checksum, clear reboot schedule, rescue-config save, then PyEZ SW.install() (or request system software add via the unlink CLI path on low-flash EX2300/EX3400)Requires software-installation privilege — JUNOS maintenance-class or superuser login class.
rollback_packagejunos_ops.upgrade.rollback() — equivalent of request system software rollback, only after confirming a pending version existsSame elevated software-maintenance privilege as install_package.
schedule_rebootSchedules request system reboot at <time>Requires reboot/maintenance privilege on the device.

Provision the config.ini account for a host with a read-only/operator login class and these five tools fail against that host with a permission error; every other tool — show commands, config reads, diagnostics, daily_brief — keeps working. There is no separate plugin-level switch for this: the privilege boundary is entirely in the JUNOS login class assigned to the account in config.ini.

Usage

Claude Code (plugin)

This repository doubles as a single-plugin marketplace, so Claude Code can install the server for you:

/plugin marketplace add shigechika/junos-mcp/plugin install junos-mcp@junos-mcp

The plugin launches uvx junos-mcp and reads the same environment variables described in Configuration; export JUNOS_OPS_CONFIG (or drop config.ini at ./config.ini or ~/.config/junos-ops/config.ini) before starting Claude Code.

uvx must be on the PATH of the process that runs Claude Code — a login shell usually has it, but a GUI-launched app may not; install uv system-wide if the plugin fails to start.

Claude Code (manual)

Register the MCP server with claude mcp add:

bash
claude mcp add junos-mcp \  -e JUNOS_OPS_CONFIG=~/.config/junos-ops/config.ini \  -- python -m junos_mcp

The --scope (-s) option controls where the configuration is stored:

ScopeDescriptionConfig location
local (default)Current project, current user only~/.claude.json
projectCurrent project, shared with team.mcp.json in project root
userAll projects, current user only~/.claude.json

Claude Desktop

Add to Claude Desktop config file:

OSConfig file
macOS~/Library/Application Support/Claude/claude_desktop_config.json
Windows%APPDATA%\Claude\claude_desktop_config.json
Linux~/.config/Claude/claude_desktop_config.json
json
{  "mcpServers": {    "junos-mcp": {      "command": "python",      "args": ["-m", "junos_mcp"],      "env": {        "JUNOS_OPS_CONFIG": "/path/to/config.ini"      }    }  }}

Restart Claude Desktop after editing.

Codex

Register the MCP server with codex mcp add:

bash
codex mcp add junos-mcp \  --env JUNOS_OPS_CONFIG=~/.config/junos-ops/config.ini \  -- python -m junos_mcp

Any other MCP client that launches stdio servers works the same way: give it python -m junos_mcp as the command and set JUNOS_OPS_CONFIG in its environment.

Remote Access with OAuth (via mcp-stdio)

junos-mcp supports Streamable HTTP transport, enabling remote access from any MCP client (Claude Desktop, Claude Code, Codex, …) through mcp-stdio as an OAuth proxy.

mermaid
graph TB    A[junos-mcp<br/>remote server] <-- "OAuth 2.1 + HTTPS" --> B[mcp-stdio<br/>proxy]    B <-- "STDIO" --> C[MCP client<br/>Claude / Codex / …]

Step 1: Start junos-mcp with Streamable HTTP on the remote server

bash
JUNOS_OPS_CONFIG=~/.config/junos-ops/config.ini \  python -m junos_mcp --transport streamable-http

The server listens on http://localhost:8000/mcp by default.

Step 2: Register mcp-stdio as the MCP server on your local machine

bash
# Claude Codeclaude mcp add junos-mcp -- mcp-stdio https://your-server:8000/mcp
# Codexcodex mcp add junos-mcp -- mcp-stdio https://your-server:8000/mcp

mcp-stdio handles OAuth 2.1 authentication (RFC 8414 discovery, RFC 7591 dynamic client registration, PKCE) and relays STDIO ↔ Streamable HTTP.

See mcp-stdio README for detailed configuration including OAuth provider setup.

When junos-mcp is launched by mcp-stdio serve (the gateway mode) and runs on MCP SDK 2.x, use mcp-stdio 0.43.7 or later. Older gateways forward a request envelope that a 2.x server rejects; on those, install with mcp<2.

MCP Inspector (development)

bash
mcp dev junos_mcp/server.py

Testing

bash
pytest tests/ -v

133 tests covering all 23 tools, the connection pool, helper functions, and edge cases.

Live smoke test

Those tests mock PyEZ, which is what makes them fast — and also what makes them blind to a tool that has stopped returning real data. scripts/smoke_test.py runs every registered tool against the configured devices and fails on empty, malformed or error answers:

bash
# uses the same inventory file as the server (JUNOS_OPS_CONFIG)uv run python scripts/smoke_test.pyuv run python scripts/smoke_test.py --only facts --traceback
  • Read-only. push_config, push_config_start, copy_package, install_package, rollback_package and schedule_reboot are skipped by name, and a test enforces that. collect_rsi / collect_rsi_batch are skipped too — they change nothing, but they are minutes of RE CPU and a file per device for an answer no assertion would read. The command-running tools are exercised with show system uptime: they accept operational commands in general, and a smoke test must not be the thing that types one that matters.
  • No payloads in the report. Tool names and statuses only; error text is redacted too, since these tools quote the device they were asked about and the payloads are configuration.
  • Nothing estate-specific in the specs. The device the per-host tools need is discovered at run time from the configured inventory, and the hardware model get_package_info needs comes from that device's own facts. Two tests keep it that way: one refuses those parameters as literals, the other bans anything address-shaped anywhere in the file, because this repository is public.
  • Every probe refuses the Error: ... / Connection error: ... lines these tools return in place of raising — otherwise an unreachable device would read as a successful call.
  • CI enforces the cheap half: a tool registered without a probe spec fails the build (tests/test_smoke_probes.py), so adding a tool forces the question "how would we know it works?".
  • scripts/smoke_harness.py is the engine and holds no JUNOS knowledge: it is kept identical across the servers that share it, so fix engine bugs once and sync the file rather than patching this copy.

Architecture

Stdout-safe by construction

Since junos-ops 0.14.1, core functions return structured dict values and never print to stdout; MCP tools render output via junos_ops.display.format_*(). No contextlib.redirect_stdout is needed, so the MCP STDIO JSON-RPC channel stays clean.

Global State Initialization

junos-ops uses common.args and common.config as global variables. The MCP server initializes these using the same pattern as the test fixtures in junos-ops (conftest.py).

Parallel Execution

Batch tools (run_show_command_batch, collect_rsi_batch) use ThreadPoolExecutor via junos-ops common.run_parallel() with configurable max_workers.

License

Apache License 2.0

来源:README.md,提交 9f2b393

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

4
  1. v0.23.0最新Oct 18, 2026
  2. v0.22.1Oct 8, 2026
  3. v0.22.0Sep 29, 2026
  4. v0.18.0Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分