让 Claude Code、Codex 直接排查你的服务器:用 MCP 把 SSH 主机接给 AI 编程工具Let Claude Code and Codex Troubleshoot Your Servers: Connecting SSH Hosts to AI Coding Tools over MCP
你让 Claude Code 改完了一个接口,它顺手把部署脚本也写好了。可真正跑起来是在服务器上:日志在那儿,进程在那儿,502 也在那儿。这时候它只能问你「能把 nginx 的错误日志贴给我吗」——你开终端、ssh 上去、tail、复制、粘贴回来。一次还行,一晚上排障下来就烦了。
两条老路,都不太对
第一条:把 SSH 私钥交给它。claude-code 和 codex 都能直接跑 shell,给它一把能登服务器的私钥,它就能自己 ssh。问题是从此它能对你的服务器做任何事——一次理解偏差的 rm -rf、一条自作聪明的 systemctl restart,没有任何东西拦得住。而且私钥、跳板机配置、端口都得摆在它能读到的地方。
第二条:你当搬运工。安全是安全,但排障是个来回问答的过程:看日志 → 看进程 → 看磁盘 → 再看日志。每一步都要你手动执行再贴回去,AI 的价值被你的复制粘贴速度限死了。
缺的是一个中间层:让 AI 能自己去服务器上看,但只能看、不能动,而且不用拿到凭证。这正是 MCP 擅长的形状。
MCP 是什么,三句话
MCP(Model Context Protocol)是一个开放协议,规定了 AI 应用怎样发现并调用外部提供的「工具」。一个 MCP 服务对外声明「我有这几个工具、各接受什么参数」,AI 客户端(claude-code、codex、Claude 桌面版、Cursor……)连上后就能按需调用,结果回到模型的上下文里继续推理。
它的关键在于工具是服务方定义的:服务方决定开放什么、不开放什么、每个调用怎么校验。模型只能在这份菜单里点菜,点不到菜单外的东西。
换句话说,只要 MCP 服务这边把菜单做成「只读」,AI 想改也改不了——不需要靠提示词求它「小心点」。
Shellby 的做法:凭证不出 App,能力只读开放
Shellby 是一款 Mac / iPhone / iPad 上的 SSH 客户端,主机、密钥、跳板机本来就配在里面。2.0.0 起它的 Mac 版可以作为一个 MCP 服务运行,把这些主机开放给桌面上的 AI 编程工具。设计上有几条硬边界:
- agent 只见主机名。工具参数里只有「哪台主机」,密码、私钥、口令从不出现在协议报文里;连接由 Shellby 用它保管的凭证建立。
- 只读。当前开放四个工具:列主机、执行只读命令、读远程文件、取监控指标。会改动系统状态的命令由本地分类器识别并拒绝,原因回传给 agent。
- 只在本机、只在运行时。服务只监听 127.0.0.1,每次请求校验令牌;令牌存系统钥匙串。Shellby 退出服务就停,没有后台常驻进程。
- 留痕。每次调用记一行审计日志:谁、什么时候、对哪台主机、跑了什么。
接入:三步,五分钟
- 开启服务。Shellby ▸ 设置 ▸ 外部 Agent,打开开关。状态变成「运行中 · 端口 xxxxx」——端口首次随机选定后会固定下来,之后不用再改客户端配置。
- 复制接入命令。同一页给出了针对 claude-code 与 codex 的完整命令,点「复制」。
- 在终端里执行。完成。
两家的鉴权方式不一样,命令长这样(端口与令牌以你设置页显示的为准):
# claude-code
claude mcp add shellby --transport http http://127.0.0.1:52731/mcp \
--header "Authorization: Bearer <你的令牌>"
# codex —— 只认环境变量名,所以两行
export SHELLBY_MCP_TOKEN=<你的令牌>
codex mcp add shellby --url http://127.0.0.1:52731/mcp \
--bearer-token-env-var SHELLBY_MCP_TOKEN
codex 那行 export 要写进 ~/.zshrc 才长期有效。这意味着令牌会落在一个明文文件里——这是 codex 的形态决定的,你可以接受它,或者只在需要时临时 export。
装好之后跑一句 claude mcp list(或 codex mcp list)确认 shellby 在列表里且状态正常。
实战:一次 502 排查
接下来就跟平常一样对话。假设你在 claude-code 里说:
它大概会这么做(每一步都是一次 MCP 工具调用,你在 Shellby 的审计日志里都能看到):
list_hosts—— 拿到 prod-web-01 的标识,顺便看到它的标签是appnginx。run_command:tail -n 50 /var/log/nginx/error.log—— 看到一串upstream prematurely closed connection while reading response header from upstream … 127.0.0.1:3000。get_metrics—— 负载 3.9,内存 91%,占 CPU 最高的进程是node。read_file:/etc/nginx/conf.d/app.conf—— 确认上游确实指向 3000 端口,超时是默认值。
然后它给出结论:Node 进程把 CPU 占满,响应超过 nginx 的上游超时,于是 502。到这里它可能会想「顺手」systemctl restart app——这条会被 Shellby 拒绝,并告诉它「会改动系统状态的命令当前不放行」。它会把建议写给你,由你决定是否在 Shellby 里自己执行。
整个过程你没有开过终端,也没有把任何凭证交出去。
几个用起来才会碰到的细节
它为什么有时被拒得「冤枉」?
分类器按整条命令判定。cat a.log && systemctl status b 是只读的,但 grep x a.log > /tmp/out 有重定向写文件,会被拒。Shellby 给模型的工具描述里已经写了「写成可独立审阅的单条、不要用 && 串联不相关步骤、被拒时不要改写绕过」,实际用下来模型很少踩这个坑。
输出太长怎么办?
单次调用的输出上限是 64 KB,超出会截断并告诉模型 truncated: true,模型会自己缩小范围(加 grep、tail)。读大日志本来就该这么做,而不是整个 cat 进上下文。
它会复用我开着的会话吗?
会,而且优先。你正在 Shellby 里连着的机器,agent 查起来几乎零等待;没连着的才新建连接。跳板机、Mosh 兼容传输这些配置也都跟着主机走,agent 不用知道。
令牌泄露了怎么办?
设置页「重置访问令牌」——旧令牌立即作废,用新命令重新 add 一次即可。
它不做什么
- 不做写操作。重启服务、改配置、写文件目前都不开放。带审批卡的变更能力在计划中,但要等只读版本用扎实了再上。
- 不在 iPhone / iPad 上提供。桌面上的编程工具才是消费方,而且 iOS 的后台限制会让本地服务随时被挂起。
- 不常驻后台。要用就把 Shellby 开着;这是刻意的选择。
- 没有绕过分类器的「直通」工具。哪怕只是「调试方便」也没有——一旦存在,agent 一定优先用它,所有护栏形同虚设。
小结
把 AI 编程工具接到服务器上,本质上是在回答一个问题:你愿意把多大的权限交给一个会犯错的自动化。MCP 让这个权限可以被精确地定义,Shellby 把它定义成了「只读、不见凭证、全程留痕」。这已经覆盖了排障、看日志、查状态这大半的日常场景,而风险面几乎为零。剩下那一小半需要动手的,仍然由你自己来。
更完整的参数与限制,见外部 Agent · MCP 文档。
Claude Code just finished changing an endpoint and, while it was at it, wrote the deploy script too. But the thing actually runs on a server: the logs are there, the process is there, and so is the 502. All it can do is ask “could you paste me the nginx error log?” — so you open a terminal, ssh in, tail, copy, paste it back. Fine once. After an evening of debugging, it is not fine.
Two old answers, neither quite right
One: give it your SSH key. claude-code and codex can both run shell commands, so hand them a key that logs into the server and they will ssh by themselves. The catch is that from then on they can do anything to that server — one misread rm -rf, one helpful systemctl restart, and nothing stands in the way. The key, jump-host config and ports also have to sit somewhere the tool can read.
Two: you become the courier. Safe, but debugging is a back-and-forth: look at logs → look at processes → look at disk → look at logs again. Every step waits for you to run it and paste it back, so the AI is throttled to your copy-paste speed.
What is missing is a layer in between: let the AI go and look for itself, but look only, never touch, and without ever holding credentials. That is exactly the shape MCP is good at.
MCP in three sentences
MCP (Model Context Protocol) is an open protocol that defines how AI applications discover and call externally provided “tools”. An MCP server declares “here are my tools and the arguments each takes”; an AI client (claude-code, codex, Claude Desktop, Cursor…) connects and calls them as needed, and the results flow back into the model's context.
The key point is that the server defines the tools: it decides what is exposed, what is not, and how every call is validated. The model can only order off that menu.
In other words, if the MCP server makes the menu read-only, the AI cannot change anything even if it wants to — no prompt begging it to “be careful” required.
Shellby's approach: credentials stay in, read-only goes out
Shellby is an SSH client for Mac, iPhone and iPad; your hosts, keys and jump hosts are already configured in it. From 2.0.0 the Mac build can run as an MCP server and expose those hosts to the AI coding tools on your desktop. A few hard boundaries are built in:
- The agent sees host names only. Tool arguments say which host; passwords, private keys and passphrases never appear in protocol messages. Shellby opens the connection with the credentials it holds.
- Read-only. Four tools today: list hosts, run read-only commands, read remote files, fetch metrics. Anything that changes system state is recognised by a local classifier and refused, with the reason sent back to the agent.
- Local only, while running only. The server listens on 127.0.0.1 and checks a token on every request; the token lives in the system Keychain. When Shellby quits the server stops — no background daemon.
- Traceable. Every call is one line in an audit log: who, when, which host, what command.
Setup: three steps, five minutes
- Enable the server. Shellby ▸ Settings ▸ External Agents, flip the switch. The status becomes “Running · port xxxxx” — the port is chosen once and then kept, so you will not have to touch the client config again.
- Copy the connection command. The same page shows complete commands for claude-code and codex; hit Copy.
- Run it in a terminal. Done.
The two clients authenticate differently, so the commands look like this (use the port and token from your own Settings page):
# claude-code
claude mcp add shellby --transport http http://127.0.0.1:52731/mcp \
--header "Authorization: Bearer <your token>"
# codex — only accepts an environment variable name, hence two lines
export SHELLBY_MCP_TOKEN=<your token>
codex mcp add shellby --url http://127.0.0.1:52731/mcp \
--bearer-token-env-var SHELLBY_MCP_TOKEN
For codex, the export line has to go into ~/.zshrc to persist — which puts the token in a plaintext file. That is how codex works; accept it, or export only when you need it.
Afterwards, run claude mcp list (or codex mcp list) and confirm shellby is listed and healthy.
In practice: chasing a 502
From here it is a normal conversation. Say you tell claude-code:
It will go roughly like this (each step is one MCP tool call, and each shows up in Shellby's audit log):
list_hosts— gets prod-web-01's identifier and notices its tags areappnginx.run_command:tail -n 50 /var/log/nginx/error.log— finds a run ofupstream prematurely closed connection while reading response header from upstream … 127.0.0.1:3000.get_metrics— load 3.9, memory at 91%, top CPU consumer isnode.read_file:/etc/nginx/conf.d/app.conf— confirms the upstream really points at port 3000 and the timeouts are defaults.
Then the verdict: the Node process is pegging the CPU, responses exceed nginx's upstream timeout, hence 502. At this point it may be tempted to “just” systemctl restart app — Shellby refuses that and tells it that commands which change system state are not allowed right now. It writes the recommendation down for you, and you decide whether to run it yourself in Shellby.
You never opened a terminal, and you never handed over a credential.
Details you only notice once you use it
Why does it sometimes get refused “unfairly”?
The classifier judges the whole line. cat a.log && systemctl status b is read-only, but grep x a.log > /tmp/out writes a file through redirection and is refused. The tool description Shellby gives the model already says “single, independently reviewable commands; don't chain unrelated steps with &&; don't rewrite a refused command to sneak past” — in practice the model rarely trips over this.
What if the output is huge?
A single call returns at most 64 KB; beyond that it is truncated and the model is told truncated: true, so it narrows the query itself (grep, tail). That is how big logs should be read anyway — not by cat-ing the whole thing into context.
Does it reuse my open sessions?
Yes, and preferentially. A machine you are connected to in Shellby answers the agent almost instantly; only otherwise is a new connection opened. Jump hosts and Mosh-compatible transport follow the host config, so the agent never needs to know about them.
What if the token leaks?
“Reset access token” on the Settings page — the old token dies immediately; re-add with the new command and you are done.
What it does not do
- No writes. Restarting services, editing config and writing files are not exposed. Mutating commands behind an approval card are planned, but only after the read-only version has proven itself.
- Not on iPhone / iPad. The consumers are desktop coding tools, and iOS background limits would suspend a local service at any moment.
- No background daemon. Keep Shellby open while you use it; that is a deliberate choice.
- No “raw ssh” tool that bypasses the classifier. Not even “for debugging” — if it existed, the agent would always prefer it and every guardrail would be decoration.
In short
Connecting an AI coding tool to your servers comes down to one question: how much power are you willing to hand to an automation that will sometimes be wrong? MCP lets that power be defined precisely, and Shellby defines it as “read-only, never sees credentials, always leaves a trace”. That already covers troubleshooting, reading logs and checking status — most of the everyday work — with almost no attack surface. The small remainder that needs hands on the keyboard stays yours.
Full parameters and limits are in the External Agents · MCP documentation.