# MShell 本机 Agent / MCP 安全接入

MShell 可以向本机运行的 Codex、Claude 等支持 MCP 的 Agent 提供当前 SSH 会话、远程文件和命令能力。“设置 - Agent 接入”提供查询、确认、执行三档权限；默认使用“查询”。服务只监听 `127.0.0.1`，不会暴露给局域网或公网。

## 启用方式

1. 在 MShell 的“设置 - Agent 接入”中打开“启用 MCP 服务”。
2. 保持需要读取的 SSH 会话处于已连接状态。
3. 使用页面中的“复制 Codex 配置”或“复制 Claude 配置”按钮，写入对应客户端配置。
4. 配置字段、工具清单、写入规则和故障处理可直接点击该页面右上角的“使用详情”查看。

Codex 可以使用直接 `Authorization` 标头或 Bearer 令牌环境变量认证。MShell 的“复制 Codex 配置”按钮提供的是 PowerShell 会话变量和 `config.toml` 的环境变量配置；令牌重新生成后，需要更新配置并重启 Agent。

## 在 Codex 图形界面中添加

在 Codex 的“连接至自定义 MCP”页面，选择“流式 HTTP”（Streamable HTTP），不要选择“STDIO”。MShell 已经作为本机 HTTP 服务运行，不需要 Codex 再启动一个命令行进程。

按以下内容填写：

| 字段                | 推荐的直接标头模式                  |
| ------------------- | ----------------------------------- |
| 名称                | `MShell`，也可以使用其他名称        |
| 类型                | `流式 HTTP`                         |
| URL / 服务端点      | `http://127.0.0.1:47821/mcp`        |
| Bearer 令牌环境变量 | 留空                                |
| 标头键              | `Authorization`                     |
| 标头值              | `Bearer <MShell 中复制的 MCP 令牌>` |

标头值必须包含开头的 `Bearer` 和一个空格。只填写令牌本身会被 MShell 按 `401 Unauthorized` 拒绝。例如，填写 `abc123` 是错误的，填写 `Bearer abc123` 才是正确格式。

也可以选择环境变量模式，但不要与直接标头模式混用：

1. 在启动 Codex 的环境中，将 `MSHELL_MCP_TOKEN` 的值设置为 MShell 中复制的令牌。
2. 在 Codex 的“Bearer 令牌环境变量”字段中填写 `MSHELL_MCP_TOKEN`，这里填写的是变量名称，不是令牌内容。
3. 删除 `Authorization` 标头，避免两个认证来源产生冲突。

填写完成后保存，完全退出并重新打开 Codex，再新建任务加载 MCP 工具。

以下内容不适用于 MShell：

- 不要把 `http://127.0.0.1:47821/mcp` 填到“启动命令”或“参数”中。
- 不要填写 `npx`、`node`、`python` 等启动命令。
- 不需要填写 STDIO 参数、工作目录或本地脚本路径。

## 使用 Codex CLI 添加

在启动 Codex 的同一个 PowerShell 窗口中执行：

```powershell
$env:MSHELL_MCP_TOKEN = '<从 MShell 复制的令牌>'
codex mcp add mshell --url http://127.0.0.1:47821/mcp --bearer-token-env-var MSHELL_MCP_TOKEN
codex mcp list
```

检查或移除已有配置：

```powershell
codex mcp get mshell
codex mcp remove mshell
```

也可以手动写入 `~/.codex/config.toml`：

```toml
[mcp_servers.mshell]
url = "http://127.0.0.1:47821/mcp"
bearer_token_env_var = "MSHELL_MCP_TOKEN"
```

PowerShell 环境变量只对当前窗口及其子进程有效。若 Codex 是从开始菜单或其他图形启动器打开的，它可能不会继承该变量；此时应从同一个 PowerShell 窗口启动 Codex，或在 Codex 图形界面中配置请求头。

## STDIO 与流式 HTTP 的区别

| 类型      | 谁启动服务                                     | 典型配置                           | MShell 是否使用 |
| --------- | ---------------------------------------------- | ---------------------------------- | --------------- |
| STDIO     | Codex 启动本地命令并通过标准输入输出通信       | 启动命令、参数、环境变量、工作目录 | 否              |
| 流式 HTTP | MShell 自己运行 HTTP 服务，Codex 通过 URL 访问 | URL、Authorization 请求头          | 是              |

只有在接入另一个明确提供 STDIO 入口的 MCP 服务器时，才填写“启动命令”和“参数”。把 MShell 当作 STDIO 添加会导致连接失败。

