
Ftp Ssh Mcp
io.github.Chris221由 GitHub 账号 Chris221 发布v1.0.3更新于 Oct 24, 2026
Work with a remote host over FTP/FTPS/SFTP, SSH, and MySQL.
概览
一个 MCP 服务器,让助手管理单台共享主机或 VPS:通过 FTP/FTPS/SFTP 传输文件、执行白名单内的 SSH 命令,并查询 MySQL 数据库。
- 可以做什么
- 通过 FTP、FTPS 或 SFTP 在远程主机上列出、上传、下载文件,创建目录和删除文件,操作限制在配置的基准目录内。通过 SSH 执行白名单内的 shell 命令,命令中的串联与重定向会被拒绝。通过 SSH 调用主机自带的 mysql 客户端执行 SQL 并读回表格输出。用自检命令确认配置并列出将注册的工具,但不建立连接。
- 适用场景
- 适合在共享主机或普通 VPS 上部署和维护网站时使用,让助手搬移文件、查看服务器状态,或执行简单命令和查询,而无需云厂商 API。默认只启用文件传输,shell 与数据库访问需要主动开启。
- 运行要求
- 以 npx 启动的本地进程,需要 Node 20 或更高版本。工作目录中的 .env 文件,或用 MCP_ENV_FILE 指定的绝对路径,内含 REMOTE_HOST、REMOTE_USER 等主机与用户设置,以及密码或 SSH 私钥。Windows 上客户端须通过 cmd /c 启动。需要能访问该主机的网络。
安装
在 SourceWeft 中
- 打开 控制台中的 Ftp Ssh Mcp,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
ftp-ssh-mcp
[npm version] [npm downloads] [CI] [node] [license]
An MCP server for working with a single remote host over FTP, FTPS, SFTP, and SSH — the kind of host a typical shared-hosting plan or a plain VPS gives you, not a cloud provider's API. It groups its tools into three capability classes: file transfer (list, upload, download, mkdir, delete), shell execution over SSH, and a MySQL query wrapper that also runs over SSH. File transfer is the only capability enabled by default — shell execution and database access are both opt-in and stay off until you explicitly turn them on. Treat those two as equally dangerous: mysql_query runs arbitrary SQL on a production database and can escape to a shell, so it is not the milder of the pair.
Install
Published to npm and run with npx — there is nothing to clone or build. Requires Node 20 or newer. Works on Windows, macOS and Linux.
1. Add it to your MCP client
On Windows, use the
cmd /cform shown for each client below. Most clients spawncommanddirectly rather than through a shell, andnpxon Windows is a.cmdshim that cannot be executed that way — the server simply fails to start, usually with no useful error. Treat this as the rule on Windows, not an edge case.
Claude Code — .mcp.json in the project root
Windows:
Claude Desktop — claude_desktop_config.json
Same mcpServers shape as Claude Code. Claude Desktop has no project directory, so set MCP_ENV_FILE to an absolute path — see step 2.
Cursor — .cursor/mcp.json (project) or ~/.cursor/mcp.json (global)
Same mcpServers shape as Claude Code.
VS Code — .vscode/mcp.json (workspace), or MCP: Open User Configuration for global
VS Code uses servers, not mcpServers:
Codex CLI — ~/.codex/config.toml, or .codex/config.toml in a trusted project
Codex uses TOML with a snake_case mcp_servers table:
Windows:
Or add it from the shell: codex mcp add remote -- npx -y ftp-ssh-mcp@1
2. Point it at a host
Create a .env file in your project root. The server reads it from the working directory, which is the project root for project-scoped clients:
That is enough for the six file tools over FTPS. Keep this file out of version control — add .env and .env.* to .gitignore. Getting credentials out of a tracked config file is the reason the server reads .env at all.
If your client has no project directory (Claude Desktop), or you want several servers against different accounts, point each at its own file instead:
A fuller setup, adding shell execution and database access — both off unless you opt in:
See .env.example for every variable and Configuration below for what each does.
3. Check it before you rely on it
Run it from the directory holding your .env. It resolves configuration, registers tools and prints a one-line summary — without opening a connection — then exits. Use it to confirm the right capabilities came up before wiring the server into a client:
It exits non-zero with a message naming the missing variable if configuration is incomplete. See Troubleshooting.
Options
--version and --help answer before any configuration is read, so they work on a machine with nothing set up yet. Both print to stdout; everything else the server says goes to stderr, because stdout is the JSON-RPC channel.
--quiet silences the warnings about a missing DB_PASSWORD, an MCP_CAPABILITIES entry that registered no tools, and a relative base directory. It deliberately does not silence the two that say your security posture is weaker than you might assume — an unverified host key, or path confinement switched off. A flag set once in a client's config file is never looked at again, so those stay visible until you fix them, which is the only silence worth having.
Capabilities
A capability's tools are only registered when it is both configured (its required variables are set) and, if MCP_CAPABILITIES restricts the set, named in that list.
The files tools are transport-neutral: they work over whichever transport FILE_TRANSPORT selects (ftp or sftp), and each call can override the transport for that one call if both profiles are configured. mysql_query is a convenience wrapper, not a database driver — it pipes SQL over SSH to the mysql client already installed on the host (mysql --user=... --database=... --table, with the SQL sent on stdin), then parses the CLI's table output. There is no connection pooling and no parameterised-query support. It exists because most shared hosts will not accept a database connection from outside the host itself; if you need a real client-side database integration, use one instead of mysql_query. Enable it with the same caution as ssh_exec, not less — see Security.
Configuration
Every setting is resolved from environment variables in this order: a profile-specific variable (FTP_* or SSH_*) first, then the shared REMOTE_* fallback, then a built-in default. Two independent profiles exist — FTP and SSH — and you may configure either, both, or neither in isolation (though at least one is required to start).
Variables are normally read from process environment, but the server also loads a .env file from the current working directory before resolving configuration, so credentials can live outside the MCP client's own (often tracked) config file. A real environment variable already set takes precedence over the .env file. Point at a different file with MCP_ENV_FILE — a leading ~ is expanded to your home directory, a relative value is resolved against the working directory, and a set-but-unreadable one fails startup rather than silently falling back to .env and loading different credentials than the ones you named. The .env parser does not support inline comments — KEY=value # note treats everything after = as the value, including # note, so keep comments on their own line.
Secret inheritance
Non-secret settings (HOST, USER, BASE_DIR) fall back to REMOTE_* freely. Secrets (PASSWORD, PRIVATE_KEY, PASSPHRASE) fall back to REMOTE_PASSWORD only when the profile does not set its own *_USER. A profile that names its own user is declaring a distinct identity, and sending that identity a password meant for a different account causes repeated authentication failures — on many shared hosts, enough failures trip brute-force protection and lock the account out at the host level. If FTP_USER (or SSH_USER) is set, its password must be set explicitly too.
Variables
Command presets
SSH_ALLOWED_CMDS entries starting with @ expand to a named group; presets and literal command names mix freely (SSH_ALLOWED_CMDS=@basic,@php,rsync). An unknown preset name fails startup. Presets are named after stacks, not hosting products, and expand as follows:
A DB_USER with no resolvable DB_PASSWORD is not treated as an error: the remote mysql client can legitimately get credentials from ~/.my.cnf or a trusted local socket instead of a password on the connection. The server prints a warning to stderr at startup rather than refusing to run, and if a query then fails to authenticate, mysql_query's error message names DB_PASSWORD again so the cause is visible from inside the MCP client, not just in a startup log you may not have seen.
Tilde base directories
~ and ~/<dir> are accepted and expanded to the account's own login directory: realpath(".") over SFTP, PWD over FTP, for the files tools. That lookup happens on connect, and only when there is a ~ to expand, so an absolute base directory costs nothing extra and the server still needs no network at startup.
ssh_exec and mysql_query resolve the same setting a different way: the raw ~ is handed to the remote login shell, which expands $HOME itself on every command, so there is no connect-time lookup on that path at all. The two mechanisms agree in practice, since they resolve against the same logged-in account — only when and how the expansion happens differ.
Each profile expands its own value against its own login directory. That is deliberate: on many shared hosts the FTP account is chrooted so that its ~ is /, while SSH sees the real /home/<user> — so a ~/public_html shared through REMOTE_BASE_DIR lands in the right place on both.
A named home, ~other/site, is refused at startup — but only when the files tools are registered; a config that excludes them (e.g. MCP_CAPABILITIES=ssh) starts fine and only fails later, the first time ssh_exec or mysql_query tries to cd there. Only the account that logged in can be located, so a named home would be sent to the server as a literal path. Use an absolute path for that case.
Security
-
ssh_execonly runs a program named inSSH_ALLOWED_CMDS, checked against the first whitespace-delimited token of the command — not a policy over arguments. -
Before that check, the command is rejected outright if it contains any of a fourteen-character shell metacharacter class (
; & |$(){}<>\and newline/carriage return), so a call can only ever be a single plain invocation — no chaining, redirection, or substitution. -
Remote paths passed to the
filestools and toSSH_BASE_DIR/FTP_BASE_DIRare resolved and confined to the profile's configured base directory;..segments are rejected outright. Confinement is per profile, and a per-calltransportoverride is confined to the profile it selects, not to the default one. A profile with no base directory at all is not confined — there is nothing to confine it to — so each profile falls back to the other's base directory when it has none of its own, and the server warns on stderr at startup for any transport that still ends up without one. -
Deletes (
REMOTE_ALLOW_DELETE) and shell execution (SSH_ALLOW_EXEC) are both off by default and must be turned on explicitly. -
Pin the SSH host key with
SSH_HOST_FINGERPRINT. Unless it is set, the SSH and SFTP connections accept whatever host key they are offered — there is noknown_hoststo fall back on — so anything on the network path can present its own key and be handed the account's password. When the variable is set, a mismatched key aborts the connection with an error naming both the expected and the received fingerprint; when it is unset, the server says so in a startup warning on stderr. Get the value from the host you trust with:and copy the
SHA256:…field (a bare hex digest is also accepted). A fingerprint that cannot be parsed is a startup error rather than a silent fallback to "no verification". -
mysql_querynever builds a shell string from your SQL — the query is written to themysqlclient's stdin, so it is never interpolated into a command line. -
DB_PASSWORDnever appears in the host's process list. It is delivered as the first line of the command's stdin, read intoMYSQL_PWDby the remote shell and exported to themysqlprocess — so no argv anywhere carries it, andps auxshows only the harmless command text. The secret exists only in process environments, which are owner-readable. One constraint follows from the framing: a password containing a line break is refused with an explicit error. WhenDB_PASSWORDis unset, no password mechanism runs at all, so host-side credentials such as~/.my.cnfgenuinely take over. -
mysql_queryis at least as powerful asssh_exec, not a lesser grant. It runs arbitrary SQL against a production database, and themysqlclient's\!command is a shell escape that is honoured on piped input — so\! curl … | shreaches a shell regardless ofSSH_ALLOW_EXECandSSH_ALLOWED_CMDS. The two capabilities are separate switches because they are separate features, not because one is safer; enablemysqlwith the same care asssh.
None of this makes the remote host a sandbox. An allowed command still runs with the full privileges of whichever account is configured, and there is no isolation between what ssh_exec can do and what that account could do logged in directly. The guards constrain the shape of a single call — one program, no shell tricks, paths that stay inside a base directory — they do not constrain what an allowed program itself is capable of once it runs.
Found a security problem? Please report it privately — see SECURITY.md.
Known advisories
npm install ftp-ssh-mcp currently reports two moderate advisories for
@hono/node-server, pulled in transitively by @modelcontextprotocol/sdk. They
concern path traversal in that package's static-file server on Windows. This
server talks JSON-RPC over stdio and never starts an HTTP listener, so that code
path is not reachable here, and the only "fix" npm offers is a breaking downgrade
of the SDK. CI runs npm audit --omit=dev --audit-level=high, which passes today
while still failing the build on anything high or critical. The advisories will
clear when the SDK updates its dependency.
Troubleshooting
Start with npx -y ftp-ssh-mcp@1 --selftest from the directory holding your .env. It reproduces everything the server does at startup except connecting, so it separates a configuration problem from a network or credentials one.
Development
npm test runs the Vitest suite, including capability-registration and transport tests against an in-process FTP server. npm run selftest resolves configuration from the environment (and .env, if present), registers tools, and prints a one-line summary — including the resolved transport and which capabilities registered — without opening a connection or starting the stdio transport.
来源:README.md,提交 6087f5a
工具
0版本历史
2- v1.0.3最新Sep 24, 2026
- v1.0.2Sep 16, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分