ChatGPT Remote SSH MCP
This server provides SSH fleet management: discover servers/groups, execute shell commands, and transfer files securely.
List configured SSH servers, optionally filtered by group, and list groups with descriptions and member counts.
Run a shell command on a single named SSH server with optional working directory, timeout, dry-run preview, and force bypass for dangerous-command checks.
Run a shell command in parallel across all servers in a group with concurrency limits, fail-fast, dry-run, and per-server results.
Upload files via SFTP from a restricted local transfer root to a remote path, with size and sensitive-path protections.
Download files via SFTP to a restricted local transfer root with no-clobber and sensitive-path protections.
Use dry runs to preview operations and rely on dangerous-command detection, output truncation, and timeout controls.
Uses a Cloudflare Tunnel (the bundled cloudflared binary) to publish the local OAuth gateway on port 8200 as a public HTTPS endpoint, giving ChatGPT an HTTPS /mcp entry point to the SSH MCP service.
Uses GitHub OAuth as the identity provider for the MCP gateway: users sign in with GitHub, and only GitHub user IDs in the configured allowlist are permitted to obtain tokens and reach the SSH MCP tools.
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., "@ChatGPT Remote SSH MCP run hostname, whoami, and uptime on gpu-local"
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.
ChatGPT MCP Bridge
让 ChatGPT 网页版通过 MCP 调用 Codex 原生开发引擎,在指定工作目录中阅读代码、修改实现、运行命令和执行测试。
Codex 额度不够用了?通过 ChatGPT 网页版 + MCP 继续部分开发工作
当 Codex 额度暂时用尽,而 ChatGPT 仍可使用所需的模型与 MCP 工具时,可以尝试通过本项目继续推进开发任务:
阅读与修改代码:了解项目结构、分析实现、调整代码。
运行命令与测试:执行脚本、定位错误、验证修改结果。
管理开发任务:查看日志和进程、检查 Git 状态、跟踪实验进展。
持续推进工作:使用原生任务规划、工具循环、历史压缩、目标管理和任务恢复。
ChatGPT 负责理解需求并生成回答或工具调用,Codex 原生引擎负责组织任务、执行工具和维护开发历史,Bridge 通过 MCP 在两者之间传递请求与结果。原生引擎由仓库内的 Codex App Server 和 core 源码编译。
将 Bridge 部署在需要操作的主机上,即可通过 MCP 访问该主机上的项目。Linux 服务器和 macOS Apple Silicon 本机部署已验证;Windows 原生部署尚不支持。
本项目不会增加或恢复 Codex 额度。实际可用能力受 ChatGPT 账号权限、使用限制和部署环境影响,也不等同于 Codex 产品的完整功能与性能。
Related MCP server: ssh-mcp-server
运行结构图
flowchart LR
W["ChatGPT 网页版"] <-->|"HTTPS / MCP"| G["认证网关"]
subgraph H["运行 Bridge 的主机"]
direction LR
G <--> M["MCP 工具"]
M <-->|"任务管理"| C["Codex 原生引擎"]
C <-->|"执行工具与沙箱"| P["项目文件 · 命令 · 测试"]
C -->|"模型请求"| R["模型桥接与持久队列"]
R -->|"读取请求 / 返回回答"| M
end主流程从左到右:网页端连接 MCP,Codex 在目标主机上执行开发任务。模型桥接监听本机回环地址,将 Codex 的模型请求交给网页端回答。
一次任务如何推进
网页端调用
start_agent创建任务,保存返回的thread_id。Codex 根据任务历史生成模型请求。
网页端调用
next_model_request读取请求;较长的上下文通过read_model_request分页读取。网页端调用
submit_model_response返回回答或原生工具调用。Codex 执行工具、收集输出、更新任务历史,并生成下一轮请求。
网页端继续处理请求,并通过
get_agent_status检查状态,直到任务完成、暂停或需要用户输入。
原生工具调用应返回给 Codex 执行,避免网页端额外执行一遍相同操作。
如何使用
连接已有服务
在 ChatGPT 中添加已部署的 MCP 连接,完成认证。
在对话中启用该连接,确认可以发现
start_agent、next_model_request等工具。描述任务和工作目录;后续在同一个任务中持续处理模型请求。
可以直接这样说:
使用 ChatGPT MCP Bridge,在 /path/to/project 中检查测试失败原因,先制定计划,再修改代码并运行相关测试。持续处理 Codex 发出的模型请求,直到完成或确实需要我提供信息。
需要持久目标时,可以说:
为这个任务创建 /goal,完成修复并验证;如果当前会话中断,下次继续同一个任务。
新部署:Linux / macOS
先准备 Python 3.11+、Rust 1.99.1 和 C 编译器。Linux 还需 pkg-config、libcap 和 OpenSSL 开发文件;macOS 需要 Xcode Command Line Tools。安装脚本按系统构建对应的原生组件:Linux 使用 Bubblewrap,macOS 使用 Codex 的 Seatbelt 沙箱。以下命令从仓库根目录执行:
Linux 沙箱还需要系统允许创建非特权用户与网络命名空间。启用 AppArmor 的 Ubuntu 环境可能需要为打包的 Bubblewrap 配置相应策略,参见 Ubuntu 的命名空间说明。可以先运行 unshare --user --map-root-user --net /bin/true 检查这项前置条件。
python3 -m venv .venv
export MCP_BRIDGE_ENV_PREFIX="$PWD/.venv"
export MCP_BRIDGE_WORKSPACE=/absolute/path/to/projects
bash deploy/install.sh
bash deploy/run-http.sh安装脚本编译仓库内的原生组件、安装 Python 包,并生成本机私有令牌。HTTP 服务默认监听 127.0.0.1:8000。
通过 deploy/run-http.sh 或 deploy/run-stdio.sh 启动时,默认开启 full access(MCP_BRIDGE_FULL_ACCESS=true)。此模式关闭操作系统沙箱,命令可使用运行 Bridge 的系统账号权限访问文件和网络;获准连接的客户端共享该账号权限。工作目录仍受 MCP_BRIDGE_WORKSPACE 限制,但这不等于文件访问边界。需要沙箱时,在启动前设置 export MCP_BRIDGE_FULL_ACCESS=false。直接运行 chatgpt-mcp-bridge 命令时,未设置该变量则默认使用沙箱。
如需分发安装包,在各自的目标系统和 CPU 架构上分别构建:先执行上述 deploy/install.sh,再运行 .venv/bin/python -m pip wheel --no-deps --no-build-isolation . -w dist。生成的 wheel 文件名包含平台和架构;macOS 和 Linux 的 wheel 不能互用,不同 CPU 架构也需要分别构建。构建 wheel 前需要在环境中安装 hatchling。本项目不会自动下载其他系统的预编译 Bridge 程序。
GitHub Actions 的验证工作流分别生成 bridge-linux-wheel 和 bridge-macos-wheel 构建产物;对应任务成功后,可从该次运行的 Artifacts 下载。下载前请检查 wheel 文件名中的操作系统和 CPU 架构是否匹配本机。
在另一个终端验证:
.venv/bin/python deploy/doctor-mcp.py --probe-execute --probe-agent诊断脚本会验证认证、17 个工具、执行功能和一次原生模型请求回传。模型回传探针使用预设测试回答,不代表 GPT 网页端自动持续运行已经得到保证。
如使用现有 Conda 环境,激活 chatgpt-mcp-bridge,并把 MCP_BRIDGE_ENV_PREFIX 指向该环境路径即可;不要重复创建环境。
接入 ChatGPT 网页端
将 MCP 服务通过带认证的 HTTPS 入口提供给 ChatGPT。仓库内 gateway/ 模块负责 OAuth 与代理,deploy/start-gateway.sh 读取私有 OAuth 配置;cloudflared/ 提供现有部署使用的隧道启动脚本。
网关部署需要 GitHub OAuth 应用、允许访问的账号 ID 和公开 HTTPS 基址。具体环境变量及读取位置见 网关启动脚本。凭据保存在服务器私有配置中。仓库附带的 cloudflared 仅适用于 Linux x86-64;macOS 和其他架构需安装对应平台的 cloudflared,或通过 CLOUDFLARED_BIN 指定其路径。
在账号支持的自定义 MCP / 开发者连接入口中添加 https://your-domain.example/mcp,完成认证,然后在对话中启用它。使用设置和可用入口以 OpenAI 官方连接说明 为准。
若客户端支持插件包,可生成引用已部署 HTTPS 服务的 ZIP:
python scripts/build_plugin.py \
--web-url https://your-domain.example/mcp \
--output dist/chatgpt-mcp-bridge-web.zipZIP 提供插件声明和任务工作流程说明,不包含服务器程序或认证凭据,也不会自动部署服务。网页端优先使用支持的自定义 MCP 连接入口。省略 --web-url 生成的是桌面 stdio 配置。
任务命令
在对话中表达需求,让 ChatGPT 调用 agent_command 并传入已有任务的 thread_id:
命令 | 行为 |
| 设置原生持久目标 |
| 查看目标 |
| 暂停、恢复或清除目标 |
| 压缩 Codex 任务历史,需要继续回答压缩请求 |
| 查看任务状态 |
| 中断当前轮次并暂停目标 |
| 恢复任务 |
| 从当前任务历史创建分支任务 |
这些是 MCP 工具参数中的命令,不会自动注册为 ChatGPT 输入框的斜杠命令。新对话恢复任务时,先调用 list_agent_tasks 查找任务,再使用同一个 thread_id 恢复。
工具与模块
工具组 | MCP 工具 |
任务流程 | start_agent、list_agent_tasks、get_agent_status、next_model_request、read_model_request、submit_model_response、agent_command、answer_agent_request |
执行和文件 | get_environment、execute、exec_command、remote_ssh、write_stdin、terminate_process、read_file、write_file、apply_patch |
开发任务优先通过任务流程工具推进;执行和文件工具适合明确的单步操作与诊断。
remote_ssh 使用 Bridge 主机的 SSH 配置连接远端,host 可填写 SSH 别名或 用户名@IP,port 可选。默认密钥认证;设置 authentication="password" 可在 SSH 提示后通过 write_stdin 输入密码,但传给模型或工具的密码可能留在会话和工具记录中,建议优先使用 SSH 密钥。两种方式均需预先在 Bridge 主机验证并保存服务器主机密钥。返回的 session_id 可用 write_stdin 发送命令和读取输出,用 terminate_process 断开。连接保持到主动断开、网络中断或服务重启。此工具要求启用网络,远端命令使用远端账号权限,不受本机沙箱限制。
路径 | 职责 |
| 原生进程、任务、目标与恢复 |
| 请求队列、Responses/SSE、分页和增量传输 |
| 直接执行与共享生命周期 |
| MCP 工具定义 |
| MCP 传输与认证校验 |
| OAuth 与代理 |
| 敏感信息处理 |
| Rust 入口、固定版本的原生源码与依赖 |
| 插件声明与网页端工作流程 |
| 构建部署、验证与文档 |
| 本机构建缓存、工具链和内部备份 |
| 日志与分发产物 |
配置
配置项 | 用途 |
| 允许操作的项目根目录 |
| Python 环境路径 |
| 持久状态目录,默认 |
| 执行环境网络权限,默认关闭 |
| 启动脚本默认 |
| MCP HTTP 监听端口,默认 8000 |
| MCP HTTP 私有令牌文件 |
部署脚本仍兼容旧变量名;应用层优先读取新的 MCP_BRIDGE_* 配置。当前部署面向单用户,获准访问的客户端共享服务器系统账号。
能力边界与验证
网页端需要继续读取并回答模型请求,Codex 才能推进。MCP 无法主动唤醒已结束或关闭的网页会话,不能保证无人值守无限循环。
原生任务历史与桥接记录保存在服务器;它们与 ChatGPT 网页会话历史分开。任务恢复仍需要一个能继续调用工具的会话。
/compact压缩 Codex 任务历史,不会压缩网页会话。桥接层按请求大小估算 token 用量,用于原生计量和压缩触发,不代表网页端实际用量或计费。
原生规划、执行、文件修改、测试、压缩、目标续跑、暂停恢复和沙箱限制已通过服务器测试。部署时通过 176 项测试,并验证 MCP 和网关的原生请求回传。
服务器测试与 GitHub Actions 是不同的验证环境;仓库自动检查状态以对应提交的 Actions 结果为准。
macOS 验证限于 Apple Silicon 本机执行和原生模型回传;尚未进行与独立 Codex 同模型条件下的性能对比。
Available Tools
6 toolsdownload_fileA
Download a file from a remote server via SFTP.
Args:
server: Server name (e.g. 'pro-dicentra').
remote_path: Path to the remote file — normally absolute, though a
relative path is not rejected, just resolved by the remote SFTP
server against the SSH login directory. Must be a regular file —
symlinks, devices, FIFOs, and other non-regular remote
files are refused. Rejected if it contains .. anywhere or
matches the same sensitive-path denylist upload_file enforces.
Size is NOT capped here (upload's 100 MiB limit has no download
counterpart) — an oversized transfer only logs a warning, after
the bytes are already on disk.
local_path: Destination path, RELATIVE to the configured
transfer_root (see [settings] in servers.toml, or the
SSH_MCP_TRANSFER_ROOT environment variable). Absolute
paths, .., . components and embedded NUL bytes are
rejected, as is a symlink at any component of the path.
Sub-directories are allowed,
but every intermediate directory must already exist under
transfer_root. NO-CLOBBER: if a file already exists at
the destination, the download fails rather than silently
overwriting it — remove or rename the existing file first.
A failed or cancelled download unlinks the partial file it
created, so a retry is not blocked by its own leftovers.
Returns: Confirmation message with file size.
Inputs: server (required), local_path (required), remote_path (required). Returns: result
executeA
Execute a shell command on a single SSH server.
Args:
server: Server name (e.g. 'web-prod-01'). Must match a configured server.
Use list_servers to see available servers.
command: Shell command to execute on the remote server (exactly as it
would be typed at a bash prompt). Rejected if it exceeds
max_command_bytes (default 65536 encoded UTF-8 bytes).
timeout: Command timeout in seconds. Default 30. Not range-checked,
and NOT authoritative: a timeout set on the server's entry
in servers.toml overrides this argument outright, so a
per-server 30 wins over a caller-supplied 600.
working_dir: Absolute remote directory to cd into before running the
command. Uses the server's default_dir from servers.toml
if omitted, or the SSH login directory if neither is set.
force: If True, bypass the dangerous-command detection patterns. Use
only for audited bulk operations. The block list is ~25 regexes
and is broader than "obviously destructive": besides rm -rf /,
mkfs, dd-to-disk, chmod 777 /, redirects into /dev/sd* and
/etc/{passwd,shadow,gshadow,sudoers}, find -delete / -exec rm,
shred / wipefs / blkdiscard / sgdisk on /dev/, partition-table
edits, and fork bombs, it also rejects ordinary interpreter
wrappers — bash -c ..., python3 -c ... (also perl/ruby
-c/-e), eval "...", and base64 -d | sh. Harmless
commands in those forms need force=True too. Default False.
dry_run: If True, do NOT connect or execute. Return a preview describing
what would run (server, command, working_dir, timeout, force).
Dangerous-command detection still runs so rejection can be
previewed. Useful for LLM plans that want to validate intent
before committing. Default False.
Returns:
Formatted command execution result with stdout, stderr, and exit code.
Long output is truncated at max_output_bytes (default 50 KiB) PER
STREAM — stdout and stderr get independent budgets, so the combined
worst case is 2x that setting. A truncated stream ends with
[... output truncated at N bytes], and hitting the cap TERMINATES
the remote process rather than letting it keep writing.
Inputs: server (required), command (required), force, dry_run, +2 more. Returns: result
execute_on_groupA
Execute a shell command on all servers in a group in parallel.
Concurrency is capped by the max_parallel_hosts setting (default 10;
configure in [settings] of servers.toml, range 1–100). The semaphore is
PROCESS-WIDE, not per call: concurrent execute_on_group calls share the same
slots and therefore serialise against each other for their share of them.
Args:
group: Group name (e.g. 'production', 'web'). Use list_groups to see
available groups.
command: Shell command to execute on every server in the group.
Rejected if it exceeds max_command_bytes (default 65536
encoded UTF-8 bytes).
timeout: Per-server command timeout in seconds. Default 30. Not
range-checked, and overridden per server by a timeout set
on that server's entry in servers.toml.
Each server has its own timer; slow servers do NOT extend the
per-server limit for others.
working_dir: Absolute remote directory to cd into on each server.
Uses each server's default_dir if omitted.
fail_fast: If True, cancel remaining tasks as soon as any server
returns a non-zero exit code or errors. Default False —
run all servers to completion and report each result.
force: If True, bypass the dangerous-command detection patterns. Use
only for audited bulk operations. The same broad block list
described under execute applies here — including plain
bash -c / python3 -c / eval wrappers. Default False.
dry_run: If True, do NOT connect or execute anywhere. Return a
per-server preview describing what would run. Dangerous-
command detection still applies. Useful for previewing
fleet-wide rollouts before committing. Default False.
Returns: Formatted summary showing per-server results, success/failure counts, and aggregate exit status.
Inputs: group (required), command (required), force, dry_run, +3 more. Returns: result
list_groupsB
List all server groups with descriptions and member counts.
Returns: Formatted table of groups with name, description, and server count.
Inputs: no named parameters. Returns: result
list_serversA
List all configured SSH servers with their groups and descriptions.
Args: group: Optional group name to filter by. Shows all servers if omitted. Use list_groups to see available group names.
Returns:
Formatted table of servers with name, groups, and description. An
unknown group is reported as a plain "Error: ..." string
rather than raised as a tool error, and a group with no members
returns "No servers found in group '<name>'".
Inputs: group. Returns: result
upload_fileA
Upload a file to a remote server via SFTP.
Files larger than 100 MiB are refused outright — use rsync or scp for those.
Args:
server: Server name (e.g. 'pro-dicentra').
local_path: Path to the local file, RELATIVE to the configured
transfer_root (see [settings] in servers.toml, or the
SSH_MCP_TRANSFER_ROOT environment variable). Absolute
paths, .., . components and embedded NUL bytes are
rejected, as is a symlink at ANY component of the path.
Sub-directories are allowed (e.g. 'reports/q1.csv'),
but every intermediate
directory must already exist under transfer_root —
upload_file does not create them. transfer_root itself must
be a real directory owned by the running user with mode 0700,
or every transfer fails.
remote_path: Destination path on the remote server — normally absolute,
though a relative path is not rejected, just resolved by the
remote SFTP server against the SSH login directory. Rejected if
it contains .. anywhere (even inside an otherwise legitimate
filename) or matches the sensitive-path denylist
(/etc/shadow, /etc/passwd, .ssh/*,
.aws/credentials, .kube/config, .netrc, …). An
existing REGULAR file at the destination is silently
OVERWRITTEN — unlike download_file, upload does not no-clobber;
an existing non-regular target (symlink, device, FIFO) is
refused.
Returns: Confirmation message with file size.
Inputs: server (required), local_path (required), remote_path (required). Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 6 tools
Each tool targets a clearly distinct purpose: group discovery, server discovery, single-host command execution, fleet command execution, upload, and download. There is no meaningful overlap between listing, executing, and transferring operations.
All names are snake_case and follow a readable verb pattern. The main minor deviation is that 'execute' is not verb_noun while 'execute_on_group', 'upload_file', and 'download_file' are more explicitly shaped.
Six tools are well-scoped for an SSH MCP server. Each tool earns its place without redundant coverage or obvious filler.
The surface covers discovery, single and fleet command execution, and both directions of file transfer. Minor gaps remain around remote file management, such as deleting, listing, or creating remote directories.
Maintenance
Related MCP Connectors
Manage servers through SSH.
Scoped, audited SSH exec, sessions, and SFTP on your saved servers without exposing credentials
Manage remote servers, SSH terminals, files, databases and proxies with scoped access and approvals.
Work on your own computers from Claude or ChatGPT: run commands, edit and search files.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to securely connect to and manage remote servers via SSH, supporting command execution, file transfers via SFTP, and multi-server management with both password and SSH key authentication.962 npm2MIT
- AlicenseAqualityDmaintenanceEnables AI assistants to securely execute remote SSH commands, perform file transfers, and monitor system status through a standardized interface. It features robust security controls including command whitelisting, blacklisting, and credential isolation to prevent unauthorized operations.1014 npmMIT
- AlicenseAqualityBmaintenanceEnables AI assistants to execute shell commands and transfer files via SFTP across remote servers using existing SSH configurations. It supports parallel execution on server groups and provides built-in safety warnings for potentially destructive commands.680 PyPI6Mozilla Public 2.0
- AlicenseAqualityDmaintenanceEnables LLMs to interact with remote servers via SSH, supporting command execution, file upload/download, and directory listing.7MIT