## 首次验证流程

配置完成后，按“列出会话、确认连接、读取文件或执行查询”的顺序验证：

1. 确认 MShell MCP 服务为“运行中”，并且至少有一个 SSH 会话状态为“已连接”。
2. 在 Codex 中先发送：

   ```text
   请调用 list_ssh_sessions，列出当前已连接的 SSH 会话。
   不要执行命令，不要修改文件。
   只返回 connectionId、主机、端口、用户名和会话名称。
   ```

3. 从返回结果中确认目标服务器，并明确指定一个 `connectionId`。不要让 Agent 根据主机名、用户名或列表顺序自行猜测。
4. 再发送：

   ```text
   使用 connectionId="这里填写已确认的 ID"。
   不要执行命令，不要修改文件。
   请读取 /etc/os-release，只告诉我文件是否存在，不要输出文件内容。
   ```

5. 读取目录时使用 `list_remote_files`；读取文本时使用 `read_remote_file`。当前没有单独的文件存在性工具，判断文件是否存在会通过读取结果完成。

## 执行查询命令

确认 `connectionId` 后，可以让 Agent 调用 `execute_readonly_command` 执行一条查询命令，并读取返回结果。例如：

```text
使用 connectionId="这里填写已确认的 ID"。
调用 execute_readonly_command 执行 df -h，并根据返回的 output 总结磁盘使用情况。
不要修改文件，不要执行其他命令。
```

工具返回：

- `command`：实际执行的查询命令。
- `exitCode`：远程命令退出码；非 0 只表示命令本身失败（例如文件不存在），工具调用仍会返回结果。被信号终止时另有 `signal`。
- `output`：命令标准输出，Agent 可以直接读取和分析。
- `outputBytes`：完整标准输出的 UTF-8 字节数。
- `truncated`：标准输出是否因超过上限而被截断。
- `stderr`、`stderrBytes`、`stderrTruncated`：标准错误输出及其大小、截断标记，与标准输出分开返回。

查询命令规则：

- 每次只能执行一条命令，最长 `2000` 个字符。
- 默认超时 `10` 秒，调用方最多可设置为 `20` 秒。
- 返回给 Agent 的输出最多 `64 KiB`；底层捕获超过 `256 KiB` 时会终止查询并返回错误。
- 支持 `ls`、`df`、`du`、`free`、`ps`、`ss`、`ip`、`systemctl status`、`journalctl`、`docker ps` 等白名单查询。
- 不支持管道、重定向、`;`、`&&`、命令替换、脚本解释器、`sudo` 或不在白名单中的程序。
- 每个参数按字面量传给远程 Shell，不做通配符、花括号、`~` 或变量展开；例如 `ls /var/log/*.log` 会查找名为 `*.log` 的文件。需要匹配多个文件时请使用 `find -name`。
- 各程序只接受已确认只读的选项，未知选项一律拒绝。
- `systemctl restart`、`docker stop`、`iptables -F`、`find -delete` 等修改操作会被拒绝。

## 数据库查询

`query_database` 在查询、确认、执行三种权限下都保持只读。它通过已连接的 SSH 会话查询数据库，可读取账号有权限访问的业务表、日志表、系统目录和表结构，不维护业务表白名单。

当用户没有明确给出数据库类型、容器或主机、数据库名和数据库用户时，Agent 必须先调用 `discover_database_targets`。该工具只读取数据库客户端、容器名称和镜像、应用容器、发布端口、非秘密数据库用户名，以及能够安全枚举的数据库名称；不会返回密码或完整容器环境变量。宿主机上的 PostgreSQL、MySQL 和 MariaDB 会使用当前 SSH 账号已经配置好的非交互认证尝试枚举，SQLite 会在常用应用目录中限量查找并验证数据库文件。

发现结果包含 `targets`、`applications` 和 `requiresSelection`。同一服务器存在多个数据库、站点或应用时，Agent 必须把候选项交给用户确认业务目标，再把选中目标的 `engine`、`container`、`host`、`port`、`database`、`username` 和 `systemUser` 原样用于 `query_database`。不能根据活动连接、名称相似、列表顺序、默认数据库或端口自行判断目标。发现工具只能提供候选关系，不能证明某个网站必然使用某个数据库。

支持 `engine=mysql`、`mariadb`、`postgresql`、`sqlite`、`mongodb`、`redis`、`sqlserver`、`oracle`，以及通过可选的 `container` 参数在 Docker 容器内运行客户端。工具不会执行任意数据库 Shell；MongoDB 和 Redis 使用结构化 JSON 请求，SQL 数据库使用受校验的单条只读 SQL。

