Skip to main content
Glama
Shadow-linux

jumpserver-ssh-mcp

by Shadow-linux
README.md
# jumpserver-ssh-mcp

`jumpserver-ssh-mcp` 是一个 MCP Server,用来让 Agent 通过直连 SSH 或 JumpServer 入口安全地执行远程命令。

产品方向是 **MCP only**:

- `gateway` 表示一个 JumpServer / 环境入口。
- 入口匹配插件负责适配不同 JumpServer 交互界面。
- 到达目标机器 shell 后,继续复用现有 SSH 执行、安全检查、审计和输出收集机制。
- 不新增 CLI 产品工作流;仓库里保留的 CLI 只用于兼容和本地 smoke check。

## 当前状态

这个项目原来是 `ssh-assist-mcp` 原型,已经具备:

- `ssh.run_command`
- `ssh.run_script`
- `ssh.rsync_upload`
- `ssh.rsync_download`
- `ssh.file_push`(文件上传工具;gateway 有有效映射时优先使用 Koko SFTP)
- `ssh.file_pull`(文件下载工具;gateway 有有效映射时优先使用 Koko SFTP)
- `ssh.job_start`
- `ssh.job_status`
- `ssh.job_log`
- `ssh.job_cancel`
- `ssh.job_list`
- 基础安全策略和审计日志
- 基础 JumpServer 交互式 gateway 执行

现在要产品化的是 JumpServer 入口层:

- matcher plugin 契约和 registry
- 内置通用 JumpServer matcher
- 用户自写声明式 matcher
- 多 JumpServer / 多环境 gateway 自由切换
- MCP resources 暴露 matcher 编写文档、示例、schema、排障说明
- README 和 `docs/install/*` 提供 Codex、Kimi CLI、opencode 安装入口

## 安装入口

安装文档是普通仓库文件,所以 **MCP 还没装好之前也能看**:

- [任意 Agent 安装 MCP](docs/install/agent.md)
- [Codex 安装配置](docs/install/codex.md)
- [Kimi CLI 安装配置](docs/install/kimi-cli.md)
- [opencode 安装配置](docs/install/opencode.md)

推荐顺序:

1. 先按 [任意 Agent 安装 MCP](docs/install/agent.md) 把 `jumpserver-ssh-mcp` 接入 Agent 客户端。
2. 再让 Agent 安装或读取 `skills/jumpserver-agent-skill`。
3. Agent 按 skill 创建 profile、配置 gateway、probe、smoke test,必要时编写 matcher。

MCP 安装完成后,Agent 可以再通过 MCP resources 读取运行时文档,例如 matcher 编写指南、示例、schema 和 troubleshooting。

## Agent 自主安装

可以直接把下面这段任务交给 Codex 或其他具备本地文件和终端权限的 Agent:

> 从 `https://github.com/Shadow-linux/jumpserver_ssh_mcp.git` 获取或更新项目,先阅读 `skills/jumpserver-agent-skill/SKILL.md` 和 `references/install-and-upgrade.md`。由你负责创建 venv、安装 MCP、把完整 Skill 同步到当前 Agent 的技能目录、备份并更新 MCP 客户端配置、创建用户目录下的 profile,然后重启或提示我重启客户端。不要覆盖已有运行期配置,不要读取或复制私钥内容。安装后确认 matcher、命令、文件传输工具以及五个 `ssh.job_*` 工具完整可见,再执行无害 probe 和 smoke test。

Codex 的 Skill 安装位置是:

```text
~/.codex/skills/jumpserver-agent-skill
```

仓库内入口是 `skills/jumpserver-agent-skill/SKILL.md`。用户通常只需提供 gateway 名称、JumpServer SSH 登录命令和一个验证目标;安装路径、MCP 配置、profile、Skill 同步与验证由 Agent 负责。五个 `ssh.job_*` 必须同时出现,部分缺失表示 MCP 进程或客户端工具 schema 尚未更新。

## 快速初始化

从源码安装时,先把项目拉到一个稳定目录:

```bash
git clone <repo-url> jumpserver_ssh_mcp
cd jumpserver_ssh_mcp
```

创建虚拟环境并安装 MCP server:

```bash
uv venv
uv pip install -e '.[mcp]'
```

确认启动命令存在:

```bash
.venv/bin/jumpserver-ssh-mcp
```

准备本机 profile。运行期文件统一放在用户目录,避免更新源码仓库时覆盖本机配置:

```bash
mkdir -p ~/jumpserver-ssh-mcp/{config,logs,matchers}
cp config/example.yaml ~/jumpserver-ssh-mcp/config/local.yaml
```

配置文件说明:

- `config/example.yaml`:推荐起步样例,只包含人类需要维护的最小字段。
- `config/full-example.yaml`:完整参考样例,给 Agent 或高级用户查看所有可选字段。
- `~/jumpserver-ssh-mcp/config/local.yaml`:本机真实配置,不放在源码仓库里。

