内容
<!-- mcp-name: io.github.shigechika/junos-mcp -->
# Junos-MCP
| [English](README.md) | [日本語](README.ja.md) |
适用于 [junos-ops](https://github.com/shigechika/junos-ops) 的 MCP(Model Context Protocol)服务器。
通过 STDIO 传输协议,将 Juniper Networks 设备操作暴露给与 MCP 兼容的 AI 助手(例如 Claude Desktop、Claude Code)。
而 [junos-ops](https://github.com/shigechika/junos-ops) 是用于人类的 CLI 工具,
**junos-mcp** 是面向 AI 的相同强大引擎的接口。
## 功能
### 设备信息
| 工具 | 描述 | 连接 |
|------|-------------|:----------:|
| `get_device_facts` | 获取基本设备信息(型号、主机名、序列号、版本) | 是 |
| `get_version` | 获取 JUNOS 版本和升级状态 | 是 |
| `get_router_list` | 从 config.ini 列出路由器(可选按标签过滤) | 否 |
| `health_check` | 报告服务器版本 + 配置状态(路由器数量、不同标签)。 轻量级;不连接到任何设备 | 否 |
### CLI 命令执行
| 工具 | 描述 | 连接 |
|------|-------------|:----------:|
| `run_show_command` | 运行单个 CLI show 命令(`output_format`: text/json/xml) | 是 |
| `run_show_commands` | 在单个会话中运行多个 CLI 命令(`output_format`: text/json/xml) | 是 |
| `run_show_command_batch` | 并行在多个设备上运行命令(支持标签过滤和 `grep_pattern`) | 是 |
### 配置管理
| 工具 | 描述 | 连接 |
|------|-------------|:----------:|
| `get_config` | 获取设备配置(text/set/xml 格式) | 是 |
| `get_config_diff` | 显示与回滚版本的配置差异 | 是 |
| `push_config` | 推送配置并确认 + 健康检查 | 是 |
### 升级操作
| 工具 | 描述 | 连接 |
|------|-------------|:----------:|
| `check_upgrade_readiness` | 检查设备是否准备好升级 | 是 |
| `compare_version` | 比较两个 JUNOS 版本字符串 | 否 |
| `get_package_info` | 获取特定型号的软件包文件和哈希值 | 否 |
| `list_remote_files` | 列出远程设备路径上的文件 | 是 |
| `copy_package` | 通过 SCP 复制软件包并校验和 | 是 |
| `install_package` | 安装软件包并进行预检(EX2300/EX3400 的 `unlink` 标志) | 是 |
| `rollback_package` | 回滚到以前的软件包版本 | 是 |
| `schedule_reboot` | 在指定时间安排设备重启 | 是 |
### 诊断
| 工具 | 描述 | 连接 |
|------|-------------|:----------:|
| `collect_rsi` | 使用特定型号的超时收集 RSI/SCF | 是 |
| `collect_rsi_batch` | 并行从多个设备收集 RSI/SCF(支持标签过滤) | 是 |
### 预检
等同于 `junos-ops check` 子命令模式。所有三个都重用 junos-ops 的显示层进行表格渲染。
| 工具 | 描述 | 连接 |
|------|-------------|:----------:|
| `check_reachability` | 探测 NETCONF 可达性 + 每个主机的可用磁盘空间(快速:无事实,5 秒 TCP 探测) | 是 |
| `check_local_inventory` | 验证本地固件校验和与 config.ini 清单 | 否 |
| `check_remote_packages` | 验证暂存固件校验和 + 设备上的可用磁盘空间(SCP 后验证) | 是 |
### 日常操作
| 工具 | 描述 | 连接 |
|------|-------------|:----------:|
| `daily_brief` | 跨多个设备进行早晨健康检查 — 警报、接口上下线、系统日志警报模式在回溯窗口内(`since_hours`,默认为 18 小时),双-RE 故障(`[RE_FAULT]`;在 SRX 机箱集群中跳过,这些集群的事实报告了 RE 状态不佳 — 失败的集群节点通过机箱警报显示)以及可选的 `inet.0` 路由计数基线(`route_baseline`,例如 `tags=["main"], route_baseline=152`)。返回关键/警告/正常 Markdown 摘要。 | 是 |
### 安全设计
所有破坏性操作(`push_config`, `copy_package`, `install_package`, `rollback_package`, `schedule_reboot`)默认 **dry-run 模式** (`dry_run=True`)。
AI 助手必须显式设置 `dry_run=False` 才能进行更改。
`push_config` 提供了其他 Junos MCP 服务器中没有的安全功能:
- **确认提交**,具有可配置的超时(如果未确认则自动回滚)
- **提交后健康检查** (ping、NETCONF 上线探测或任何 CLI 命令)
- **自动回滚** 如果健康检查失败(未确认提交、计时器过期)
- **`no_commit=True`** — 发出 `commit confirmed` 但故意跳过最终提交。
JUNOS 在 `confirm_timeout` 分钟后自动回滚。适用于重启缺乏 `request ...restart` 命令的服务(例如 EX3400 上的 syslog 守护进程)。
## 要求
- Python 3.12+
- [junos-ops](https://github.com/shigechika/junos-ops) 并且有有效的 `config.ini`
- [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) >= 1.0
## 安装
```bash
pip install junos-mcp
```
或用于开发:
```bash
git clone https://github.com/shigechika/junos-mcp.git
cd junos-mcp
python3 -m venv .venv
. .venv/bin/activate
pip install -e ".[test]"
```
## CLI 选项
```bash
python -m junos_mcp --help
```
| 选项 | 描述 |
|--------|-------------|
| `-V`, `--version` | 打印版本并退出 |
| `--check` | 加载 config.ini,列出路由器并退出(错误时退出代码 1) |
| `--check-host HOSTNAME` | 使用 `--check`,还打开一个 NETCONF 会话以验证可达性/身份验证 |
| `--transport {stdio,streamable-http}` | 传输协议(默认:`stdio`) |
`--check` 可用于在将服务器注册到 AI 助手之前验证 `JUNOS_OPS_CONFIG` 和 `config.ini` 是否可达。
将其与 `--check-host rt1` 结合使用,还可以确认凭据实际上对真实设备进行身份验证。
## 基于标签的主机过滤
`run_show_command_batch`, `collect_rsi_batch`, 和 `get_router_list` 接受一个可选的 `tags` 参数。
语法与 `junos-ops --tags` CLI 标志匹配(自 junos-mcp 0.9.0 / junos-ops 0.16.6 起):
- 每个列表元素是一个 **标签组**。组内的逗号分隔标签 **与** 运算。
- 多个列表元素 **或** 跨组运算。
- 与批处理工具上的 `hostnames` 结合使用时,结果是 **交集**(进一步由名称细化的标签过滤)。空交集返回错误。
```python
# 1 组,1 标签 — 标记为 "main" 的主机
run_show_command_batch(command="show route summary", tags=["main"])
# 1 组,2 标签 — 组内 AND:tokyo AND edge
collect_rsi_batch(tags=["tokyo,edge"])
# 2 组 — OR 跨组:main OR backup
get_router_list(tags=["main", "backup"])
# 混合:(tokyo AND core) OR backup
run_show_command_batch(command="show version", tags=["tokyo,core", "backup"])
# 交集:标记为 backup 的主机中,仅限 rt1/rt2
run_show_command_batch(
command="show version",
hostnames=["rt1.example.jp", "rt2.example.jp"],
tags=["backup"],
)
```
参见 [junos-ops 标签文档](https://github.com/shigechika/junos-ops#tag-based-host-filtering),了解如何在 `config.ini` 中标记部分以及匹配的 CLI 语法。
## 结构化输出格式
`run_show_command` 和 `run_show_commands` 接受一个可选的 `output_format` 参数:
| 值 | 描述 |
|-------|-------------|
| `"text"` | 默认。纯文本 CLI 输出(与键入命令相同) |
| `"json"` | NETCONF JSON 输出 — 设备返回结构化字典 |
| `"xml"` | NETCONF XML 输出 — 设备返回格式良好的 XML |
**注意:** CLI 管道阶段(`match`, `last`, `count` 等)被静默删除,
无论 `output_format` 如何。PyEZ 的 `Device.cli()` 通过 NETCONF
RPC 发送命令,而 JunOS 不会进行管道处理。在客户端过滤之前,
请不要使用管道运行命令。对于单个命令,`run_show_command_batch` 的 `grep_pattern`
参数(见下文)提供服务器端样式过滤 — 甚至针对单个
主机,通过传递一个元素列表 `hostnames` — 但它始终内部获取纯文本
输出(不能与 `output_format="json"`/`"xml"` 结合使用),并且
它一次只接受一个命令,因此它不是 `run_show_commands`
多命令情况的直接替代品。
```python
# 获取结构化 BGP 摘要数据
run_show_command("router-a", "show bgp summary", output_format="json")
```
## 服务器端输出过滤
`run_show_command_batch` 接受一个可选的 `grep_pattern` 参数(Python `re` 模式)。
设置后,仅保留每个主机输出中与模式匹配的行。标头行(以 `#` 开头)始终保留。
没有匹配行的主机显示 `(no match)`。
这减少了大型批处理结果 — 例如,93 台路由器 × `show route summary` — 从几百 KB 到几百字节,通过提取相关行:
```python
# 仅从 93 台路由器中提取 inet.0 目标计数
run_show_command_batch(
command="show route summary",
tags=["main"],
grep_pattern=r"inet\.0:\s+\d+ destinations",
)
```
## 连接池
junos-mcp 维护一个每主机的 NETCONF 连接池。
重用空闲的 `Device` 可以避免每次工具调用时的 TCP/NETCONF 握手;
连接池通过每个主机的锁序列化同一主机上的并发操作。
| 环境变量 | 默认值 | 描述 |
|----------------------|---------|-------------|
| `JUNOS_MCP_POOL` | `1`(启用) | 设置为 `0` 以禁用池并为每个调用打开新的连接 |
| `JUNOS_MCP_POOL_IDLE` | `60` | 空闲超时(秒)。未使用超过此时间的连接将在下一次调用时关闭。设置为 `0` 以禁用驱逐 |
**安全注意:** 池化连接是长生存期的 SSH 会话。
在会话持续时间受策略限制的环境中,请将
`JUNOS_MCP_POOL_IDLE` 设置为小于不活动限制的值,或将
`JUNOS_MCP_POOL=0` 设置为完全禁用池。
## 配置
此服务器使用与 junos-ops 相同的 `config.ini`。有关详细信息,请参阅 [junos-ops README](https://github.com/shigechika/junos-ops)。
每个工具都接受一个可选的 `config_path` 参数。如果省略,则使用默认搜索顺序:
1. 环境变量 `JUNOS_OPS_CONFIG`
2. `./config.ini`
3. `~/.config/junos-ops/config.ini`
## 用法
### Claude Code
使用 `claude mcp add` 将 MCP 服务器注册到 Claude Code:
```bash
claude mcp add junos-mcp \
-e JUNOS_OPS_CONFIG=~/.config/junos-ops/config.ini \
-- python -m junos_mcp
```
`--scope`(`-s`)选项控制配置存储的位置:
| 范围 | 描述 | 配置位置 |
|-------|-------------|-----------------|
| `local`(默认) | 当前项目,仅当前用户 | `~/.claude.json` |
| `project` | 当前项目,与团队共享 | 项目根目录下的 `.mcp.json` |
| `user` | 所有项目,仅当前用户 | `~/.claude.json` |
### Claude Desktop
添加到 Claude Desktop 配置文件:
| 操作系统 | 配置文件 |
|----|-------------|
| 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"
}
}
}
}
```
编辑后重启 Claude Desktop。
### 使用 OAuth 进行远程访问(通过 mcp-stdio)
junos-mcp 支持 Streamable HTTP 传输协议,通过
[mcp-stdio](https://github.com/shigechika/mcp-stdio) 作为 OAuth 代理,实现与 Claude Desktop 或 Claude Code 的远程访问。
```mermaid
graph TB
A[junos-mcp<br/>远程服务器] <-- "OAuth 2.1 + HTTPS" --> B[mcp-stdio<br/>代理]
B <-- "STDIO" --> C[Claude Desktop<br/>Claude Code]
```
**步骤 1:在远程服务器上使用 Streamable HTTP 启动 junos-mcp**
```bash
JUNOS_OPS_CONFIG=~/.config/junos-ops/config.ini \
python -m junos_mcp --transport streamable-http
```
服务器默认监听 `http://localhost:8000/mcp`。
**步骤 2:在本地机器上将 mcp-stdio 注册为 MCP 服务器**
```bash
claude mcp add junos-mcp -- mcp-stdio https://your-server:8000/mcp
```
mcp-stdio 处理 OAuth 2.1 身份验证(RFC 8414 发现,RFC 7591
动态客户端注册,PKCE)和中继 STDIO ↔ Streamable HTTP。
有关包括 OAuth 提供商设置在内的详细配置,请参阅
[mcp-stdio README](https://github.com/shigechika/mcp-stdio)。
### MCP 检验器(开发)
```bash
mcp dev junos_mcp/server.py
```
## 测试
```bash
pytest tests/ -v
```
133 个测试覆盖所有 23 个工具、连接池、帮助函数和边缘情况。
### 现场烟雾测试
这些测试模拟 PyEZ,这就是它们速度快的原因 — 但也使它们
对停止返回真实数据的工具视而不见。
`scripts/smoke_test.py` 在配置的
设备上运行 **每个已注册的工具**,并在空、无效或错误答案时失败:
```bash
# 使用服务器使用的相同库存文件(JUNOS_OPS_CONFIG)
uv run python scripts/smoke_test.py
uv run python scripts/smoke_test.py --only facts --traceback
```
- **只读。** `push_config`, `copy_package`, `install_package`,
`rollback_package` 和 `schedule_reboot` 按名称跳过,并且测试
强制执行。`collect_rsi` / `collect_rsi_batch` 也跳过 — 它们
不会改变任何东西,但它们是 RE CPU 的几分钟和每个设备的答案文件,
没有断言会读取。运行命令的工具通过 `show system uptime` 进行测试:
它们接受一般操作命令,烟雾测试不应该是关键命令。
- **报告中没有有效载荷。** 仅限工具名称和状态;错误文本也被
遮蔽,因为这些工具引用了它们所请求的设备和有效载荷是配置。
- **规格中没有特定于房产的内容。** 每个主机需要的设备是在运行时
从配置的库存中发现的,并且 `get_package_info` 所需的硬件
型号来自该设备的自身事实。两个测试保持这样:一个拒绝这些
参数作为字面值,另一个禁止文件中的任何地址形字符串,因为
此存储库是公开的。
- 每个探测器都拒绝 `/Error: ...` / `Connection error: ...` 行,这些
工具在引发异常之前会返回这些行 — 否则无法访问的设备将被
读取为成功的调用。
- CI 强制执行廉价的一半:未注册带有探测器规范的工具会构建失败
(`tests/test_smoke_probes.py`),因此添加工具会强制提出问题
“我们如何知道它有效?”。
- `scripts/smoke_harness.py` 是引擎并且不包含 JUNOS 知识:它
保持不变跨共享它的服务器,因此修复引擎错误一次并同步文件
而不是修补此副本。
## 架构
### 构造 stdout 安全
自 junos-ops 0.14.1 以来,核心函数返回结构化 `dict` 值并且从不打印到 stdout;
MCP 工具通过 `junos_ops.display.format_*()` 渲染输出。
不需要 `contextlib.redirect_stdout`,因此 MCP STDIO JSON-RPC 通道保持清洁。
### 全局状态初始化
junos-ops 使用 `common.args` 和 `common.config` 作为全局变量。
MCP 服务器使用与 junos-ops 测试夹具(`conftest.py`)相同的模式初始化这些变量。
### 并行执行
批处理工具(`run_show_command_batch`, `collect_rsi_batch`)通过 junos-ops `common.run_parallel()` 使用 `ThreadPoolExecutor` 进行可配置的 `max_workers`。
## 许可证
Apache License 2.0