前提：

- 远程 Linux 主机或目标容器内安装对应的 `mysql`、`mariadb`、`psql`、`sqlite3`、`mongosh`、`redis-cli`、`sqlcmd` 或 `sqlplus` 客户端，以及 GNU `timeout`。容器模式在容器内部执行超时控制。
- MySQL 需要 5.7.8+，MariaDB 需要 10.1+，psql 需要支持 `--csv`，SQLite CLI 需要支持 `-safe` 和 `-readonly`，Redis CLI 需要支持 `--json`，Oracle 需要支持 `SET MARKUP CSV` 和 `FETCH FIRST`。复杂查询还需要相应数据库版本支持。
- 使用数据库只读账号，在远程提前配置 `.my.cnf`、`.pgpass` 或 peer/socket 认证。容器模式的认证配置也必须在容器中可用；不要把密码放进工具参数、SQL 或连接 URL。
- MongoDB 使用 `mongosh` 已有的无交互认证；官方 Docker 镜像可使用容器内的 `MONGO_INITDB_ROOT_USERNAME` 和 `MONGO_INITDB_ROOT_PASSWORD`。Redis 使用 `REDISCLI_AUTH`，Docker 模式也识别常见的 `REDIS_PASSWORD`。SQL Server 使用 `SQLCMDPASSWORD` 或已配置的集成认证，官方容器识别 `MSSQL_SA_PASSWORD`。Oracle 使用安全外部密码存储、wallet、操作系统认证或容器内的 SYSDBA 认证。凭据只在远程客户端内部使用，不会出现在发现结果和 MCP 审计中。
- 宿主机认证成功时，发现结果会提供可直接查询的 `host`、`port`、`database` 和 `username`。认证失败时仍会返回 `requires-configuration`，表示只确认客户端存在，尚不能枚举数据库；切换 Agent 操作权限不会改变数据库认证结果。
- SSH 会话使用 root 且 PostgreSQL 配置了本机 peer 认证时，发现器可以采用实际 PostgreSQL 服务进程的非 root 系统账号，并在目标中返回 `systemUser`。查询工具只用该账号启动数据库客户端，SQL 校验、只读事务、行数和超时限制仍然生效。
- PostgreSQL 容器的 `username` 和 `database` 必须与容器初始化时的账号和数据库一致，不要默认使用 `postgres/postgres`。容器内通过本地 socket 已能认证时不需要 `.pgpass`；从宿主机通过 TCP 连接且服务端要求密码时才需要非交互凭据。
- 服务端只读事务、SQL 校验不能代替数据库最小权限配置。生产环境优先配置专用只读账号；root SSH 会话采用 PostgreSQL 系统账号的 peer 认证时读取范围较大，自定义类型、视图、外部表和函数扩展的行为仍由数据库服务器控制。

参数：

| 参数                       | 含义                                                              |
| -------------------------- | ----------------------------------------------------------------- |
| `connectionId`             | 已确认的 SSH 会话 ID                                              |
| `engine`                   | 八种已支持的数据库引擎                                            |
| `database`                 | 数据库名称；Redis 使用数字库编号，SQLite 使用绝对文件路径         |
| `query`                    | 单条只读 SQL，或 MongoDB/Redis JSON 请求；最多 20000 个字符       |
| `host`、`port`、`username` | 可选连接参数，SQLite 不使用这些参数                               |
| `systemUser`               | 可选宿主机系统账号；只能使用发现结果提供的值，与 `container` 互斥 |
| `container`                | 可选 Docker 容器名或 ID                                           |
| `maxRows`                  | SELECT 返回行数上限，默认 200，最大 2000                          |
| `timeoutMs`                | 默认 20000，最大 120000 毫秒                                      |

示例：

```text
1. 调用 discover_database_targets(connectionId)。
2. 列出候选的应用容器和业务数据库，让用户确认要查询的网站或程序。
3. 使用用户确认目标返回的准确参数调用 query_database。
4. 查询使用稳定的 ORDER BY 和 LIMIT/OFFSET，不读取无关数据库。
```

MongoDB 查询示例：

```json
{
  "operation": "find",
  "collection": "logs",
  "filter": { "level": "error" },
  "projection": { "message": 1, "createdAt": 1 },
  "sort": { "createdAt": -1 },
  "limit": 100
}
```