然后把 `~/jumpserver-ssh-mcp/config/local.yaml` 改成真实 JumpServer:

```yaml
gateways:
  pro-jumpserver:
    command: ssh -i ~/.ssh/pro.pem ops@jump.example.com -p2222
    matcher: builtin-generic
```

在任意支持 MCP 的 Agent 客户端里配置:

```text
server name: jumpserver-ssh-mcp
command: /path/to/jumpserver_ssh_mcp/.venv/bin/jumpserver-ssh-mcp
env.SSH_ASSIST_PROFILE: /Users/you/jumpserver-ssh-mcp/config/local.yaml
env.SSH_ASSIST_AUDIT_LOG: /Users/you/jumpserver-ssh-mcp/logs/jumpserver-ssh-mcp-audit.jsonl
```

配置后重启或 reload Agent 客户端,然后验证:

```text
ssh.matcher_list
```

能看到 profile 里的 gateway,就说明 MCP 初始化完成。

## 从 v0.1.0 升级

`0.2.1` 起推荐把运行期文件放到 `~/jumpserver-ssh-mcp/`。如果旧版本已经在 MCP 客户端里显式配置了 `SSH_ASSIST_PROFILE`,升级后会继续优先使用这个路径。

如果旧配置还在源码仓库里,可以迁移一份:

```bash
mkdir -p ~/jumpserver-ssh-mcp/{config,logs,matchers}
cp config/local.yaml ~/jumpserver-ssh-mcp/config/local.yaml
```

为了兼容旧安装,无显式 `SSH_ASSIST_PROFILE` 时会按顺序查找:

1. `~/jumpserver-ssh-mcp/config/local.yaml`
2. `config/local.yaml`
3. `config/example.yaml`

## Gateway 是环境入口

调用 MCP 工具时用 `gateway` 选择要进入哪个 JumpServer / 环境:

```text
gateway=prod-jumpserver  -> 生产 JumpServer / 环境
gateway=test-jumpserver  -> 测试 JumpServer / 环境
gateway=ops-jumpserver   -> 运维 JumpServer / 环境
```

建议运维类调用都显式传 `gateway`,这样审计日志能清楚记录 Agent 进入了哪个环境入口。

## Agent 会话归属与残留清理

通过 JumpServer gateway 执行命令时,Agent 可以给 SSH 工具传 `owner_id`,例如 `codex-thread-<id>`。

`owner_id` 用来把本次 gateway SSH 子进程登记到本地运行目录:

```text
~/jumpserver-ssh-mcp/run/sessions/<owner_id>/
```

下一次同一个 `owner_id` 启动 gateway SSH 前,MCP 会先清理该 owner 下已超过 grace 时间的残留 child 进程。不同 owner 的记录不会被清理,因此不会误杀其他 Agent 或人工打开的 SSH。

建议:

- 每个 Agent 线程或任务使用稳定且唯一的 `owner_id`。
- 不要复用其他 Agent 的 `owner_id`。
- 正常结束、超时、`SIGINT`、`SIGTERM`、进程退出时,MCP 也会清理自己登记过的 gateway child。
- `kill -9` 或系统崩溃无法触发进程内清理;下次同 owner 调用会尝试清理残留。

## Matcher 插件放在哪里

内置通用 matcher 和 reference matcher 会随 Python 包一起分发,安装后默认可用:

- `builtin-generic`
- `ttyuyin-opt-account`
- `qmzy-asset-list-id`

用户自写 matcher 推荐放在用户运行目录:

- 用户运行目录:`~/jumpserver-ssh-mcp/matchers/`

然后在 profile 中配置:

```yaml
matchers:
  custom_dirs:
    - ~/jumpserver-ssh-mcp/matchers
```

每个 gateway 可以绑定自己的 matcher:

```yaml
default_gateway: jumpserver-test

gateways:
  jumpserver-test:
    command: ssh -i ~/.ssh/jumpserver-test.pem ops@jump-test.example.com -p2222
    matcher: builtin-generic
```

## Matcher 能做什么

Matcher 只负责 JumpServer 登录入口匹配,不能执行目标机器命令。

它可以返回这些动作:

- 发送目标 host/IP
- 从主机候选列表中选择目标
- 从账号表中选择账号
- 报告已经到达 shell
- 报告未匹配,并返回脱敏 transcript 片段给 Agent 修插件

远程命令仍由 `ssh.run_command` / `ssh.run_script` 统一执行。文件传输优先使用 `ssh.file_push` / `ssh.file_pull`;`ssh.rsync_upload` / `ssh.rsync_download` 只适合 direct SSH + rsync 场景。

## MCP Tools

已有 SSH tools:

