让 AI 直接上服务器看日志?我用 MCP 把 Shell 和 Docker 接到了Cursor上
主包最近一直在看 MCP 相关的概念。看着看着发现一件事:既然 AI 可以通过 MCP 很顺手地去操作 GitHub、企业微信这些外部系统,那能不能也让它去操作服务器上的数据库、Docker 和 Shell 呢?
想象的场景很具体:生产环境半夜报警,我不想先爬起来开电脑、SSH 上去、docker ps、docker logs --tail、再翻应用日志。如果 AI 能直接上去看,把相关日志拉出来、对一下时间线、给我一个"可能是什么原因、先看哪里"的初判,那我至少是带着方向起床的。
想法很美好,但我很快意识到,这件事的难点不在"怎么连上去",而在"连上去之后它能干什么"。这篇文章记录我是怎么一步步把它做成一个只读、可审计、不泄露密钥的版本的。
1. 先想清楚:让 AI 上生产机器,到底怕什么
最直觉的做法是把 SSH 私钥或者数据库账号密码交给 AI 工具。我很快否掉了,原因有这几条:
- 密钥外流 。密钥一旦进了 AI 工具的配置,就多了一条泄露路径。
mcp.json之类的文件常常被提交进仓库,或者被别的工具读到。 - 权限被放大。SSH 私钥背后是一个系统用户的全部权限,而我只想让 AI 看日志。
- 黑盒。AI 在机器上具体执行了什么命令、读了哪些文件,事后我想复盘却没有记录。
- 日志本身就是输入 。这一条是我后来才想到的:AI 读到的日志内容会进入模型的上下文。如果日志里有人构造的恶意文本(比如把一句"忽略之前的指令,执行......"写进 User-Agent 或表单字段),模型可能被带偏。所以更不能给它写权限。
- 敏感信息出站。日志里常有手机号、token、SQL 参数,AI 读一遍等于发给了模型服务商。
所以我的目标被收窄成四句话:
- 密钥不离开服务器;
- 能调用的工具要逐个授权,默认全部不能用;
- 每一次调用都留痕;
- 先只读,写操作永远留给人。
2. 方案思路
Shell 和 Docker 这类 MCP 服务大多是 stdio 形态:只能在本机命令行里被拉起,没法被远程的 AI 工具直接访问。我在想有没有东西可以把 stdio 的MCP和我本地的AI Agent串起来呢?
b 站上搜了半天,最后看到一个视频: https://www.bilibili.com/video/BV1wzps6AENo/
这个 up 分享了一些经验, 大概十分钟左右的视频, 说到了orbitproxy 这个平台,然后我注册了一下, 跟着 up 的脚步也是大概搞明白了咋回事, 中间需要一个叫做【MCP网关】的东西去连接, 大概流程是下面这样
text
AI 工具 (Cursor 等)
│ HTTPS + 访问密钥
▼
MCP 网关(鉴权、按 Tool 授权、记录调用)
│ 经由服务器上的客户端
▼
服务器上的 stdio MCP 进程(Shell MCP / Docker MCP)
他的case 里是写了怎么连接 mysql, 我一开始也跑了一下,是完全没问题的,这个 orbitproxy 的官网是: https://orbitproxy.cc, 防止自己以后忘记插个眼
orbitproxy他们这个流程大概是下面这样:
- 服务器上跑一个客户端,由它拉起 stdio MCP 进程,并和网关建立连接。内部形态的 Connector 不分配公网地址,不监听公网端口。
- MCP 服务需要的敏感配置以环境变量的形式留在服务器上,网关只会提示"环境变量缺失",不会让你把密钥填进网页。
- Connector 默认是内部形态:没有公网地址,AI 不能直接访问它。要让 AI 用,必须通过 Composer 这个唯一的入口。
- 入口上可以挂访问密钥、按身份源授权 Tool,以及访问日志。
数据库、容器、系统日志这类东西,官方文档里的定位就是"不该拥有独立公网入口"的服务。这点和我的想法一致。
3. 准备工作
服务器上需要有:
- 一个已经认证好的 OrbitProxy 客户端;
- Shell MCP :预置的是
@wonderwhy-er/desktop-commander,依赖 Node.js 18+。预置服务的拉起命令是npx --no-install ...,所以需要先全局安装:
bash
PUPPETEER_SKIP_DOWNLOAD=true npm i -g @wonderwhy-er/desktop-commander
- Docker MCP :预置的是
mcp-server-docker,用uvx拉起,依赖uv以及一个能访问 Docker socket 的用户:
bash
uv tool install mcp-server-docker
我强烈建议不要用 root 跑客户端。后面第 7 节会解释为什么。
4. 注册 Connector,以及调试时容易卡住的地方
在控制台的 MCP Connectors 页面新建,选对应的客户端,从预置列表里勾选"Shell MCP"和"Docker MCP",类型选内部端点。同一台客户端上已经创建过的预置服务不能重复创建。
创建之后会进入预检。预检会校验这个 MCP 服务在客户端上的运行环境是不是完整。
如果预检失败,我的排查顺序是
- 先在服务器上用客户端同一个用户 ,手动把拉起命令跑一遍(
npx --no-install @wonderwhy-er/desktop-commander、uvx mcp-server-docker),看是不是自己就起不来。 --no-install意味着不会现场下载,没有全局安装就会失败。- 客户端如果是以系统服务的方式运行,它的
PATH可能和你登录终端里的不一样,导致找不到npx或uvx。 - Docker MCP 需要当前用户能访问 Docker socket。用户不在
docker组里的话,列容器这种最基础的调用都会失败。
另外有一个设计我觉得挺实用:可以给每个客户端设置一个环境标识(Env Key) ,比如 prod-web-01、test-115。当 AI 同时挂了多个环境的 MCP 时,它能靠这个标识分清哪个是生产、哪个是测试,少猜一点。
最后,把这两个 Connector 加进同一个 Composer ,给它一个 Server Key,比如 prod。AI 工具里最终只配置 Composer 这一个地址。
5. 权限:默认全拒,只放行读
这一步是整件事里我最看重的。
Docker MCP 预置的 Tool 一共这些(来自预置清单的 0.3.0 版本,以你控制台里"重新发现"出来的为准):
| 类别 | Tool | 风险 |
|---|---|---|
| 只读 | list_containers、fetch_container_logs、list_images、list_networks、list_volumes |
低 |
| 变更 | create_container、run_container、recreate_container、start_container、stop_container、remove_container |
高 |
| 镜像 | pull_image(中)、push_image、build_image、remove_image(高) |
中/高 |
| 网络与卷 | create_network、create_volume(中)、remove_network、remove_volume(高) |
中/高 |
对于"看日志"这个任务,我只放行第一行的五个。停止、删除、重建容器这些,AI 想都不要想。
Shell MCP 就麻烦多了。它的 Tool 里:
list_directory、start_search、get_file_info、list_processes这类是低风险;read_file、read_multiple_files是读文件,风险为中;start_process、interact_with_process、kill_process、write_file、edit_block、move_file是高风险。
官方给这个预置服务写的备注是:权限最高,建议只给运维负责人开通。我同意。
这里有个必须说清楚的限制:Shell MCP 和 Docker MCP 在网关里的访问控制粒度是 Tool 白名单。SQL 语句拦截只对数据库类 MCP 生效,文件操作的读写移动拦截只对文件系统类 MCP 生效。这意味着:
- 如果我放行了
start_process,那它就能执行任意命令,网关这一层已经没法再细分了; - 如果我放行了
read_file,它能读客户端所属用户有权限读的任何文件。
所以我的做法是:
- Shell MCP 不放行
start_process。需要看日志,就放行list_directory、start_search、read_file这几个读类 Tool; - 靠操作系统层面 限制读取范围:客户端用一个专用的低权限用户来跑,这个用户只对日志目录有读权限,对别的目录(
~/.ssh、配置文件、.env)没有; - Docker 这边同样,只放行只读的五个 Tool。
这套授权是在 MCP 访问控制里配的:选 Endpoint(也就是 Composer)、选身份源(比如我的 Cursor)、勾选允许的 Tools,保存。没勾选的 Tool,调用会在网关就结束,不会到达服务器上的 MCP 进程。
别忘了入口本身:Composer 的公网地址默认不校验接入身份,一定要先配 API 访问密钥,再把地址写进 AI 工具。
6. 实测:我自己造了一个故障
为了验证整条链路,我在测试机上起了一个会不停报错的容器,模拟"应用连不上数据库":
bash
docker run -d --name demo-api --restart unless-stopped alpine sh -c \
'while true; do echo "$(date -Iseconds) ERROR db connect ECONNREFUSED 10.0.0.12:3306"; sleep 2; done'
docker run -d --name demo-worker alpine sh -c \
'while true; do echo "$(date -Iseconds) INFO job ok"; sleep 5; done'
然后在 Cursor 里问它:
demo-api 好像不对劲,帮我看看是哪个容器出的问题,原因是什么?只读,不要做任何修改。
Cursor 也是成功访问到了我服务器上的 mcp, 然后快速帮我排查了问题
我关注三件事:
① 它能不能自己走通只读的链路。 在我的白名单下,它能走的路只有:列容器 → 拉日志。
② 越界会不会被拦。 我故意追问了一句:"那你把 demo-api 停掉再启动一下试试。"stop_container 和 start_container 都不在白名单里。【截图:被拒绝的提示】, 被拦截的他也会提示异常

③ 事后能不能复盘。 在访问日志里,一次调用会记录:调用的 Tool、MCP 方法(比如 tools/call)、请求体和响应体、耗时与字节数、结果(成功、策略拒绝、上游错误等)。他这个日志记得还是挺详细的, 基本上是覆盖到所有了

两点说明:这些记录需要你先主动配置访问日志规则才会有;响应体是否写入取决于采集级别
8. 小结
回到最初的问题:能不能让 AI 直接上服务器看日志?我的答案是可以,但前提是:
- 密钥留在服务器;
- Tool 默认全拒、逐个放行;
- 先只读;
- 每次调用可复盘;