MongoDB 支持 `find`、`aggregate`、`count`、`distinct` 和 `listCollections`。`aggregate` 使用 `pipeline` 数组，`distinct` 使用 `field`；拒绝 `$out`、`$merge`、`$where`、`$function` 和 `$accumulator`。返回结果为 Extended JSON，并强制应用 `maxRows` 和 `maxTimeMS`。

Redis 查询示例：

```json
{
  "command": "HGETALL",
  "args": ["customer:123"]
}
```

Redis 只接受内置只读命令白名单，例如 `GET`、`MGET`、`HGETALL`、`LRANGE`、`SCAN`、`ZRANGE`、`XRANGE`、`INFO` 和 `DBSIZE`；拒绝 `SET`、`DEL`、`EVAL`、`CONFIG`、`FLUSHALL` 等写入、脚本和管理操作。命令及参数通过标准输入发送，不进入远程进程命令行。

MySQL、MariaDB、PostgreSQL、SQLite 和 SQL Server 支持单条 `SELECT`、查询型 `WITH`、关联、聚合、子查询及常用只读函数。MySQL/MariaDB 还支持可解析的 `SHOW TABLES`、`SHOW COLUMNS`、`SHOW CREATE TABLE`、`DESCRIBE`。Oracle 支持经过保守词法校验和函数白名单检查的单条 `SELECT` 或 `WITH`。也可查询 `information_schema`、各数据库系统目录和 `sqlite_master` 了解表结构。

查询入口始终拒绝多条 SQL、修改语句、写锁、`SELECT INTO`、文件输出、客户端转义命令及未确认为只读的函数；SQL Server 另外拒绝 `EXEC`、`sp_*`/`xp_*` 存储过程、`WAITFOR`、`DBCC`、`BACKUP`、`SHUTDOWN` 等管理命令（字符串和带引号标识符中的同名文字不受影响）。即使处于“执行”，`query_database` 仍是只读；需要修改时使用受权限模式控制的 `execute_command`。

返回 `output`、`format`、`maxRows`、`outputBytes` 和 `truncated`。MySQL、MariaDB 和 SQL Server 返回 TSV，PostgreSQL、SQLite 和 Oracle 返回 CSV，MongoDB 和 Redis 返回 JSON。关系型数据库在数据库侧限制行数；元数据查询的 `maxRows` 为 null，受字节上限控制。`truncated` 仅表示输出超过 64 KiB 被截断，不表示已读完所有记录。继续查询请使用稳定分页；关联查询建议明确列名并为重复列起别名。底层捕获超过 256 KiB 时中止查询。

查询内容通过 SSH 标准输入传递，不放入进程命令行。审计只记录查询哈希、长度、引擎和结果大小，不记录查询原文或查询数据。数据库客户端原始错误可能含查询内容和敏感信息，因此不会直接返回或写入审计。

查询失败会返回 `errorCode`、中文 `message`、`guidance` 和 `retryable`，用于区分数据库用户不存在、数据库不存在、认证失败、客户端缺失、容器不可用、连接失败、查询超时、权限不足等情况。返回内容和审计均不包含原始数据库错误、SQL 原文或凭据。

系统及容器日志仍可使用 `execute_readonly_command`，例如 `journalctl -n 100 --no-pager`、`docker logs --tail 100 app`、`tail -n 100 /var/log/mysql/error.log`。

## 写文件和执行修改命令

写入能力分为两个工具：

| 工具                | 作用                                            |
| ------------------- | ----------------------------------------------- |
| `write_remote_file` | 通过 SFTP 创建或完整覆盖一个 UTF-8 文本文件。   |
| `execute_command`   | 执行可能修改服务器的 SSH 命令，并返回命令输出。 |

两个工具都受 MShell 本机的三档权限控制：查询模式直接拒绝；确认模式把具体操作发送到 MShell 界面并等待一次批准；执行模式直接运行，适合已经明确授权的连续任务。

写文件示例：

```text
使用 connectionId="这里填写已确认的 ID"。
Agent 权限为“确认”或“执行”。
调用 write_remote_file，filePath 使用 /etc/example.conf，写入我要求的完整内容。
```

执行修改命令示例：

```text
使用 connectionId="这里填写已确认的 ID"。
Agent 权限为“确认”或“执行”。
调用 execute_command 执行 systemctl restart nginx。
读取返回的 output 并告诉我执行结果，不要执行其他命令。
```

写入限制：