- `ssh.run_command`
- `ssh.run_script`
- `ssh.rsync_upload`
- `ssh.rsync_download`
- `ssh.file_push`
- `ssh.file_pull`

文件传输:

Agent 始终调用 `ssh.file_push` 和 `ssh.file_pull`。Koko SFTP 是这两个 MCP tool 内部自动选择的
优先 transport,不额外暴露 `ssh.sftp_upload` 或 `ssh.sftp_download`;未配置有效 SFTP 映射时
自动使用 Base64 portable 路径。

- `ssh.file_push`:单文件上传,支持 direct SSH 和 JumpServer gateway,不依赖远端 rsync。
  gateway 配置了 `sftp_command`、`sftp_asset_path_by_host` 和 `sftp_remote_root` 时优先使用
  JumpServer Koko 虚拟 SFTP 单次传输;数据先写入目标目录内的随机临时文件,远端 SHA256
  校验成功后再原子替换最终文件,失败时尽力清理临时文件。该路径复用 JumpServer SSH/SFTP
  端口,不要求目标机新增监听端口。未配置或目标路径不在映射根目录下时自动回退到便携的
  base64 分块传输。gateway Base64 会把单次 PTY 原始载荷限制在 128KiB,使用 UUID 中间
  文件,并在校验成功后原子替换;普通失败、超时或可捕获的进程中断时会尽力清理中间文件。
  Koko SFTP 不设应用层文件大小上限;Base64/direct portable 路径默认上限 50MB。
- `ssh.file_pull`:单文件下载,支持 direct SSH 和 JumpServer gateway,不依赖远端 rsync。
  有有效 Koko 映射时优先 SFTP,下载到本地同目录 UUID 临时文件,校验远端大小和 SHA256 后
  原子替换;SFTP 不设应用层大小上限,Base64/direct portable 路径默认上限 50MB。
- `ssh.rsync_upload` / `ssh.rsync_download`:保留给 direct SSH 的大文件或目录同步;交互式 JumpServer gateway 不支持 rsync 协议流。

Matcher tools:

- `ssh.matcher_list`
- `ssh.matcher_validate`
- `ssh.matcher_probe`
- `ssh.matcher_test_transcript`

`ssh.matcher_probe` 只验证 matcher 能否通过 JumpServer 到达目标 shell,不执行目标机器命令。

后台 Job tools:

- `ssh.job_start`:提交一个受管理的后台命令,返回稳定 `job_id`;默认和最大运行时间为两小时。
- `ssh.job_status`:通过精确 ID 刷新远端状态;无法确认时返回 `unknown`,不会猜测成功或失败。
- `ssh.job_log`:读取有界输出,默认 80 行、单次最多 64 KiB;远端逻辑输出最多保留 5 MiB。
- `ssh.job_cancel`:需要 `confirmed=true`,只取消该 Job 的精确 systemd cgroup 或已验证进程组。
- `ssh.job_list`:只查询本地 SQLite 索引,不打开 SSH;可按 owner、host、state 筛选。

Job 索引位于 `~/jumpserver-ssh-mcp/jobs/jobs.sqlite3`。每个 `owner_id` 最多 10 个活跃 Job,每个目标 host/account 最多 30 个。终态保留两小时后由后续 Job 操作按精确 ID 惰性清理;`submitting`、`running` 和 `unknown` 不会仅因时间经过而删除。

远端只有在 `systemd --user` 已启用 linger、能够跨 SSH 会话存活时才使用 transient service;否则自动回退到受管理的 `python3 + setsid` supervisor。两种能力都不满足时返回 `unsupported_runner`。

下载、构建、安装等长任务优先使用 `job_start`,由 Agent 用 `job_status` 和按需 `job_log` 轮询。不要在 `job_start` 回执不明确时重复提交相同命令,应保留 `job_id` 并继续对账。

## 安全与审计

远程命令会经过 `SafetyPolicy` 评估。

高风险操作必须显式确认。审计日志基础路径默认是 `~/jumpserver-ssh-mcp/logs/jumpserver-ssh-mcp-audit.jsonl`,也可以通过 `SSH_ASSIST_AUDIT_LOG` 指定;实际写入时会按 UTC 日期滚动为 `~/jumpserver-ssh-mcp/logs/jumpserver-ssh-mcp-audit-YYYY-MM-DD.jsonl`。

不要把私钥内容、明文密码、token 写进 profile、matcher、文档或审计日志。

## 开发验证

运行单元测试:

```bash
PYTHONPATH=src .venv/bin/python -m unittest discover -s tests -p 'test_*.py'
```

运行 MCP server:

```bash
SSH_ASSIST_PROFILE=config/example.yaml .venv/bin/jumpserver-ssh-mcp
```

旧原型入口 `ssh-assist-mcp` 暂时保留为兼容别名。

构建 wheel:

```bash
uv build --wheel
```