mcp-terminal
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-terminalopen a session in ~/Projects/demo, run npm run dev, and show me the output"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-terminal:给 Agent 用的本地终端 MCP Server(macOS)
mcp-terminal 让任何 MCP 客户端(Manus、Claude Desktop、Cursor、自研 Agent……)通过 stdio 在本机创建并控制终端会话:启动命令、按游标增量读取输出、向交互式程序写入、判断“在跑 / 等输入 / 已结束”,并在需要时中断或终止整棵进程树。所有会话默认运行在 macOS Seatbelt 沙箱里,沙箱可写目录可以由你指定。
它不是把单次 exec 包成一个 Tool,而是一个有状态的终端服务:会话里的 cd、export、虚拟环境会保留;长任务不会阻塞调用方;输出不会因为没及时读取而丢失;客户端断开或服务崩溃后,残留进程会被找回并清理。
特性一览
方面 | 做法 |
会话模型 | PTY(node-pty)为默认后端,另有 pipe 后端用于分离 stdout/stderr;每个会话有 |
命令边界与退出码 | 向 zsh / bash 注入 OSC 133 shell 集成标记(带每会话随机 nonce,程序伪造的标记会被忽略), |
增量输出 | 单调递增的字节游标;内存环形缓冲 + 磁盘分段溢写;省略游标即“从上次读到的地方继续”;超长输出头尾保留、中间给出可分页的游标 |
不阻塞的等待 | `until=["command_done" |
交互输入 |
|
全屏程序 | 基于 |
输出整理 | 默认去掉 ANSI 颜色与控制序列, |
沙箱 | 每个会话用 |
命令策略 | tree-sitter-bash 解析整条命令(管道、 |
审批 | 需审批的命令依次尝试:MCP elicitation(客户端支持时)→ macOS 原生对话框 → |
环境变量与脱敏 | 默认白名单继承环境变量, |
审计 | JSONL 审计日志,按实例做哈希链, |
生命周期 | 关闭会话时按 SIGHUP → SIGTERM → SIGKILL 升级,覆盖进程组、tty 上的作业和已脱离的 |
Related MCP server: shell-session-mcp
环境要求
macOS(已在 macOS 26.5 / Apple Silicon 上测试;所用 Seatbelt 规则在较早版本上同样可用,但未逐一验证),Node.js 22.13 及以上(需要内置的 node:sqlite)。不需要 Xcode:node-pty 自带 macOS 预编译二进制,bash 解析器使用 WASM。
安装与自检
git clone https://github.com/dogeheng/MCP-server-controller.git
cd MCP-server-controller
npm install # postinstall 会修复 node-pty spawn-helper 的执行权限
npm run build
node dist/bin/termctl.js doctordoctor 会检查 Node 版本、sandbox-exec、PTY 能否启动、解析器能否加载、当前生效的沙箱与审批配置。可以 npm link 把 mcp-terminal 和 termctl 两个命令放进 PATH,下文为简洁起见直接写命令名。
接入 MCP 客户端(stdio)
最简单的方式是让 termctl 生成配置片段,它会写入当前 Node 的绝对路径(GUI 应用启动时 PATH 往往不完整):
termctl client-config --workspace ~/Projects/demo输出形如:
{
"mcpServers": {
"terminal": {
"command": "/Users/you/.n/bin/node",
"args": ["/path/to/mcp-terminal/dist/bin/mcp-terminal.js", "--workspace", "/Users/you/Projects/demo"]
}
}
}Manus:在 Manus 的自定义 MCP(Custom MCP)里选择以 JSON / 命令方式添加,粘贴上面的 mcpServers 片段即可(stdio 形式为 command + args + 可选 env)。添加后先让 Agent 调一次 terminal_list 或 terminal_run {"command":"pwd"} 验证。已在 Manus Studio 桌面端实测:服务由本机的 manus-computer-operator 以 stdio 方式拉起,运行在这台 Mac 上;服务进程退出后,下一次工具调用会自动重新拉起。Manus 目前不声明 elicitation 能力,所以需要审批的命令会弹出 macOS 原生对话框,超时后退回到 termctl approve <id>。修改代码并 npm run build 后,在活动监视器里结束名为 node 且命令行含 dist/bin/mcp-terminal.js 的进程,Manus 下次调用时就会加载新版本。完整测试步骤见 TESTING.md。
Claude Desktop:把片段合并进 ~/Library/Application Support/Claude/claude_desktop_config.json。其他客户端:只要支持 stdio 传输,配置方式相同。
服务端同时支持 2025-era 的 initialize 握手和 2026-07-28 版协议(无初始化握手、MRTR 形式的 elicitation),由 SDK 在首个请求时自动选择。
设定沙箱目录
沙箱是“读宽写窄”:会话能读系统里的大部分文件(凭据目录除外),但只能写下面这些位置。
方式 | 作用 |
| 最常用。会话只能在 |
| 只设置允许的工作目录根,替换默认的 |
| 默认工作目录 |
| 额外可写目录,例如 |
| 额外不可读路径 |
`--network allow | localhost |
| 不使用 OS 沙箱(命令策略与审批仍生效) |
同样的设置都可以写进 ~/.config/mcp-terminal/config.toml,带注释的完整示例见 config.example.toml。termctl sandbox-profile --cwd <dir> 可以打印实际生成的 Seatbelt 配置。服务自身的状态目录(~/.local/state/mcp-terminal)和配置目录在沙箱内不可写、关键部分不可读,Agent 无法篡改审批记录、审计日志或给自己加规则。
在沙箱里,写 home 目录下的文件会得到 Operation not permitted,这是预期行为。zsh 的补全缓存和 oh-my-zsh 缓存已被重定向到可写的用户缓存目录;vim 的 .viminfo 等写不了的文件会提示错误但不影响编辑。
工具一览
所有工具都同时返回一段便于模型阅读的文本(首行是 [session · 状态 · 退出码] 摘要,末行是下一步建议)和对应的 structuredContent。出错时返回 isError: true,structuredContent.error 含 code、message 和 hint。
工具 | 用途 | 关键参数 |
| 一次性命令,等待结束并返回 stdout / stderr / 退出码;超过 |
|
| 创建持久会话:交互 shell(默认),或直接运行程序( |
|
| 在 shell 会话中执行一条命令,返回这条命令的输出、退出码和新的 cwd;超时返回 |
|
| 向会话写入文本和按键,返回随后产生的输出 |
|
| 按游标增量读取输出,可在服务端等待条件 |
|
| 会话状态与活动判断(idle / busy / awaiting_input / exited)、前台进程、未读字节数 |
|
| 列出会话 |
|
| 向前台作业或整个会话发信号 |
|
| 调整 PTY 尺寸 |
|
| 结束会话及其整棵进程树,可保留输出供之后读取 |
|
所有会执行命令或写入输入的工具都接受 approval_id,用于审批后的重试。
典型调用编排
一次性命令:
terminal_run {"command": "git status --short && npm test"}
→ [term_x · exited · exit 0 · 8.2s] …输出…多步工作(状态会保留):
terminal_create {"name": "main"}
terminal_exec {"session_id": "main", "command": "cd api && source .venv/bin/activate"}
terminal_exec {"session_id": "main", "command": "pytest -q"}长任务:先短等,再跟随输出直到结束,期间不会丢输出:
terminal_exec {"session_id": "main", "command": "npm run build", "wait_ms": 3000}
→ status: running
terminal_read {"session_id": "main", "until": ["command_done"], "wait_ms": 30000}开发服务器:放在独立会话里,等到就绪行出现:
terminal_create {"name": "dev", "program": "npm", "args": ["run", "dev"]}
terminal_read {"session_id": "dev", "until": ["pattern:Local:|listening on|ready in"], "wait_ms": 30000}交互程序:
terminal_exec {"session_id": "main", "command": "npm init"}
→ status: awaiting_input, "package name: (demo)"
terminal_send {"session_id": "main", "text": "my-pkg", "submit": true}
terminal_send {"session_id": "main", "keys": ["C-c"]} # 中断全屏程序:
terminal_exec {"session_id": "main", "command": "vim notes.txt"} # 自动返回屏幕快照
terminal_send {"session_id": "main", "text": "ihello", "keys": ["Escape"]}
terminal_send {"session_id": "main", "text": ":wq", "submit": true}安全模型
三道防线,彼此独立:OS 沙箱限制“能碰到什么”,命令策略限制“能直接说什么”,审批把高风险操作交给人决定。沙箱是硬边界;策略解析是尽力而为(无法覆盖脚本内部的行为),所以默认两者同时开启。
内置规则(termctl check "<命令>" 可查看任意命令的判定与原因):sudo/su/doas、格式化磁盘、dd of=/dev/…、关机、csrutil/nvram、rm -rf 关键路径(/、~、/System……)以及 fork bomb 为 deny;rm -rf <目录>、find -delete、git push --force、git reset --hard、git clean -f、curl … | sh、chmod -R、killall、npm publish、docker system prune、kubectl delete、launchctl load、读取钥匙串密码、osascript 以及无法静态判定的动态命令为 ask。用户规则可以把 ask 放宽为 allow,也可以新增 ask / deny;内置 deny 是底线,规则无法放宽。项目目录里的 .mcp-terminal.toml 只能让策略更严格。
审批流程(security.approval.mode,默认 auto):
客户端声明支持 elicitation 时,直接在客户端界面里弹出审批表单(允许一次 / 总是允许 / 拒绝)。
否则弹出 macOS 原生对话框,默认按钮是“拒绝”,超时(默认 60 秒)不算同意。
都不可用或超时时,返回
APPROVAL_REQUIRED和一个审批 ID,Agent 会请你在自己的终端里运行termctl approve apr_xxx,然后带approval_id重试。审批只对完全相同的参数有效、只能用一次、默认 5 分钟过期。
“总是允许”会把这条精确命令(限定在该目录)写入 ~/.config/mcp-terminal/rules.toml,可随时编辑或删除。
审计:每次工具调用、策略判定、审批、会话开始与结束都写入 ~/.local/state/mcp-terminal/audit/*.jsonl。termctl audit tail 查看最近记录,termctl audit verify 校验哈希链。
生命周期与恢复
会话在空闲超过 idle_ttl_s(默认 1 小时)后自动关闭;结束的会话输出保留 retain_after_exit_s(默认 30 分钟)供读取。全局内存超出预算时,最久没有活动的会话先缩减内存缓冲,被淘汰的输出仍可从磁盘溢写日志中读取。客户端关闭 stdin、发送 SIGTERM/SIGINT/SIGHUP,或父进程消失(被 launchd 接管)时,服务会终止全部会话后退出。
如果服务被强制杀死,PTY 会话中的普通进程会随 PTY 关闭收到 SIGHUP 自行退出;pipe 会话和 nohup 后台进程会被下一次启动的服务根据注册表中记录的进程组(校验进程启动时间,避免误杀 PID 复用的进程)找回并清理,会话状态标记为 lost。也可以随时手动运行 termctl cleanup。
termctl
termctl approvals 列出待审批请求
termctl approve <id> [--always] 批准(--always:记住这条命令)
termctl deny <id> 拒绝
termctl sessions [--all] 注册表中的会话
termctl cleanup 清理已失去服务端的会话进程
termctl check "<cmd>" [--cwd d] 查看一条命令的策略判定
termctl sandbox-profile [--cwd d] 打印生成的 Seatbelt 配置
termctl audit verify | tail [-n N]
termctl config 生效配置及其来源
termctl doctor 环境自检
termctl client-config [--workspace d] 生成 MCP 客户端配置片段测试
npm test # 构建后运行单元测试与集成测试
npx vitest run test/unit # 只跑单元测试集成测试会通过 stdio 启动真实服务,覆盖:退出码与 stderr 分离、shell 状态保持、等待输入检测与应答、长任务游标续读、正则就绪等待、C-c 中断、沙箱写入限制、工作目录根限制、策略拒绝与审批票据、密钥脱敏、关闭时清理整棵进程树(含 nohup)、以及 2026-07-28 协议下的 elicitation 审批。
已知限制
仅支持 macOS。sandbox-exec 已被苹果标记为弃用但在当前系统上仍可用(Codex CLI、Claude Code 的 macOS 沙箱同样基于它);若未来不可用,termctl doctor 会报告,可暂时用 --profile host 运行。沙箱内的网络只有开关和仅本机两档,没有按域名过滤。命令策略是静态分析,无法判断脚本文件内部会做什么,这部分由沙箱兜底。等待输入检测对没有提示符、也不阻塞在 tty 读的程序可能判断为 busy,此时可用 until=["idle:2000"]。登录 shell 环境在启动时解析一次(oh-my-zsh 等较慢的配置约 2 秒),第一次创建会话会等待它完成。
目录结构
src/
bin/ mcp-terminal(stdio 服务入口)、termctl(管理 CLI)
server.ts 运行时装配与 MCP Server 构建
mcp/ 工具注册与渲染、审批守卫(elicitation / 对话框 / 票据)
core/ 会话、管理器、等待器、输出视图、按键映射、环境变量
backends/ PTY 与 pipe 进程后端
detect/ shell 集成脚本生成、OSC 133/7 标记扫描
output/ 环形缓冲、磁盘溢写、文本规范化、屏幕模型、脱敏
policy/ 命令解析、策略引擎、审批存储、macOS 对话框
sandbox/ Seatbelt 配置生成
store/ SQLite 会话注册表、哈希链审计日志
config/ 配置结构与分层加载
test/ 单元测试与集成测试
examples/ 测试用的交互脚本(greet.py)
docs/ 技术选型报告与初步设计设计背景与各关键节点的选型对比见 docs/tech-selection-report.md,Manus 中的分步测试见 TESTING.md。
许可证
MIT © dogeheng
This server cannot be deployed
Maintenance
Related MCP Connectors
Termalin's hosted MCP server. Claude, ChatGPT or any MCP client runs commands, moves files over SFTP, queries databases and reads server metrics and monitoring alerts on your servers, keylessly through Termalin Connectors: no SSH key is handed over and no inbound port is opened. Per-host command policy and an audit log. Needs a termal.in Pro or Team account (new accounts start with a 14-day Pro trial).
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
On-demand cloud browser sessions; page actions need a compatible CDP client.
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to launch and interact with terminal/TUI programs, providing tools to inspect and control live CLI/TUI sessions.2 npm1Apache 2.0
- AlicenseNot gradedqualityDmaintenanceProvides persistent interactive shell sessions via pseudo-terminals for MCP agents, enabling bidirectional communication, incremental reads, and stateful command execution across steps.7 npmMIT
- FlicenseNot gradedqualityCmaintenanceEnables end-to-end testing of CLI applications, REPLs, and TUIs by driving interactive pseudo-terminal sessions and reading clean rendered output through any MCP client.-
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to interact with real terminal sessions across local and remote hosts, driving multi-turn interactive programs like TUIs, REPLs, and SSH prompts through MCP.57MIT