- `write_remote_file` 每次最多写入 `1 MiB` UTF-8 文本，目标已存在时会完整覆盖，不是局部补丁。
- `execute_command` 最长 `8000` 个字符，默认超时 `20` 秒，最大 `120` 秒。
- 修改命令返回给 Agent 的标准输出和标准错误各最多 `64 KiB`，并附带 `exitCode`；底层捕获上限为 `2 MiB`。
- 修改命令不经过查询白名单，可以执行删除、覆盖、安装、重启等高风险操作。
- 超时或客户端断开不能保证远程进程已经回滚；执行前必须准备备份和恢复路径。
- 权限模式会保存在本机，重启 MShell 后仍保持上次状态；任务完成后建议切回“查询”。
- “确认”由 MShell 对每次写工具调用弹窗审批；“执行”不是第二个密码，任何持有 MCP Token 的客户端都可以直接调用写入工具。
- 审批框显示目标会话名称和 `用户名@主机:端口`、完整命令或完整写入内容，以及自动拒绝时间；多个请求排队逐个确认。审批 2 分钟内未处理、MShell 锁屏、权限模式切换或 Agent 断开连接时，请求自动拒绝且审批框关闭。
- Agent 断开连接时，正在执行的命令也会被取消；批准后如果会话已断开或权限已切到“查询”，操作不会执行。

## 服务端点

- 默认端点：`http://127.0.0.1:47821/mcp`
- 认证方式：`Authorization: Bearer <MCP 令牌>`
- 令牌：由 MShell 生成，可在“设置 - Agent 接入”中复制或重新生成。

## 可用工具

| 工具                        | 作用                                               |
| --------------------------- | -------------------------------------------------- |
| `list_ssh_sessions`         | 仅列出当前确实处于已连接状态的 SSH 会话。          |
| `get_ssh_connection_status` | 读取一个已连接 SSH 会话的安全状态信息。            |
| `list_remote_files`         | 通过该会话的 SFTP 列出一个远程目录。               |
| `read_remote_file`          | 通过 SFTP 读取一个 UTF-8 文本文件。                |
| `execute_readonly_command`  | 执行单条白名单查询命令并返回命令和输出。           |
| `discover_database_targets` | 发现数据库与应用候选目标，要求多目标确认。         |
| `query_database`            | 查询数据库数据、表结构及日志表，支持 Docker 容器。 |
| `write_remote_file`         | 确认模式批准后或执行模式下写入远程文本文件。       |
| `execute_command`           | 确认模式批准后或执行模式下执行修改命令。           |

## 约束与安全边界

- Agent 只能使用 MShell 当前已连接的 SSH 会话；断开或重连期间的会话不会被列出或读取。
- 默认“查询”只提供读取和白名单查询；写文件或执行修改命令需要“确认”或“执行”。
- 不返回密码、私钥、代理凭据或 Electron IPC 接口。
- 单次目录列表最多返回 `2000` 项；单个远程文件最多读取 `1 MiB`。
- 每次 Agent 工具调用都会写入 MShell 的审计日志；命令输出只记录字节数和退出码，不记录内容。
- Agent 接入的启用状态、端口、令牌和权限模式只能通过“设置 - Agent 接入”的专用接口修改；保存其它设置不会覆盖这些值。
- 数据库发现和 MongoDB 容器查询从容器环境变量读取凭据并在 `mongosh` 内部认证，密码不会出现在进程参数中。
- SSH 自动重连后，MShell 会按当前 SSH 客户端重建 SFTP 通道，避免使用旧连接遗留的通道。
- MCP 服务当前运行在 MShell Windows 桌面端；Android 端不能作为 MCP HTTP 服务端。
- MCP 端点只监听本机 `127.0.0.1`。不要通过端口转发、反向代理、内网穿透或防火墙把 `47821` 暴露给其他设备。
- MCP 令牌不是 SSH 密码，也不是同步加密密码；三者必须分别保管。
- 读取配置文件、日志和代码仍可能暴露业务数据。只允许 Agent 读取完成当前任务所需的最小路径。
- 查询命令的输出也可能包含进程参数、环境、日志、IP、容器配置或其他敏感信息，使用前必须确认查询范围。
- 权限模式不是额外身份认证。“执行”期间 MCP Token 泄露时，应视为远程服务器写入能力同时泄露并立即轮换 Token。

该能力适合让 Agent 做状态检查、阅读配置和分析日志；日常修改使用“确认”，明确授权的连续任务临时使用“执行”。高风险变更仍应准备备份和回滚方式，任务完成后应切回“查询”。
