ftp-deploy-mcp
ftp-deploy-mcp
面向 AI 编码代理的部署按钮。 Claude Code · Claude Desktop · Cursor · Windsurf · Trae · Antigravity → 你自己的 FTP / FTPS / SFTP 服务器。
法语版 → README.fr.md
你的代理运行部署,你只需提出要求。
为什么
每个 Web 项目最终都会以同样的方式结束:“现在把它放到服务器上。”
AI 代理能写出很棒的代码,但大多数无法安全地部署到传统托管环境——OVH、Ionos、Hostinger、o2switch 这类共享主机仍然依赖 FTP/SFTP,而不是
git push。ftp-deploy-mcp给任何 MCP 客户端提供了一条通向你自己服务器的部署路径,发生在编写代码的同一段对话中。与通用型 SSH 执行 MCP 服务端不同,这个专为文件部署而设计:路径隔离、只读模式、试运行,以及永不进入模型上下文的凭据。
Related MCP server: SSH MCP Server
功能
功能 | 说明 |
多服务器 | FTP / FTPS / SFTP,一个配置可管任意数量的服务器 |
一键部署 | 递归目录部署,类似 gitignore 的排除规则,可出现在任意层级,试运行 |
路径隔离 | 所有操作都限制在每个服务器 |
只读模式 | 在必须保持不变的服务器上禁用所有写入操作 |
FileZilla 导入 | 一条命令把现有的 |
自动配置 | 自动配置 5+ 种 MCP 客户端,所有配置备份都带时间戳 |
诊断 | 对 Node、配置、和客户端连接进行只读诊断 |
零构建 | 纯 ESM JavaScript —— 约 12 个依赖,无原生编译 |
默认安全 | 默认拒绝明文 FTP / 不受信任的 TLS,除非显式允许 |
久经考验 | 209 个端到端断言,覆盖真实本地 FTP 和 SFTP 服务器 |
无遥测 | 除了调用你的服务器,没有任何数据被发送出本机 |
快速开始
git clone https://github.com/alebgl77/ftp-deploy-mcp.git && cd ftp-deploy-mcp运行
install(双击,Windows)或./install.sh(macOS / Linux)。重启 IDE 后对代理说:“把
dist部署到生产环境。”
工作原理
flowchart LR
subgraph agents [AI agents]
A[Claude Code]; B[Cursor]; C[Windsurf]; D[Trae]; E[Antigravity]
end
agents -- MCP stdio --> S[ftp-deploy-mcp<br/>10 tools · path jail · read-only guard]
S -- FTP / FTPS --> F[(your web hosts)]
S -- SFTP --> G[(your servers)]
K[ftp-servers.json<br/>credentials stay local] -.-> S1. 这是什么
一个 MCP(Model Context Protocol)服务器,通过 stdio 运行并提供 10 个工具供你的编码代理调用。凭据只会保存在本地配置文件中,永远不会进入 LLM 上下文。每次远程操作都被限制在每个服务器 root 下。
要求 Node.js >= 18,没有需要编译的原生依赖。
2. 安装
⚡ 一键安装(推荐)
git clone https://github.com/alebgl77/ftp-deploy-mcp.git
cd ftp-deploy-mcp然后打开向导:
Windows: 双击
install.cmdmacOS / Linux:
./install.sh(必要时先执行chmod +x install.sh)手动:
npm install && npm run setup
setup 向导会自动完成:
创建 或 导入 你的服务器列表 (包括用 FileZilla 导入 现有站点)
测试 到每个服务器的连接
配置 已检测到的 MCP 客户端 (Claude Code、Claude Desktop、Cursor、Windsurf、Antigravity) — 在修改任何文件之前都会生成
.backup-<时间戳>备份并打印 可直接粘贴的配置块 给 Trae, Trae 是在它的界面上配置的
一切就绪后,重启 IDE,然后就可以让你的智能体部署了。
诊断和命令
任何时候,一个 只读 检测 (不写任何东西):
npm run doctor # or: node src/index.js doctor它会输出 Node 版本、使用配置文件的路径、服务器列表 (绝不 是密码),以及每个客户端是否连接到了这个安装。
setup 选项 ( node src/index.js setup [options] ):
参数 | 效果 |
| 非交互模式 (保留现有配置,或与 |
| 要配置的客户端 (默认:所有检测到) 。 |
| 从 FileZilla 导入 (路径可选 → 位于其默认位置) 。 |
| 配置文件的目标 (例如 |
| 跳过连接测试。 |
| 输出将要执行的动作,不执行 任何 插件。 |
| 替换已存在但不同的 |
(b) 全局安装
npm install -g .然后 将 ftp-deploy-mcp 放入你的 PATH,以后可以直接使用命令,而不再使用 node .../src/index.js。
(c) 发布到 npm (用于 npx -y)
如果你把这个包以 你自己的名字 发布到 npm,客户端可以运行 npx -y 而不需要提前安装:
{ "command": "npx", "args": ["-y", "your-package-name"] }3. 服务器配置
保存配置文件 ftp-servers.json。服务器按此顺序搜索:
--config <path>(启动参数)FTP_SERVERS_CONF环境变量 (JSON 路径)./ftp-servers.json(可以运行加载的命令行所在目录)~/.ftp-mcp/servers.json
完整结构 (schema)
{
"defaultServer": "prod", // optional: used when "server" is not given
"servers": {
"prod": {
"protocol": "sftp", // REQUIRED: "ftp" | "ftps" | "sftp"
"host": "ssh.example.com", // REQUIRED
"port": 22, // optional (defaults: ftp/ftps 21, implicit ftps 990, sftp 22)
"user": "deploy", // REQUIRED
"password": "${ENV:PROD_PW}", // optional: password (or an env placeholder)
"privateKeyPath": "~/.ssh/id_ed25519", // optional (sftp); "~" is expanded
"passphrase": "…", // optional: private-key passphrase
"root": "/var/www/site", // optional (default "/"): ALL ops are jailed under it
"readOnly": false, // optional: blocks upload/deploy/mkdir/rename/delete
"insecureTLS": false, // optional (ftps): skip certificate checks — requires "allowInsecure"
"implicitTLS": false, // optional (ftps): implicit TLS (port 990, legacy servers)
"allowInsecure": false // optional: explicit opt-in REQUIRED for plain "ftp" or "insecureTLS"
}
}
}上面的配置用
//注释只是用于说明。 真正的 文件必须是 严格的 JSON 格式 (不允许注释)。请参考ftp-servers.example.json。
环境变量的替换
任何字符串值都可以包含 ${ENV:变量名}。它在启动时会被对应的环境变量替换。如果该变量未设置,工具会返回一个明确错误并指出缺少哪个变量。
例子:
假如你有
FTP_PASSWORD=...
"password": "${ENV:OVH_FTP_PASSWORD}"安全提示
优先使用 SFTP。 普通
ftp和ftps+insecureTLS: true是 默认拒绝 的,因为在这些传输中,网络攻击者可以读取或篡改凭据和文件。若要使用,必须在对应的服务器上显式设置"allowInsecure": true—— 并且从启动后和每次工具调用都会显示安全警告。把
ftp-servers.json加入.gitignore(仓库中已添加)。限制文件的权限 (
chmod 600 ftp-servers.json在 Unix 上)。尽量使用 环境变量 (
ENV:...) 或 SSH 加密 而不是明文密码。对任何不允许代理写的数据源,请设置
"readOnly": true。让
root尽可能窄:能防止任何../越界。
4. 导入 FileZilla
如果你已有 FileZilla 中的站点,可直接导入:
# Auto-detect the default sitemanager.xml location…
node src/index.js import-filezilla
# …or an explicit file, written to an ftp-servers.json
node src/index.js import-filezilla --file /path/sitemanager.xml --out ./ftp-servers.json如果不传 --out,那么 JSON 会打印到标准输出。Base64 编码的密码会被解码;没有保存密码的站点会得到 ${ENV:<名称>_PASSWORD} 占位符 (由你设置环境变量) 。输出示例:
{
"defaultServer": "my-site",
"servers": {
"my-site": {
"protocol": "ftp",
"host": "ftp.example.com",
"user": "deploy",
"password": "…",
"root": "/www/html"
}
}
}注意:生成的文件会包含解码后的明文密码 ——— 请放在
.gitignore中排除,以及chmod 600限制权限。
明文 FTP 站点:用
"protocol": "ftp"导入的站点(如上例)连接时会被拒绝,除非你将它们改为sftp或ftps,或显式设置"allowInsecure": true,否则每个站点都会打印警告。参见 技术说明 。
5. 手动配置客户端 (如果不用 setup)
npm run setup会自动写入这些文件 (带有备份)。如果你希望手动配置,可以参考这一段。
把 /absolute/path/to/ftp-deploy-mcp/src/index.js 替换成实际路径。如果已经发布到 npm,可以把 args 换成 "npx", "-y", "your-package",而不是 node。
下面列出的配置文件默认位置仅是当前文档撰写时的位置;请参考各开发工具的官方文档来获得最新路径。
Claude Code
项目根目录的 .mcp.json:
{
"mcpServers": {
"ftp": {
"command": "node",
"args": ["/absolute/path/to/ftp-deploy-mcp/src/index.js"]
}
}
}或一次性命令:
claude mcp add ftp -- node /absolute/path/to/ftp-deploy-mcp/src/index.jsClaude Desktop
Windows:
%APPDATA%\Claude\claude_desktop_config.jsonmacOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
在服务添加字段:
{
"mcpServers": {
"ftp": {
"command": "node",
"args": ["/absolute/path/to/ftp-deploy-mcp/src/index.js"]
}
}
}Cursor
~/.cursor/mcp.json (全局) 或 .cursor/mcp.json (项目):
{
"mcpServers": {
"ftp": {
"command": "node",
"args": ["/absolute/path/to/ftp-deploy-mcp/src/index.js"]
}
}
}Windsurf
~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"ftp": {
"command": "node",
"args": ["/absolute/path/to/ftp-deploy-mcp/src/index.js"]
}
}
}Trae
Trae 没有便捷的配置文件 —— 全部在 UI 中完成。在 Chat 里的服务器配置,选择 Add → Manual Config,然后粘贴 (就是 setup 命令输出的内容):
{
"mcpServers": {
"ftp": {
"command": "node",
"args": ["/absolute/path/to/ftp-deploy-mcp/src/index.js"]
}
}
}Antigravity
根据版本不同,文件可能是:
~/.gemini/antigravity/mcp_config.json或者是
~/.gemini/config/mcp_config.json
{
"mcpServers": {
"ftp": {
"command": "node",
"args": ["/absolute/path/to/ftp-deploy-mcp/src/index.js"]
}
}
}也可以使用代理的 MCP 面板 (MCP server management) → 添加服务器使用同样的格式。
6. 10 个工具
所有远程路径 (path、remote_path 等) 都是 相对 root 的,并使用 POSIX 格式。server 参数总是可选的 (解析规则见下文)。
工具 | 参数 | 描述 |
| (无) | 列出已配置的服务器(协议、主机、端口、根目录、只读、认证类型)。绝不返回密码。 |
|
| 连接,列出根目录,确认成功。 |
|
| 列出远程目录(目录优先)。 |
|
| 读取文本文件(默认 262144,最大 1048576 字节)。拒绝二进制文件。 |
|
| 上传单个文件,并创建父目录。 |
|
| 通过单个连接递归部署目录,带默认排除项。 |
|
| 下载文件;除非 |
|
| 创建目录(递归)。 |
|
| 重命名或移动。 |
|
| 删除文件;目录需要 |
服务器解析:显式 server 参数 → defaultServer → 如果只有一个服务器则使用该服务器 → 否则返回错误并列出可用名称。
ftp_deploy 默认排除项:**/node_modules/**、**/.git/**、.env、.env.*、*.log、.DS_Store、Thumbs.db、ftp-servers.json、**/.ftp-mcp/**(你的 exclude glob 模式会被追加;include 将限制为匹配的文件)。不带斜杠的模式可匹配任意深度(类似 gitignore):嵌套的 apps/api/.env 也会被排除。
7. 示例提示词
"将
./dist部署到prod服务器。""列出
ovh上/www中的内容。""从
prod获取.htaccess并展示给我。""对将
./build部署到/www做一次试运行,这样我就能看到会发送什么。""在
prod上将index.old.html重命名为index.html。"
8. 安全
默认使用安全传输:明文 FTP 以及禁用证书验证的 FTPS(
insecureTLS: true)会被拒绝,除非服务器条目显式设置"allowInsecure": true。允许时,会在启动时、ftp_list_servers中、doctor中显示安全警告,并附加到该服务器的每个工具结果中。根目录隔离:每个操作都会先规范化,然后验证其保持在服务器
root之下。任何逃逸尝试(../…)都会被拒绝,即使root是/。只读:
readOnly: true会阻止所有写操作(上传、部署、mkdir、重命名、删除);读取仍然可用。凭据不进入 LLM:密码、口令和密钥绝不会出现在工具输出中。
无遥测,除连接你自己的服务器外,无任何出站连接。
每次调用独立连接:每个工具都会打开连接、执行操作并关闭它——没有持久会话。
9. 故障排查
超时 / 无法连接(FTP):通常是被动模式被防火墙阻止。请确保服务器的被动端口可达。
SFTP 密钥认证:设置
privateKeyPath(~会被展开),如果密钥已加密,还需设置passphrase。检查密钥的权限。"INSECURE CONNECTION REFUSED":服务器使用明文 FTP,或使用禁用证书验证的 FTPS。请将其切换为
sftp(或使用有效证书的ftps),或者——只有当你完全接受被拦截的风险时——在该服务器上设置"allowInsecure": true。自签名 FTPS:
insecureTLS: true接受未经验证的证书。这会禁用中间人保护,因此还需要"allowInsecure": true,并且每次调用都会打印安全警告。建议安装有效证书。隐式 FTPS(端口 990):对于从第一个字节就开始加密、不使用
AUTH TLS命令的旧版服务器,请设置implicitTLS: true(ftps协议)。"no server configured":在 4 个位置均未找到该文件。请创建
ftp-servers.json,或传入--config <path>/FTP_MCP_CONFIG=<path>。setup后客户端看不到工具:完全重启 IDE(关闭所有窗口,而不仅仅是项目),然后用npm run doctor验证配置。配置无效时服务器仍会启动:这是有意为之(MCP 客户端不喜欢在启动时就崩溃的服务器)。确切的错误会在启动时打印到
stderr,并在每次工具调用时返回。
开发
npm test # runs the full smoke test (local FTP + SFTP, no external network)
node src/index.js --version
node src/index.js --help贡献
欢迎贡献——请参阅 CONTRIBUTING.md 了解开发环境搭建、 项目原则和 PR 清单。
安全
发现漏洞?请不要公开提交 issue——请参阅 SECURITY.md 了解如何私下报告。
许可证
MIT——请参阅 LICENSE。
Available Tools
10 toolsftp_deleteDelete a remote file or directoryADestructiveIdempotent
Delete a remote file, or a directory when recursive:true. Never deletes the server root.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Remote path to delete, relative to the server root. | |
| server | No | Name of the configured server. Defaults to the configured default, or the sole server. | |
| recursive | No | Required to delete a directory and its contents. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds valuable behavioral detail beyond annotations: recursive deletion applies to directories, and the server root is protected. The annotations already signal destructive/idempotent behavior, so the description's extra guardrail and recursive qualification are strong contributions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with the core action front-loaded and the safety constraint immediately after. No filler or repeated schema boilerplate.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description gives the core behavior, the recursive condition, and a critical root-safety guard. It does not address errors, permissions, or edge cases like nonexistent paths, but for a simple destructive operation with strong annotations and schema coverage, it is reasonably complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is high (all parameters documented). The description adds no new parameter semantics beyond restating that recursive deletion applies to directories, so the schema carries the explanatory burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Delete'), identifies the target ('remote file' or 'directory'), and includes the recursive condition for directories. This clearly distinguishes it from sibling operations like ftp_read, ftp_upload, and ftp_list.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly states when the tool applies (files and directories with recursive=true) and adds a meaningful safety exclusion ('Never deletes the server root'). It does not directly compare with sibling tools, but no sibling covers deletion, so the usage context is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ftp_deployDeploy a local directoryADestructive
Recursively upload a local directory to the server over a single connection, applying default and custom exclude globs (and optional include globs). Supports dry_run.
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | Name of the configured server. Defaults to the configured default, or the sole server. | |
| dry_run | No | If true, list what would be uploaded without connecting. | |
| exclude | No | Extra glob patterns to exclude, added to the built-in defaults. | |
| include | No | Glob patterns; when given, a file must match at least one to be uploaded. | |
| local_dir | Yes | Local directory to deploy, absolute or relative to the server's configured localRoot. | |
| remote_dir | No | Destination remote directory, relative to the server root. Defaults to the root. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already signal destructiveHint=true and readOnlyHint=false, so the description does not need to prove the operation is a write. It adds useful context about single-connection behavior and dry-run capability, but it does not disclose what the destructive behavior actually affects, such as whether existing remote files are overwritten or whether files absent locally are deleted.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences lead with the action and resource, then pack the glob behavior and dry-run support without wasted words. The description is easy to scan and every clause contributes information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a complete input schema, an output schema, and annotations covering the destructive nature, the description is largely sufficient. The only notable gap is the lack of explicit routing guidance against ftp_upload, but this does not prevent a capable agent from invoking the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the input schema fully documents all six parameters and the description is not required to compensate. The description reinforces exclude/include semantics and dry_run, but adds little meaning beyond what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('upload'), a specific resource ('local directory'), and the key behavioral qualifiers: recursive traversal, glob-based exclusion/inclusion, single connection, and dry-run support. This separates it from the sibling ftp_upload tool, which appears to target individual files, even though that alternative is not named.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description makes the intended use clear: deploy a whole local directory recursively rather than a single file. However, it never explicitly says when to prefer this over ftp_upload or other siblings, nor does it state exclusions such as 'for individual files, use ftp_upload'. The usage context is implied by the wording, but not spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ftp_downloadDownload a remote fileADestructive
Download a remote file to a local path, auto-creating local parent directories. Refuses to overwrite unless overwrite:true.
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | Name of the configured server. Defaults to the configured default, or the sole server. | |
| overwrite | No | Allow overwriting an existing local file. | |
| local_path | Yes | Local destination path, absolute or relative to the server's configured localRoot. | |
| remote_path | Yes | Remote file path, relative to the server root. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses two meaningful behaviors beyond the annotations: it auto-creates local parent directories assuredly, and it refuses to overwrite existing files unless overwrite:true is set. This materially helps the agent anticipate side effects beyond the raw destructiveHint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise, front-loaded sentences cover the primary action and both edge-case behaviors. No redundancy with the title or the input schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple download tool with a rich input schema and output schema, this is mostly complete. It could mention what happens on remote-file absence or connection failures, but those are minor gaps given the structured context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers all parameter descriptions (100%), giving a baseline of 3. The description adds relevant semantics by clarifying that local parent directories will be created and that overwrite is disabled by default, which supplements the parameter meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('download'), the source ('remote file'), the destination ('local path'), and two behavioral constraints (auto-creating parent directories, refusing to overwrite unless asked). This clearly identifies the tool's job and distinguishes it from siblings like ftp_read or ftp_upload.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'download a remote file to a local path' gives clear operational context for when this tool would be used. However, it does not explicitly contrast it with alternatives such as ftp_read (view without saving) or mention any selection criteria, so the guidance is mostly implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ftp_listList a remote directoryARead-onlyIdempotent
List the contents of a remote directory (relative to the server root). Directories are listed first.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Remote directory, relative to the server root. Defaults to the root. | |
| limit | No | Maximum entries to return (default 50, maximum 200). | |
| offset | No | Zero-based entry offset (default 0). | |
| server | No | Name of the configured server. Defaults to the configured default, or the sole server. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description adds two behavioral facts not in the annotations: directories are listed first, and paths are relative to the server root. This adds value beyond structured metadata without contradicting it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler. The primary action and the most important scoping constraint are front-loaded, and the behavioral note about directory ordering is a single clause. Nothing extraneous.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simple listing tool, annotations covering safety, schema covering all params, and an output schema present, the description is nearly complete. It could mention recursion behavior or sorting, but those are not essential for correct invocation. The provided details are sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all parameters are described. The description adds context for the 'path' parameter (relative to server root) but does not elaborate on limit/offset or server defaults. Since the schema handles the heavy lifting, this is a baseline 3 with a minor enhancement.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('List') and resource ('remote directory'), and adds a precise scoping detail ('relative to the server root'). This clearly differentiates from siblings like ftp_read (file content) and ftp_list_servers (server configuration) based on the action and target.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage—this is the tool for listing directory contents—but does not explicitly mention when to prefer it over ftp_read or other siblings, nor does it state exclusions. The distinction is inferable from the name and title, but no explicit guidance is given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ftp_list_serversList configured serversARead-onlyIdempotent
List all configured FTP/FTPS/SFTP servers (name, protocol, host, port, root, read-only, auth kind) and which is default. Never reveals passwords or keys.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already convey readOnlyHint and idempotentHint, but the description adds a meaningful behavioral guarantee: 'Never reveals passwords or keys.' It also specifies the exact set of fields returned (name, protocol, host, port, root, read-only, auth kind, default), which is beyond what annotations provide. No contradiction exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, well-structured sentence front-loads the action and resource, then lists specifics and a security guarantee. No filler or redundant text. Every clause adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the presence of an output schema, the description does not need to elaborate return values. It covers the purpose, lists output fields, and flags a security invariant. For a parameterless configuration-listing tool, nothing critical is absent. The only minor gap is explicit usage guidance, but that is covered under dimension 2.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Since the tool has zero parameters, the description cannot add parameter semantics. Per calibration, baseline for 0 params is 4. The description appropriately confirms no inputs are needed, and the schema is empty, so nothing is missing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'list' and the resource 'configured FTP/FTPS/SFTP servers', enumerates the exact fields returned, and notes the default flag. It is unambiguous and distinguishes itself from sibling ftp_list (which likely lists files) by naming the resource type. The security note about not revealing passwords also clarifies its non-sensitive nature.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage (when you need to know configured servers) but provides no explicit guidance on when to choose it over alternatives like ftp_list or ftp_test. It does not state exclusions or prerequisites, relying on the tool name and purpose. A sentence like 'Use this to discover server names before invoking other ftp_* tools' would improve clarity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ftp_mkdirCreate a remote directoryAIdempotent
Recursively create a remote directory (relative to the server root).
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Remote directory to create, relative to the server root. | |
| server | No | Name of the configured server. Defaults to the configured default, or the sole server. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnlyHint=false, idempotentHint=true, and destructiveHint=false. The description adds meaningful behavioral context beyond annotations by stating the operation is recursive and relative to the server root. No contradiction with annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no filler, front-loading the core action and the recursive behavior. Every word contributes to the tool's meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter tool with full schema descriptions, annotations for safety and idempotence, and an output schema present, the description covers the essential behavioral nuance (recursion) not already in the structured data. Nothing critical appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with both path and server already described in the input schema. The description's mention of 'relative to the server root' repeats the schema's path description rather than adding new meaning, so the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('create'), a concrete resource ('remote directory'), and a key qualifier ('recursively') plus scope ('relative to the server root'). It clearly distinguishes this directory-creation tool from file-oriented siblings like ftp_upload and from operations like ftp_list or ftp_delete.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit guidance about when to use this tool over alternatives such as ftp_upload or ftp_deploy, nor any mention of prerequisites, exclusions, or conditions. The only implied context is that it creates directories, leaving the agent to infer when this is the correct choice.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ftp_readRead a remote text fileARead-onlyIdempotent
Read a remote text file and return its content. Binary files are refused (use ftp_download instead).
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Remote file path, relative to the server root. | |
| server | No | Name of the configured server. Defaults to the configured default, or the sole server. | |
| max_bytes | No | Maximum bytes to read (default 262144, hard max 1048576). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint false, so the safety profile is covered. The description adds the important behavioral detail that binary files are refused, which is beyond the annotations and helps the agent anticipate failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence plus a parenthetical alternative. The core purpose is front-loaded, and every word earns its place—no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read tool with no output schema, the description covers core behavior and the binary refusal. It doesn't address error cases (e.g., file not found) but that's minor given the tool's simplicity and strong annotation coverage. Slightly more detail about failure behavior would push it to 5, but it's already solid.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
All three parameters (path, server, max_bytes) have descriptions in the schema, so the schema carries heavy weight. The tool description adds no new information about parameter meaning, but the high schema coverage makes this acceptable. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('read') and resource ('remote text file') and explicitly distinguishes itself from ftp_download by refusing binary files. An agent can immediately tell this is the text-reading tool versus the binary-download sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternative (ftp_download) and the condition that triggers it (binary files are refused). This gives clear direction on when to use this tool versus the sibling, leaving no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ftp_renameRename or move a remote entryADestructive
Rename or move a remote file or directory (both paths relative to the server root).
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | Name of the configured server. Defaults to the configured default, or the sole server. | |
| to_path | Yes | New remote path, relative to the server root. | |
| from_path | Yes | Existing remote path, relative to the server root. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the description doesn't need to restate that. The description adds the path-relative-to-root context, but does not disclose what happens on failure, whether directories can be moved recursively, or whether overwriting is possible. With annotations covering the destructive nature, a 3 is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, efficient sentence that front-loads the action and includes the key scoping detail about paths. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-required-parameter mutation tool with a full output schema and annotations covering destructiveness, the description is nearly complete. It could mention whether moving directories is supported or whether overwrites are allowed, but these are minor gaps given the schema and annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all three parameters. The description adds the shared context that paths are relative to the server root, which reinforces the schema but doesn't add new meaning beyond it. Baseline 3 is correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Rename or move') and resource ('remote file or directory'), and clarifies that paths are relative to the server root. This clearly distinguishes it from siblings like ftp_delete, ftp_upload, and ftp_download.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for renaming or moving remote entries, but does not explicitly state when to use this tool versus alternatives like ftp_upload or ftp_mkdir. It provides context (both paths relative to server root) but no exclusions or alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ftp_testTest a server connectionARead-onlyIdempotent
Connect to a server, list its root directory, and report success.
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | Name of the configured server. Defaults to the configured default, or the sole server. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds the behavioral detail that it lists the root directory and reports success, which is useful context beyond the annotations. However, it doesn't disclose what 'report success' returns or whether it throws on failure, which would be valuable for a test tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence that front-loads the core action and includes the key behavioral detail ('report success'). Every word earns its place, and there is no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple test tool with one optional parameter, full annotation coverage, and an output schema, the description is nearly complete. The only gap is that it doesn't specify what the success report looks like, but the output schema likely covers that, so the description is adequate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the single parameter 'server' is already documented in the schema. The description doesn't add any additional meaning about the parameter beyond what the schema provides, so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Connect to a server, list its root directory, and report success') with a clear verb and resource. It distinguishes itself from siblings like ftp_list by focusing on testing connectivity rather than general listing, though it doesn't explicitly name a sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage as a connectivity test by mentioning 'report success', but it doesn't explicitly state when to use this tool versus alternatives like ftp_list or ftp_list_servers. The context is clear enough for an agent to infer the primary use case, but no exclusions or alternative routing are provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ftp_uploadUpload a local fileADestructive
Upload one local file to the server, auto-creating parent directories. Remote path defaults to the file basename at the root.
| Name | Required | Description | Default |
|---|---|---|---|
| server | No | Name of the configured server. Defaults to the configured default, or the sole server. | |
| local_path | Yes | Local file path, absolute or relative to the server's configured localRoot. | |
| remote_path | No | Destination remote path, relative to the server root. Defaults to the local basename at the root. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, which signal a mutating operation. The description adds the auto-creation of parent directories and the default remote path behavior, which are valuable non-obvious details. It does not disclose potential overwrites of existing files, but the annotation covers the destructive aspect, so the description adds appropriate context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is one tight sentence with no filler. The key behavioral details are front-loaded: action, auto-creation, and default path. Every word contributes, and it is neither under-specified nor verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present to document return values and annotations covering safety, the description is complete enough. It covers the primary behavior and defaults. It may not cover edge cases like what happens if the file doesn't exist, but given the context, it is adequately comprehensive for an upload tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema documents each parameter clearly. The description adds the default remote path behavior (basename at root) and clarifies the auto-creation of directories, which complements the schema. This is more than a simple restatement, adding value over the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action: uploading a single local file to the server, with the specific behavior of auto-creating parent directories. It distinguishes itself from siblings like ftp_deploy (which likely uploads multiple files or directories) and ftp_download, and the default remote path behavior is clearly explained.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly implies use for single-file uploads and implicitly contrasts with ftp_deploy for bulk deployments, but it does not explicitly state when to use this versus alternatives. It provides no explicit exclusions, but the context is enough for an agent to infer the intended use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.2.0- Changed
ftp_delete1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "deleted": { + "type": "boolean" + }, + "entry_type": { + "enum": [ + "file", + "directory" + ], + "type": "string" + }, + "path": { + "type": "string" + }, + "recursive": { + "type": "boolean" + }, + "security_warning": { + "type": [ + "string", + "null" + ] + }, + "server": { + "type": "string" + } + }, + "required": [ + "server", + "path", + "entry_type", + "recursive", + "deleted", + "security_warning" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "properties": { + "code": { + "enum": [ + "CONFIG_REQUIRED", + "CONFIG_INVALID", + "SERVER_REQUIRED", + "SERVER_UNKNOWN", + "INVALID_ARGUMENT", + "READ_ONLY", + "TRANSPORT_POLICY", + "HOST_KEY_REJECTED", + "REMOTE_ROOT_REJECTED", + "PATH_REJECTED", + "NOT_FOUND", + "ALREADY_EXISTS", + "TRANSFER_LIMIT", + "TRANSFER_VERIFY", + "TARGET_CHANGED", + "CANCELLED", + "TIMEOUT", + "DEPLOY_PARTIAL", + "TRANSPORT_ERROR", + "OUTPUT_LIMIT", + "INTERNAL_ERROR", + "SCAN_LIMIT", + "CAPACITY_LIMIT" + ], + "type": "string" + }, + "effects": { + "enum": [ + "none", + "possible", + "confirmed" + ], + "type": "string" + }, + "message": { + "type": "string" + }, + "next_action": { + "enum": [ + "fix_input", + "fix_config", + "select_server", + "inspect_target", + "retry", + "contact_operator", + "none" + ], + "type": "string" + }, + "partial": { + "additionalProperties": false, + "properties": { + "completed_bytes": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "completed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "failed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "final": { + "type": "boolean" + }, + "total_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "completed_files", + "completed_bytes", + "failed_files", + "final" + ], + "type": "object" + }, + "request_id": { + "format": "uuid", + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "schema_version": { + "const": 1, + "type": "number" + } + }, + "required": [ + "schema_version", + "code", + "message", + "retryable", + "request_id", + "next_action", + "effects" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } + ], + "type": "object" +}
- Changed
ftp_deploy2 fields changed- changed
Input schema / properties / local_dir / descriptionPrevious value: -"Local directory to deploy (relative paths resolve against the process cwd)."New value: +"Local directory to deploy, absolute or relative to the server's configured localRoot." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "aborted_early": { + "type": "boolean" + }, + "complete": { + "type": "boolean" + }, + "duration_ms": { + "minimum": 0, + "type": "integer" + }, + "failed_count": { + "minimum": 0, + "type": "integer" + }, + "failures": { + "items": { + "additionalProperties": false, + "properties": { + "message": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "path", + "message" + ], + "type": "object" + }, + "type": "array" + }, + "failures_omitted": { + "minimum": 0, + "type": "integer" + }, + "mode": { + "enum": [ + "dry_run", + "deploy" + ], + "type": "string" + }, + "planned": { + "items": { + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "size_bytes": { + "minimum": 0, + "type": "number" + } + }, + "required": [ + "path", + "size_bytes" + ], + "type": "object" + }, + "type": "array" + }, + "planned_omitted": { + "minimum": 0, + "type": "integer" + }, + "remote_base": { + "type": "string" + }, + "security_warning": { + "type": [ + "string", + "null" + ] + }, + "server": { + "type": "string" + }, + "total_bytes": { + "minimum": 0, + "type": "number" + }, + "total_files": { + "minimum": 0, + "type": "integer" + }, + "uploaded": { + "items": { + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + }, + "size_bytes": { + "minimum": 0, + "type": "number" + } + }, + "required": [ + "path", + "size_bytes" + ], + "type": "object" + }, + "type": "array" + }, + "uploaded_bytes": { + "minimum": 0, + "type": "number" + }, + "uploaded_count": { + "minimum": 0, + "type": "integer" + }, + "uploaded_omitted": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "mode", + "server", + "remote_base", + "total_files", + "total_bytes", + "uploaded_count", + "uploaded_bytes", + "failed_count", + "aborted_early", + "complete", + "duration_ms", + "security_warning", + "uploaded", + "uploaded_omitted", + "planned", + "planned_omitted", + "failures", + "failures_omitted" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "properties": { + "code": { + "enum": [ + "CONFIG_REQUIRED", + "CONFIG_INVALID", + "SERVER_REQUIRED", + "SERVER_UNKNOWN", + "INVALID_ARGUMENT", + "READ_ONLY", + "TRANSPORT_POLICY", + "HOST_KEY_REJECTED", + "REMOTE_ROOT_REJECTED", + "PATH_REJECTED", + "NOT_FOUND", + "ALREADY_EXISTS", + "TRANSFER_LIMIT", + "TRANSFER_VERIFY", + "TARGET_CHANGED", + "CANCELLED", + "TIMEOUT", + "DEPLOY_PARTIAL", + "TRANSPORT_ERROR", + "OUTPUT_LIMIT", + "INTERNAL_ERROR", + "SCAN_LIMIT", + "CAPACITY_LIMIT" + ], + "type": "string" + }, + "effects": { + "enum": [ + "none", + "possible", + "confirmed" + ], + "type": "string" + }, + "message": { + "type": "string" + }, + "next_action": { + "enum": [ + "fix_input", + "fix_config", + "select_server", + "inspect_target", + "retry", + "contact_operator", + "none" + ], + "type": "string" + }, + "partial": { + "additionalProperties": false, + "properties": { + "completed_bytes": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "completed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "failed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "final": { + "type": "boolean" + }, + "total_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "completed_files", + "completed_bytes", + "failed_files", + "final" + ], + "type": "object" + }, + "request_id": { + "format": "uuid", + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "schema_version": { + "const": 1, + "type": "number" + } + }, + "required": [ + "schema_version", + "code", + "message", + "retryable", + "request_id", + "next_action", + "effects" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } + ], + "type": "object" +}
- Changed
ftp_download2 fields changed- changed
Input schema / properties / local_path / descriptionPrevious value: -"Local destination path (relative paths resolve against the process cwd)."New value: +"Local destination path, absolute or relative to the server's configured localRoot." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "local_path": { + "type": "string" + }, + "overwritten": { + "type": "boolean" + }, + "remote_path": { + "type": "string" + }, + "security_warning": { + "type": [ + "string", + "null" + ] + }, + "server": { + "type": "string" + }, + "size_bytes": { + "minimum": 0, + "type": "number" + } + }, + "required": [ + "server", + "remote_path", + "local_path", + "size_bytes", + "overwritten", + "security_warning" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "properties": { + "code": { + "enum": [ + "CONFIG_REQUIRED", + "CONFIG_INVALID", + "SERVER_REQUIRED", + "SERVER_UNKNOWN", + "INVALID_ARGUMENT", + "READ_ONLY", + "TRANSPORT_POLICY", + "HOST_KEY_REJECTED", + "REMOTE_ROOT_REJECTED", + "PATH_REJECTED", + "NOT_FOUND", + "ALREADY_EXISTS", + "TRANSFER_LIMIT", + "TRANSFER_VERIFY", + "TARGET_CHANGED", + "CANCELLED", + "TIMEOUT", + "DEPLOY_PARTIAL", + "TRANSPORT_ERROR", + "OUTPUT_LIMIT", + "INTERNAL_ERROR", + "SCAN_LIMIT", + "CAPACITY_LIMIT" + ], + "type": "string" + }, + "effects": { + "enum": [ + "none", + "possible", + "confirmed" + ], + "type": "string" + }, + "message": { + "type": "string" + }, + "next_action": { + "enum": [ + "fix_input", + "fix_config", + "select_server", + "inspect_target", + "retry", + "contact_operator", + "none" + ], + "type": "string" + }, + "partial": { + "additionalProperties": false, + "properties": { + "completed_bytes": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "completed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "failed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "final": { + "type": "boolean" + }, + "total_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "completed_files", + "completed_bytes", + "failed_files", + "final" + ], + "type": "object" + }, + "request_id": { + "format": "uuid", + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "schema_version": { + "const": 1, + "type": "number" + } + }, + "required": [ + "schema_version", + "code", + "message", + "retryable", + "request_id", + "next_action", + "effects" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } + ], + "type": "object" +}
- Changed
ftp_list3 fields changed- added
Input schema / properties / limitAdded value: +{ + "description": "Maximum entries to return (default 50, maximum 200).", + "maximum": 200, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / offsetAdded value: +{ + "description": "Zero-based entry offset (default 0).", + "minimum": 0, + "type": "integer" +} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "count": { + "minimum": 0, + "type": "integer" + }, + "entries": { + "items": { + "additionalProperties": false, + "properties": { + "modified_at": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "size_bytes": { + "minimum": 0, + "type": "number" + }, + "type": { + "enum": [ + "dir", + "file", + "link" + ], + "type": "string" + } + }, + "required": [ + "name", + "type", + "size_bytes", + "modified_at" + ], + "type": "object" + }, + "type": "array" + }, + "has_more": { + "type": "boolean" + }, + "limit": { + "maximum": 200, + "minimum": 1, + "type": "integer" + }, + "next_offset": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "offset": { + "minimum": 0, + "type": "integer" + }, + "path": { + "type": "string" + }, + "security_warning": { + "type": [ + "string", + "null" + ] + }, + "server": { + "type": "string" + }, + "total": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "server", + "path", + "total", + "count", + "offset", + "limit", + "has_more", + "next_offset", + "entries", + "security_warning" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "properties": { + "code": { + "enum": [ + "CONFIG_REQUIRED", + "CONFIG_INVALID", + "SERVER_REQUIRED", + "SERVER_UNKNOWN", + "INVALID_ARGUMENT", + "READ_ONLY", + "TRANSPORT_POLICY", + "HOST_KEY_REJECTED", + "REMOTE_ROOT_REJECTED", + "PATH_REJECTED", + "NOT_FOUND", + "ALREADY_EXISTS", + "TRANSFER_LIMIT", + "TRANSFER_VERIFY", + "TARGET_CHANGED", + "CANCELLED", + "TIMEOUT", + "DEPLOY_PARTIAL", + "TRANSPORT_ERROR", + "OUTPUT_LIMIT", + "INTERNAL_ERROR", + "SCAN_LIMIT", + "CAPACITY_LIMIT" + ], + "type": "string" + }, + "effects": { + "enum": [ + "none", + "possible", + "confirmed" + ], + "type": "string" + }, + "message": { + "type": "string" + }, + "next_action": { + "enum": [ + "fix_input", + "fix_config", + "select_server", + "inspect_target", + "retry", + "contact_operator", + "none" + ], + "type": "string" + }, + "partial": { + "additionalProperties": false, + "properties": { + "completed_bytes": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "completed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "failed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "final": { + "type": "boolean" + }, + "total_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "completed_files", + "completed_bytes", + "failed_files", + "final" + ], + "type": "object" + }, + "request_id": { + "format": "uuid", + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "schema_version": { + "const": 1, + "type": "number" + } + }, + "required": [ + "schema_version", + "code", + "message", + "retryable", + "request_id", + "next_action", + "effects" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } + ], + "type": "object" +}
- Changed
ftp_list_servers2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "configured_count": { + "minimum": 0, + "type": "integer" + }, + "default_server": { + "type": [ + "string", + "null" + ] + }, + "errors": { + "items": { + "additionalProperties": false, + "properties": { + "message": { + "type": "string" + }, + "server": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "server", + "message" + ], + "type": "object" + }, + "type": "array" + }, + "errors_omitted": { + "minimum": 0, + "type": "integer" + }, + "invalid_count": { + "minimum": 0, + "type": "integer" + }, + "servers": { + "items": { + "additionalProperties": false, + "properties": { + "auth": { + "enum": [ + "key", + "password" + ], + "type": "string" + }, + "connection_refused": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "is_default": { + "type": "boolean" + }, + "local_root_status": { + "type": "string" + }, + "name": { + "type": "string" + }, + "port": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "protocol": { + "enum": [ + "ftp", + "ftps", + "sftp" + ], + "type": "string" + }, + "read_only": { + "type": "boolean" + }, + "root": { + "type": "string" + }, + "security_warning": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "name", + "protocol", + "host", + "port", + "root", + "read_only", + "auth", + "is_default", + "local_root_status", + "connection_refused", + "security_warning" + ], + "type": "object" + }, + "type": "array" + }, + "servers_omitted": { + "minimum": 0, + "type": "integer" + }, + "status": { + "enum": [ + "configured", + "missing", + "invalid" + ], + "type": "string" + }, + "valid_count": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "status", + "configured_count", + "valid_count", + "invalid_count", + "default_server", + "servers", + "servers_omitted", + "errors", + "errors_omitted" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "properties": { + "code": { + "enum": [ + "CONFIG_REQUIRED", + "CONFIG_INVALID", + "SERVER_REQUIRED", + "SERVER_UNKNOWN", + "INVALID_ARGUMENT", + "READ_ONLY", + "TRANSPORT_POLICY", + "HOST_KEY_REJECTED", + "REMOTE_ROOT_REJECTED", + "PATH_REJECTED", + "NOT_FOUND", + "ALREADY_EXISTS", + "TRANSFER_LIMIT", + "TRANSFER_VERIFY", + "TARGET_CHANGED", + "CANCELLED", + "TIMEOUT", + "DEPLOY_PARTIAL", + "TRANSPORT_ERROR", + "OUTPUT_LIMIT", + "INTERNAL_ERROR", + "SCAN_LIMIT", + "CAPACITY_LIMIT" + ], + "type": "string" + }, + "effects": { + "enum": [ + "none", + "possible", + "confirmed" + ], + "type": "string" + }, + "message": { + "type": "string" + }, + "next_action": { + "enum": [ + "fix_input", + "fix_config", + "select_server", + "inspect_target", + "retry", + "contact_operator", + "none" + ], + "type": "string" + }, + "partial": { + "additionalProperties": false, + "properties": { + "completed_bytes": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "completed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "failed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "final": { + "type": "boolean" + }, + "total_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "completed_files", + "completed_bytes", + "failed_files", + "final" + ], + "type": "object" + }, + "request_id": { + "format": "uuid", + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "schema_version": { + "const": 1, + "type": "number" + } + }, + "required": [ + "schema_version", + "code", + "message", + "retryable", + "request_id", + "next_action", + "effects" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } + ], + "type": "object" +}
- Changed
ftp_mkdir1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "created": { + "type": "boolean" + }, + "path": { + "type": "string" + }, + "security_warning": { + "type": [ + "string", + "null" + ] + }, + "server": { + "type": "string" + } + }, + "required": [ + "server", + "path", + "created", + "security_warning" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "properties": { + "code": { + "enum": [ + "CONFIG_REQUIRED", + "CONFIG_INVALID", + "SERVER_REQUIRED", + "SERVER_UNKNOWN", + "INVALID_ARGUMENT", + "READ_ONLY", + "TRANSPORT_POLICY", + "HOST_KEY_REJECTED", + "REMOTE_ROOT_REJECTED", + "PATH_REJECTED", + "NOT_FOUND", + "ALREADY_EXISTS", + "TRANSFER_LIMIT", + "TRANSFER_VERIFY", + "TARGET_CHANGED", + "CANCELLED", + "TIMEOUT", + "DEPLOY_PARTIAL", + "TRANSPORT_ERROR", + "OUTPUT_LIMIT", + "INTERNAL_ERROR", + "SCAN_LIMIT", + "CAPACITY_LIMIT" + ], + "type": "string" + }, + "effects": { + "enum": [ + "none", + "possible", + "confirmed" + ], + "type": "string" + }, + "message": { + "type": "string" + }, + "next_action": { + "enum": [ + "fix_input", + "fix_config", + "select_server", + "inspect_target", + "retry", + "contact_operator", + "none" + ], + "type": "string" + }, + "partial": { + "additionalProperties": false, + "properties": { + "completed_bytes": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "completed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "failed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "final": { + "type": "boolean" + }, + "total_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "completed_files", + "completed_bytes", + "failed_files", + "final" + ], + "type": "object" + }, + "request_id": { + "format": "uuid", + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "schema_version": { + "const": 1, + "type": "number" + } + }, + "required": [ + "schema_version", + "code", + "message", + "retryable", + "request_id", + "next_action", + "effects" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } + ], + "type": "object" +}
- Added
ftp_read - Added
ftp_rename - Changed
ftp_test1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "entries_visible": { + "minimum": 0, + "type": "integer" + }, + "host": { + "type": "string" + }, + "port": { + "exclusiveMinimum": 0, + "type": "integer" + }, + "protocol": { + "enum": [ + "ftp", + "ftps", + "sftp" + ], + "type": "string" + }, + "root": { + "type": "string" + }, + "security_warning": { + "type": [ + "string", + "null" + ] + }, + "server": { + "type": "string" + } + }, + "required": [ + "server", + "protocol", + "host", + "port", + "root", + "entries_visible", + "security_warning" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "properties": { + "code": { + "enum": [ + "CONFIG_REQUIRED", + "CONFIG_INVALID", + "SERVER_REQUIRED", + "SERVER_UNKNOWN", + "INVALID_ARGUMENT", + "READ_ONLY", + "TRANSPORT_POLICY", + "HOST_KEY_REJECTED", + "REMOTE_ROOT_REJECTED", + "PATH_REJECTED", + "NOT_FOUND", + "ALREADY_EXISTS", + "TRANSFER_LIMIT", + "TRANSFER_VERIFY", + "TARGET_CHANGED", + "CANCELLED", + "TIMEOUT", + "DEPLOY_PARTIAL", + "TRANSPORT_ERROR", + "OUTPUT_LIMIT", + "INTERNAL_ERROR", + "SCAN_LIMIT", + "CAPACITY_LIMIT" + ], + "type": "string" + }, + "effects": { + "enum": [ + "none", + "possible", + "confirmed" + ], + "type": "string" + }, + "message": { + "type": "string" + }, + "next_action": { + "enum": [ + "fix_input", + "fix_config", + "select_server", + "inspect_target", + "retry", + "contact_operator", + "none" + ], + "type": "string" + }, + "partial": { + "additionalProperties": false, + "properties": { + "completed_bytes": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "completed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "failed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "final": { + "type": "boolean" + }, + "total_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "completed_files", + "completed_bytes", + "failed_files", + "final" + ], + "type": "object" + }, + "request_id": { + "format": "uuid", + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "schema_version": { + "const": 1, + "type": "number" + } + }, + "required": [ + "schema_version", + "code", + "message", + "retryable", + "request_id", + "next_action", + "effects" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } + ], + "type": "object" +}
- Changed
ftp_upload2 fields changed- changed
Input schema / properties / local_path / descriptionPrevious value: -"Local file path (relative paths resolve against the process cwd)."New value: +"Local file path, absolute or relative to the server's configured localRoot." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "oneOf": [ + { + "additionalProperties": false, + "properties": { + "local_path": { + "type": "string" + }, + "remote_path": { + "type": "string" + }, + "security_warning": { + "type": [ + "string", + "null" + ] + }, + "server": { + "type": "string" + }, + "size_bytes": { + "minimum": 0, + "type": "number" + } + }, + "required": [ + "server", + "local_path", + "remote_path", + "size_bytes", + "security_warning" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "error": { + "additionalProperties": false, + "properties": { + "code": { + "enum": [ + "CONFIG_REQUIRED", + "CONFIG_INVALID", + "SERVER_REQUIRED", + "SERVER_UNKNOWN", + "INVALID_ARGUMENT", + "READ_ONLY", + "TRANSPORT_POLICY", + "HOST_KEY_REJECTED", + "REMOTE_ROOT_REJECTED", + "PATH_REJECTED", + "NOT_FOUND", + "ALREADY_EXISTS", + "TRANSFER_LIMIT", + "TRANSFER_VERIFY", + "TARGET_CHANGED", + "CANCELLED", + "TIMEOUT", + "DEPLOY_PARTIAL", + "TRANSPORT_ERROR", + "OUTPUT_LIMIT", + "INTERNAL_ERROR", + "SCAN_LIMIT", + "CAPACITY_LIMIT" + ], + "type": "string" + }, + "effects": { + "enum": [ + "none", + "possible", + "confirmed" + ], + "type": "string" + }, + "message": { + "type": "string" + }, + "next_action": { + "enum": [ + "fix_input", + "fix_config", + "select_server", + "inspect_target", + "retry", + "contact_operator", + "none" + ], + "type": "string" + }, + "partial": { + "additionalProperties": false, + "properties": { + "completed_bytes": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "completed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "failed_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "final": { + "type": "boolean" + }, + "total_files": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "completed_files", + "completed_bytes", + "failed_files", + "final" + ], + "type": "object" + }, + "request_id": { + "format": "uuid", + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "schema_version": { + "const": 1, + "type": "number" + } + }, + "required": [ + "schema_version", + "code", + "message", + "retryable", + "request_id", + "next_action", + "effects" + ], + "type": "object" + } + }, + "required": [ + "error" + ], + "type": "object" + } + ], + "type": "object" +}
8 tool updates
v0.1.0- First observed
ftp_delete - First observed
ftp_deploy - First observed
ftp_download - First observed
ftp_list - First observed
ftp_list_servers - First observed
ftp_mkdir - First observed
ftp_test - First observed
ftp_upload
TDQS
Scored across 10 tools
Most tools have clear, distinct purposes (list, read, upload, download, mkdir, rename, delete, deploy, list servers, test). The only potential overlap is between ftp_deploy and ftp_upload, but ftp_deploy is for recursive directory uploads while ftp_upload is for a single file, so they are distinguishable with descriptions.
All tool names follow a consistent ftp_ prefix with a verb (deploy, list, test, read, upload, download, mkdir, rename, delete). The pattern is uniform and predictable, making it easy for an agent to infer functionality.
With 10 tools, the set is well-scoped for an FTP deployment server. Each tool covers a distinct operation (connection management, file operations, directory operations, deployment), and the count is within the ideal range of 3-15 tools.
The core FTP operations are covered: list, read, upload, download, mkdir, rename, delete, and deploy. Missing features like file permissions or detailed metadata (e.g., last modified time) are minor gaps, but the CRUD lifecycle is complete for file and directory operations.
Maintenance
Related MCP Connectors
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
MCP server for building and testing AI agents with multi-model experimentation and insights.
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
Related MCP Servers
- AlicenseAqualityDmaintenanceAn enterprise-grade MCP server for FTP and SFTP operations optimized for AI coding assistants, featuring smart synchronization, connection pooling, and unified diff patching.2837 npm3MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives AI agents SSH capabilities to execute commands, transfer files, and inspect remote systems through a preconfigured host list.41 npmMIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that enables AI assistants to perform development operations on remote servers via SSH, including executing commands, managing files, and browsing directories.1MIT
- AlicenseNot gradedqualityCmaintenanceAn MCP server for managing remote SSH servers, enabling AI agents to execute commands, transfer files, and perform deployment operations securely.MIT