内容
# 自托管的MCP服务器
[](LICENSE)
[](https://github.com/gouthamkallempudi/silo-mcp/actions/workflows/test.yml)
[](pyproject.toml)
[](https://modelcontextprotocol.io)
一个自托管的MCP服务器,可以在您的文件系统和云存储之间传输文件——支持九种平台,提供统一的工具界面。上传、下载、列出、搜索、删除、生成分享链接,以及直接从一个云存储复制到另一个,无需提供LLM原始文件系统访问权限或一大堆特定于平台的凭证。
这不是“本地优先”——文件存放在云端,每次操作都与远程提供商API通信。保持本地的是信任相关的部分:服务器作为您机器上的子进程运行,您的凭证存储在操作系统密钥环中(从不存储在云端、配置文件或工具调用参数中),传输审计日志是本地的SQLite文件。
**文档存储:** Dropbox · Google Drive · OneDrive · Box · Google Photos(仅上传)· Yandex Disk
**对象存储:** S3兼容(AWS S3,Cloudflare R2,MinIO)· Google Cloud Storage · Azure Blob Storage
## 使用Silo MCP可以做什么?
- **直接从一个云存储复制文件到另一个** — “将我的Dropbox `/photos` 文件复制到我的S3存储桶中”,“将此Drive文件迁移到OneDrive” — 通过一次工具调用,无需手动下载然后上传。单一供应商存储集成在结构上无法实现的功能。
- **在云存储中移动文件** — “将 `report.pdf` 上传到Dropbox”,“从Google Drive下载 `notes.txt` 以便使用” — 跨所有九个平台,使用一套工具。
- **浏览和搜索** 您的文档存储,按文件夹或查询,并按键前缀列出对象存储桶。
- **生成可分享的链接** 到文件 — 对象存储上的预签名/签名URL和Box(带过期时间),其他平台上的分享链接。
- **桥接到其他MCP服务器** — 这里的 `download_file` 可以直接用于另一个服务器(例如,社交发布服务器的 `media_paths`)。
- **保持本地审计跟踪** — 每次上传、下载、删除和分享链接都记录在本地SQLite日志中,您可以使用 `list_transfers` 查询。
所有操作都通过自然语言通过您的MCP客户端完成 — 请参见[示例工作流程](#使用自然语言与之交互)。
**快速入门:** `pip install -e .` → 将 `silo-mcp` 添加到您的MCP客户端配置中 → `silo-mcp-accounts add dropbox --label personal` → 询问您的客户端列出您的Dropbox文件。完整步骤请参见下面的[安装](#安装)和[添加账户](#添加账户)。
## 目录
- [使用Silo MCP可以做什么?](#使用silo-mcp可以做什么?)
- [为什么](#为什么)
- [架构](#架构)
- [需求](#需求)
- [安装](#安装)
- [配置您的MCP客户端](#配置您的mcp客户端)
- [支持的客户端](#支持的客户端)
- [与Ollama一起使用](#与ollama一起使用)
- [添加账户](#添加账户)
- [示例工作流程](#使用自然语言与之交互)
- [工具](#工具)
- [支持的平台和工具](#支持的平台和工具)
- [设置摩擦和每个平台的凭证形状](#设置摩擦和每个平台的凭证形状)
- [路径安全性](#路径安全性)
- [特定于平台的说明](#特定于平台的说明)
- [安全性](#安全性)
- [测试](#测试)
- [故障排除](#故障排除)
- [相关](#相关)
- [许可证](#许可证)
## 为什么
大多数“给予LLM您的云存储”的设置都陷入两个陷阱之一:
单一平台集成,一旦更换提供商就失效,或者无限制的本地文件访问,让操纵的对话可以读取或上传磁盘上的任何内容。Silo MCP针对两者而设计:
- **一套工具界面,九种平台。** `upload_file`、`download_file`、`list_files`、`search_files`、`delete_file`、`create_share_link` 的行为与您指向的平台无关 — 对象存储(桶+键)和文档存储(基于路径)共享一个 `FileStore` 接口。
- **设计上受限,而不是靠约定。** `local_path` 用于上传,下载的目的地都是LLM提供的工具调用参数,因此两者都严格限制在配置的根目录中 — 操纵的对话不能要求此服务器读取SSH密钥或写入不应该下载的位置。
- **突变需要 `confirm=true`。** `upload_file`、`delete_file` 和 `create_share_link` 都修改远程状态或创建持有者凭证链接,需要明确确认。`download_file`/`list_files`/`search_files` 仅写入本地,因此不进行限制。
- **从一开始就支持多账户。** 每个工具都接受一个可选的 `account` 标签 — 可以并行运行一个工作Dropbox和一个个人Dropbox,无需重新配置任何内容。
## 架构
```mermaid
flowchart TB
subgraph Client["MCP客户端"]
direction LR
CD["Claude Desktop / Code<br/>(stdio)"]
OL["Ollama bridge<br/>(streamable-http / SSE)"]
end
subgraph Silo["Silo MCP服务器 (server.py)"]
direction TB
Tools["工具界面<br/>upload_file · download_file · list_files<br/>search_files · delete_file · create_share_link"]
Paths["paths.py<br/>上传/下载根目录限制"]
Registry["stores/registry.py<br/>解析(platform, account)"]
Accounts["accounts.py<br/>凭证存储 + OAuth刷新"]
History[("db.py<br/>SQLite传输日志")]
end
subgraph Backends["FileStore实现"]
direction LR
Doc["文档存储<br/>Dropbox · Drive · OneDrive<br/>Box · Photos · Yandex Disk"]
Obj["对象存储<br/>S3兼容 · GCS · Azure Blob"]
end
FS[("本地文件系统<br/>上传/下载根目录")]
Cred[("操作系统凭证存储<br/>Windows / macOS / Linux密钥环")]
CD --> Tools
OL --> Tools
Tools --> Paths --> FS
Tools --> Registry
Registry --> Doc
Registry --> Obj
Registry --> Accounts --> Cred
Tools --> History
```
每个工具调用都解析一个 `(platform, account)` 对到一个 `FileStore` 实现和来自 `accounts.py` 的凭证,检查本地路径是否与上传/下载根目录匹配,针对真正的平台API执行,并记录结果 — 无论哪一个后端在另一端,形状保持不变。
## 需求
- Python 3.11+
- 操作系统凭证存储 `keyring` 可以使用(Windows Credential Manager,macOS Keychain或Linux上的Secret Service提供程序)— 凭证从不以明文写入磁盘或通过MCP工具调用传递。
## 安装
```bash
git clone https://github.com/gouthamkallempudi/silo-mcp.git
cd silo-mcp
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
pip install -e .
```
对象存储SDK是可选的额外组件 — 只需安装您需要的:
```bash
pip install -e ".[s3]" # AWS S3 / Cloudflare R2 / MinIO
pip install -e ".[gcs]" # Google Cloud Storage
pip install -e ".[azure]" # Azure Blob Storage
pip install -e ".[all]" # 以上所有
```
核心安装(无额外组件)涵盖Dropbox/Drive/OneDrive/Box/Google Photos/Yandex Disk,无需依赖 `httpx` — 如果对象存储SDK未安装,服务器会优雅地降级,而不是启动失败。
## 配置您的MCP客户端
将以下内容添加到客户端的MCP配置中(例如Claude Desktop的 `claude_desktop_config.json` 或Claude Code的 `.mcp.json`):
```json
{
"mcpServers": {
"silo-mcp": {
"command": "silo-mcp"
}
}
}
```
`silo-mcp` 必须在客户端启动服务器的环境中可以解析 — 如果您安装到虚拟环境中,请直接将 `command` 指向该虚拟环境的 `silo-mcp` 可执行文件(例如 `/path/to/silo-mcp/.venv/bin/silo-mcp`),而不是依赖shell激活。
<details>
<summary><strong>传输选项</strong>(默认stdio,适用于网络客户端的HTTP)</summary>
默认为 `stdio`,每个桌面MCP客户端(Claude Desktop,Claude Code,Cursor等)都作为子进程启动 — 不打开网络端口。对于无法启动本地子进程并需要通过HTTP访问服务器的客户端(请参见下面的[与Ollama一起使用](#与ollama一起使用)),请设置:
```bash
SILO_MCP_TRANSPORT=streamable-http SILO_MCP_HOST=127.0.0.1 SILO_MCP_PORT=8000 silo-mcp
```
`SILO_MCP_TRANSPORT` 也接受 `sse`(较旧的HTTP传输,已被客户端弃用) 。`SILO_MCP_HOST`/`SILO_MCP_PORT` 仅在两种HTTP传输中读取,默认为 `127.0.0.1:8000`。
> [!WARNING]
> 该服务器在HTTP模式下没有内置身份验证 — 不要将其绑定到 `0.0.0.0` 或通过localhost以外的任何方式公开,否则将面临身份验证风险,因为每个工具调用都可访问您的云存储账户。
</details>
## 支持的客户端
任何支持本地stdio服务器的MCP客户端都可以使用 — 服务器仅使用标准MCP工具调用,无需客户端特定功能。已验证并预计可正常工作的客户端:
| 客户端 | 配置 | 备注 |
|---|---|---|
| Claude Code | `.mcp.json` 在项目中 (`{"mcpServers":{"silo-mcp":{"command":"silo-mcp"}}}`) | 通过MCP协议验证端到端。 |
| Claude Desktop | `claude_desktop_config.json` — 相同的 `mcpServers` 块 | stdio。 |
| Cursor / VS Code (MCP) | 他们的 `mcp.json` — 相同的 `mcpServers` 块 | stdio。 |
| Ollama (通过桥接器) | 将桥接器指向HTTP传输 | 参见[与Ollama一起使用](#与ollama一起使用)。 |
> [!TIP]
> 如果 `silo-mcp` 不在启动shell的 `PATH` 中(虚拟环境中常见),请将 `command` 直接设置为虚拟环境的二进制文件路径,例如在Windows上为 `C:\\path\\to\\silo-mcp\\.venv\\Scripts\\silo-mcp.exe` 或其他位置为 `/path/to/.venv/bin/silo-mcp`。
## 与Ollama一起使用
Ollama不原生支持MCP — 它需要一个MCP客户端在循环中,将Ollama的工具调用转换为MCP工具调用,与Claude Desktop/Code为Claude所做的一样。该服务器不附带该桥接器(为了保持服务器软件包的纯粹性),但任何支持MCP的Ollama客户端都可以使用,只要您将其指向streamable-http而不是stdio:
1. 以HTTP模式启动服务器:`SILO_MCP_TRANSPORT=streamable-http silo-mcp`。
2. 将您的Ollama侧MCP客户端指向 `http://127.0.0.1:8000/mcp`。
3. 使用支持工具调用的模型(例如 `llama3.1`、`qwen2.5`)— Ollama仅为支持 `tools` API字段的模型路由工具调用。
## 添加账户
凭证通过CLI添加,具有隐藏输入,绝不通过MCP工具调用,并存储在操作系统的凭证存储中:
```bash
silo-mcp-accounts add dropbox --label personal
silo-mcp-accounts add google_drive --label personal
silo-mcp-accounts add onedrive --label personal
silo-mcp-accounts add box --label personal
silo-mcp-accounts add google_photos --label personal
silo-mcp-accounts add yandex_disk --label personal
silo-mcp-accounts add s3 --label personal
silo-mcp-accounts add gcs --label personal
silo-mcp-accounts add azure_blob --label personal
silo-mcp-accounts list
```
<details>
<summary><strong>首次OAuth设置</strong>(Drive,Photos,OneDrive,Box)</summary>
这四个平台在`silo-mcp-accounts add`接受它们之前需要一个`refresh_token` — 而获取第一个令牌需要一次性浏览器授权,而不仅仅是一个client_id/secret对。 在每个平台的开发者控制台中注册一个应用程序,然后运行:
```bash
silo-mcp-accounts oauth google_drive --label personal
silo-mcp-accounts oauth google_photos --label personal
silo-mcp-accounts oauth onedrive --label personal
silo-mcp-accounts oauth box --label personal
```
这将打开您的浏览器到平台的同意屏幕,监听 `http://localhost:8765/callback` 的重定向,将代码交换为 `refresh_token`,并存储账户 — 之后无需运行 `add`。
应用注册,按平台:
- **谷歌(Drive + Photos共享一个应用)**: [谷歌云控制台](https://console.cloud.google.com) → 新项目 → APIs & Services → 启用 **谷歌硬盘API** 和 **照片库API** → OAuth同意屏幕(外部适用于个人测试,添加您自己作为测试用户)→ 凭证 → 创建OAuth客户端ID → 类型 **桌面应用**。 桌面应用客户端接受任何 `http://localhost:<port>` 重定向,无需预先注册,因此无需设置重定向URI。
- **OneDrive**: [Azure门户](https://portal.azure.com) → 应用注册 → 新注册 → 平台 **移动和桌面应用程序** → 添加重定向URI `http://localhost:8765/callback` 确切(Azure需要精确匹配)。 API权限 → 微软图 → 添加 `Files.ReadWrite` 和 `offline_access`(委托)。 证书和密钥 → 新的客户端密钥。
- **Box**: [Box开发者控制台](https://app.box.com/developers/console) → 创建新应用 → **自定义应用** → **用户身份验证(OAuth 2.0)** → 在配置下,设置重定向URI为 `http://localhost:8765/callback` 确切(Box也需要精确匹配),并检查您需要的范围(读取/写入文件)。
如果您使用 `--port` 选择不同的本地端口,请在注册的重定向URI中使用相同的端口。
</details>
<details>
<summary><strong>获取Yandex Disk令牌</strong></summary>
Yandex Disk是一个静态令牌,像Dropbox一样 — 没有 `refresh_token`,因此它不在上述 `silo-mcp-accounts oauth` 启动流程中。 其OAuth应用配置使用 **隐式授权**:令牌直接在浏览器中返回,没有代码交换步骤来脚本。
1. [oauth.yandex.com](https://oauth.yandex.com/) → **创建应用**(或重用现有应用)。
2. 在 **平台** 下,选中 **Web服务** 并将重定向URI设置为 `https://oauth.yandex.com/verification_code` — Yandex自己的内置页面,仅显示令牌,无需本地侦听器。
3. 在 **权限** 下,授予磁盘访问权限:`cloud_api:disk.read` 和 `cloud_api:disk.write`(或 `cloud_api:disk.app_folder` 代替 `disk.write`,如果您希望将其范围限定为应用特定的文件夹而不是整个磁盘)。
4. 保存应用并记下其 **ID**。
5. 在浏览器中访问 `https://oauth.yandex.com/authorize?response_type=token&client_id=<your-app-id>`,批准访问 — 令牌直接在重定向页面上显示。
6. `silo-mcp-accounts add yandex_disk --label personal`,粘贴令牌。
</details>
<details>
<summary><strong>更快的批量设置</strong>:导入文件或环境变量</summary>
运行 `add` 一次需要为每个平台执行一次 — 当您一次配置多个平台时,这会变得很麻烦。 两个更快的路径 — 两者最终都存储在操作系统的密钥环中,而不是文件或环境变量中:
**导入文件** — 复制模板,填写真实值,一次导入。 [`silo-accounts.example.yaml`](silo-accounts.example.yaml) 在存储库根目录中为所有九个平台提供了每个平台所需的确切字段名称和注释:
```bash
cp silo-accounts.example.yaml silo-accounts.yaml
# 编辑silo-accounts.yaml为真实值,删除不使用的平台
silo-mcp-accounts import silo-accounts.yaml
```
JSON也支持(相同的`platform -> label -> fields`结构),通过文件扩展名调度 — 任何不以`.json`结尾的文件都将被解析为YAML。
`silo-accounts.yaml`/`.json`,`credentials.yaml`/`.json`和任何`*.local.yaml`/`.json`都已在`.gitignore`中,但将其视为备份,而不是计划 — **在导入后立即删除文件。**
它是磁盘上纯文本机密,直到删除,gitignore与否。
**环境变量** — 对于脚本或CI设置不方便时,使用文件,`add`读取`SILO_MCP_CRED_<PLATFORM>_<FIELD>`,然后提示:
```bash
SILO_MCP_CRED_DROPBOX_ACCESS_TOKEN=sl.xxx silo-mcp-accounts add dropbox --label personal
```
环境变量比删除的文件更容易暴露(进程列表,shell 历史记录,崩溃转储),因此对于快速自动化测试设置,请首选导入文件。
</details>
<a id="talking-to-it-in-natural-language"></a>
## 示例工作流
连接服务器后,您不会直接调用工具,而是用自然语言询问您的MCP客户端(Claude Desktop、Claude Code等),它会选择合适的工具和参数。以下示例都通过真实的MCP协议与实时Dropbox、Google Drive和Google Photos账户进行了端到端的测试。
### **Dropbox / OneDrive / Yandex Disk** (真实路径):
- "将`report.pdf`从我的上传文件夹上传到Dropbox。"
- "我的Dropbox根目录下有什么?找任何名为`invoice`的文件。"
- "从Dropbox下载`notes.txt`并给我一个可分享的链接。"
- "从我的Dropbox删除`old-draft.txt`。
### **Google Drive / Box** (按ID寻址 — 先搜索,然后对ID操作):
- "在我的Google Drive中找到`budget.xlsx`。" → 然后 "下载那个文件。"
- "分享那个文件,生成一个公共链接。
### **Google Photos** (仅上传):
- "将这个截图上传到我的Google Photos。
### **对象存储 — S3 / GCS / Azure Blob** (需要存储桶/容器):
- "将`backup.zip`上传到我的S3存储桶`my-backups`。"
- "列出`my-backups`存储桶下的`logs/`目录中的所有文件。"
- "给我一个`backup.zip`在`my-backups`中的1小时预签名链接。
### **跨云复制** (从一个平台直接复制到另一个平台):
- "将`report.pdf`从我的Dropbox复制到我的Google Drive。"
- "将我刚刚在Drive中找到的文件迁移到我的S3 `archive`存储桶。
### **跨服务器:**
- "从Google Drive抓取我的头像,以便我可以将其附加到帖子。" — `download_file`获得一个本地路径,另一个MCP服务器可以使用。
### 工具调用的完整映射:
| 您说的话 | 运行的工具 |
|---|---|
| "我可以使用哪些云存储,哪些账户已设置?" | `list_supported_stores` |
| "将`report.pdf`从我的上传文件夹上传到Dropbox。" | `upload_file(dropbox, …, confirm=true)` — 客户端在您同意后设置`confirm` |
| "我的Dropbox根目录下有什么?" | `list_files(dropbox)` |
| "在我的Dropbox中找到名为`invoice`的文件。" | `search_files(dropbox, "invoice")` |
| "从Dropbox下载`notes.txt`以便使用。" | `download_file(dropbox, "/notes.txt")` |
| "给我一个可分享的链接到那个文件。" | `create_share_link(dropbox, …, confirm=true)` |
| "从Dropbox删除`old-draft.txt`。" | `delete_file(dropbox, "/old-draft.txt", confirm=true)` |
| "将此图片放入我的Google Photos。" | `upload_file(google_photos, …, confirm=true)` |
| "将我的简历从Dropbox复制到推文草稿中。" | `download_file` → 将本地路径交给另一个MCP服务器的`media_paths` |
| "展示我最近移动了什么。" | `list_transfers` |
因为Google Drive和Box按ID寻址文件,所以自然流程是两步 — "在我的Drive中找到`budget.xlsx`" (`search_files`,返回ID),然后使用客户端刚刚看到的ID "下载那个文件" / "分享那个文件"。
变异操作 (`upload_file`, `delete_file`, `create_share_link`) 需要 `confirm=true`,所以一个好的客户端会准确显示它即将执行的操作,只有在您批准后才继续 — 删除或生成公共链接不会在模糊请求时无声进行。
## 工具
| 工具 | 确认门控 | 描述 |
|---|---|---|
| `list_supported_stores()` | — | 所有已知平台、其类型(文档 vs. 对象存储)、功能和已配置的账户。 |
| `list_accounts(platform)` | — | 已配置的账户,可选过滤。 |
| `upload_file(platform, local_path, remote_path, account, target, confirm)` | ✅ | `local_path` 必须在上传根目录内。 |
| `download_file(platform, remote_path, local_filename, account, target)` | — | 写入下载根目录并返回本地路径。 |
| `list_files(platform, folder_or_prefix, account, target)` | — | 文件夹列表(文档存储)或前缀列表(对象存储)。 |
| `search_files(platform, query, account, target)` | — | 平台不支持搜索时清楚地报错。 |
| `delete_file(platform, remote_path, account, target, confirm)` | ✅ | |
| `create_share_link(platform, remote_path, account, target, expires_in_seconds, confirm)` | ✅ | 一个链接,任何人都可以使用它来读取文件,而无需账户,在支持的平台上。参见 [分享链接](#分享链接)。 |
| `copy_file(from_platform, from_remote_path, to_platform, to_remote_path, from_account, to_account, from_target, to_target, confirm)` | ✅ | 直接从一个云复制文件到另一个云。参见 [跨云复制](#跨云复制)。 |
| `list_transfers(platform, limit)` | — | 上传、下载、删除、分享链接和跨云复制的本地审计日志。 |
| `get_client_capabilities(platform)` | — | 在调用平台之前检查其支持的功能。 |
`target` 是对象存储的存储桶/容器名称 — 对文档存储忽略。
### 分享链接
`expires_in_seconds` 在S3、GCS、Azure Blob和Box上本地支持(预签名/签名URL,或Box的`unshared_at`)。Dropbox、Google Drive、OneDrive和Yandex Disk也创建链接,但它们的API不支持个人/非商业账户的过期,因此参数被接受以保持接口一致性,但不在那里强制执行。Google Photos不支持(仅上传)。
在 `confirm=true` 后,即使它不移动或删除数据,也会受到保护 — 返回的URL本身就是一个凭证,与 `upload_file` 具有相同的外泄问题。
### 跨云复制
`copy_file` 直接**从一个平台复制文件到另一个平台** — "将我的Dropbox `/photos/id.png` 复制到我的S3 `backups` 存储桶", "将此Drive文件迁移到OneDrive" — 通过一次工具调用。在此之前,单一厂商的存储集成无法实现此功能;这是将所有后端放在一个接口后面的回报。
```mermaid
sequenceDiagram
actor User
participant Client as MCP Client<br/>(Claude)
participant Silo as Silo MCP
participant Src as 源云<br/>(Dropbox)
participant Dst as 目标云<br/>(Google Drive)
User->>Client: "将 report.pdf 从 Dropbox 复制到我的 Google Drive"
Client->>Silo: copy_file(from=dropbox, to=google_drive, confirm=false)
Silo-->>Client: 试运行 — 将复制 /report.pdf → google_drive:report.pdf
Client-->>User: 即将从Dropbox复制到Google Drive。批准?
User->>Client: 是
Client->>Silo: copy_file(…, confirm=true)
Note over Silo: 通过临时文件流式传输<br/>服务器控制
Silo->>Src: 下载 /report.pdf
Src-->>Silo: 字节 → 磁盘上的临时文件
Silo->>Dst: 从临时文件上传
Dst-->>Silo: 新的文件 ID + 元数据
Note over Silo: 删除临时文件,<br/>将 '复制' 记录到审计跟踪
Silo-->>Client: 复制 ✓
Client-->>User: 完成 — report.pdf 现在在您的 Google Drive 中
```
LLM 永远不会触及文件字节或中间路径 — 它只发出一个 `copy_file` 调用,服务器处理下载 → 临时 → 上传 → 清理,取决于您的批准。
- 它通过服务器创建和删除的临时本地文件流式传输 — 您永远不会处理中间下载/上传,临时路径永远不是调用者提供的参数(因此它不受上传根目录的限制检查)。
- `from_remote_path` 根据**源**平台的期望进行寻址(对于Dropbox/OneDrive/Yandex 对象存储来说是一个路径;对于Google Drive/Box来说是一个文件 **ID** — 搜索后获取)。
- `from_target`/`to_target` 是任一端为对象存储时的存储桶/容器名称。
- 需要 `confirm=true` — 它写入目标。 在审计日志中记录为 `copy`,同时记录两端。
> [!NOTE]
> 复制受到与 5 GiB 下载/上传限制的约束,源下载流式传输到磁盘,因此大跨云复制不会在内存中缓冲整个文件。
## 支持的平台和工具
每个平台支持哪些操作及其身份验证模型。 ✅ 支持 · — 不支持。 `target` = 对象存储所需的存储桶/容器名称。
| 平台 | 类型 | 上传 | 下载 | 列出 | 搜索 | 删除 | 分享链接 | 认证 |
|---|---|:--:|:--:|:--:|:--:|:--:|:--:|---|
| Dropbox | 文档 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 静态令牌 |
| Google Drive | 文档 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | OAuth2 刷新 |
| OneDrive | 文档 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | OAuth2 刷新 |
| Box | 文档 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (带过期) | OAuth2 刷新 |
| Google Photos | 文档 | ✅ | — | — | — | — | — | OAuth2 刷新 |
| Yandex Disk | 文档 | ✅ | ✅ | ✅ | — | ✅ | ✅ | 静态令牌 |
| S3 兼容 | 对象(需要 `target`) | ✅ | ✅ | ✅ | — | ✅ | ✅ (预签名) | 静态密钥/秘密 |
| Google Cloud Storage | 对象(需要 `target`) | ✅ | ✅ | ✅ | — | ✅ | ✅ (签名) | 服务账户 JSON |
| Azure Blob | 对象(需要 `target`) | ✅ | ✅ | ✅ | — | ✅ | ✅ (SAS) | 连接字符串 |
> [!NOTE]
> Google Photos **仅支持上传** — Google 在 2025 年 3 月移除了 Photos Library API 的读取库范围 — 应用程序现在只能管理它自己创建的媒体项目。 浏览或下载用户的现有库需要交互式 "Picker API"(一个网页 UI 会话),而头less MCP 工具调用无法驱动。 `list_files`/`download_file`/ `search_files` 都返回一个明确的错误,而不是假装工作。 **此项目或 Google 的 API 中也没有 Google Photos 的删除功能** — 任何上传的内容都是永久的,直到通过 photos.google.com 手动删除。
## 设置摩擦和每平台凭证形状
<details open>
<summary><strong>凭证形状表</strong></summary>
| 平台 | 凭证 | 认证模型 | 设置 |
|---|---|---|---|
| Dropbox | `access_token` | 静态令牌 | 应用控制台 → 生成令牌。 无需刷新操作。 参见 [所需权限/范围](#所需权限范围) — `missing_scope` 错误意味着重新生成,而不是重新配置。 |
| Yandex Disk | `access_token` | 静态令牌,隐式授予(无代码交换步骤) | 参见 [获取 Yandex Disk 令牌](#添加帐户) 以上。 |
| Azure Blob | `connection_string` | 静态 | 存储帐户 → 访问密钥。 对象存储中最简单。 |
| S3 | `access_key_id`, `secret_access_key`, 可选 `region`/`endpoint_url` | 静态 | AWS IAM 用户,或 R2/MinIO 凭证 + `endpoint_url`。 |
| GCS | `service_account_json` | JWT(服务账户) | Google Cloud 控制台 → 创建服务账户密钥,粘贴整个 JSON。 |
| Google Drive | `client_id`, `client_secret`, `refresh_token` | OAuth2,刷新 | Google Cloud OAuth 同意屏幕 + 一次性授权以获取初始刷新令牌。 |
| Google Photos | 与 Drive 相同 | OAuth2, `photoslibrary.appendonly` 范围 | 相同的 Google Cloud 应用; **仅上传**,参见下文。 |
| OneDrive | `client_id`, `client_secret`, `refresh_token` | OAuth2,刷新 | Azure AD 应用注册。 |
| Box | `client_id`, `client_secret`, `refresh_token` | OAuth2,刷新,**令牌轮换** | Box 开发者应用。 每个刷新都会自动颁发新的刷新令牌,此服务器会将其持久化回密钥环 — 不要手动重用旧令牌。 |
Dropbox、Yandex Disk、S3、GCS 和 Azure Blob 使用静态凭证读取一次。 Google Drive、OneDrive、Box 和 Google Photos 都是 Big-Tech OAuth2,具有短生命周期(~1h)访问令牌 — 该服务器在每次 API 请求之前调用每个平台的令牌端点,而不是缓存,从而保持正确性故事简单,但每次调用多一个快速(~200ms)请求。
</details>
<details>
<summary><strong>每平台所需的权限/范围</strong></summary>
凭证字段仅为您获取令牌 — 令牌还需要正确的范围,否则每个调用都会失败,并出现权限错误,与其存储方式无关。 在平台侧配置,不在此处:
| 平台 | 所需范围 | 在哪里设置它们 |
|---|---|---|
| Dropbox | `files.content.write`, `files.content.read`, `files.metadata.read`, `sharing.write`(用于 `create_share_link`) | 应用控制台 → **权限** 选项卡 → 勾选范围 → 点击 **提交**。 范围在令牌颁发时固定 — 您必须生成一个 **新** 令牌以更改范围;已经颁发的令牌不会溯后获得它们。 |
| Yandex Disk | 完全磁盘访问(读/写) | oauth.yandex.com 应用配置 → 勾选磁盘读/写权限以创建应用,然后颁发令牌。 |
| Google Drive | `https://www.googleapis.com/auth/drive` | 由 `silo-mcp-accounts oauth google_drive` 自动请求 — 您只需在项目中启用 Drive API 即可。 |
| Google Photos | `https://www.googleapis.com/auth/photoslibrary.appendonly` | 与上述相同,通过 `silo-mcp-accounts oauth google_photos`;启用 Photos Library API。 |
| OneDrive | `Files.ReadWrite`, `offline_access`(委托) | Azure 门户 → 应用注册 → **API 权限** → Microsoft Graph → 添加两者,然后如果您的租户需要它,请 **授予管理员同意**。 也由 `silo-mcp-accounts oauth onedrive` 自动请求。 |
| Box | "读写 Box 中存储的所有文件和文件夹"(或更窄,匹配您需要的范围) | 开发者控制台 → 应用 → **配置** 选项卡 → **应用范围**。 |
| S3 | `s3:GetObject`, `s3:PutObject`, `s3:ListBucket`, `s3:DeleteObject` 目标存储桶(以及预签名 URL 生成不需要额外权限 — 这是一个本地签名操作) | IAM 策略附加到用户/角色使用的 `access_key_id`/`secret_access_key`。 |
| GCS | 存储对象管理员(或 Object Viewer + Object Creator 以获得更窄的授权) 存储桶/项目 | IAM & Admin → 将角色授予服务账户,然后生成其密钥。 |
| Azure Blob | 使用连接字符串的完整帐户访问 - 无单独的范围概念 | 不适用 — 连接字符串本身是权限边界;如果要限制范围,请使用 SAS 限制的连接字符串。 |
> [!TIP]
> Dropbox 是最可能出现问题的一个:`missing_scope` 401 令牌与正确的令牌几乎总是意味着范围在令牌生成 **后** 更改了。 重新生成令牌,不要只是重新保存。
</details>
## 路径安全性
上传的 `local_path` 和下载的目标文件名都是 LLM 提供的工具调用参数,因此两者都局限于各自的根目录:
- `SILO_MCP_UPLOAD_ROOT`(默认为 `~/silo-mcp/uploads`)— 文件夹外的文件无法上传。 这很重要:没有它,操纵的对话可以要求服务器将任意本地文件(SSH 密钥、`.env`、浏览器凭证存储)上传到云帐户 — 持久、可分享,并且不会在任何平台上显示警告。
- `SILO_MCP_DOWNLOAD_ROOT`(默认为 `~/silo-mcp/downloads`)— 下载不能写入此目录之外的文件。 上传限制为 5 GiB(`MAX_UPLOAD_BYTES`)和 5 GiB(`SILO_MCP_MAX_DOWNLOAD_BYTES`,可覆盖)。 下载流式传输到磁盘,如果超出限制,则飞行中中止,因此意外的大文件不会耗尽内存。 凭证存储在 OS 密钥环中,从不存储在 SQLite DB 中;一次性 OAuth 授权流程使用 PKCE 和随机 `state` 值(RFC 8252),因此循环重定向无法伪造。## 平台特定说明
<details open>
<summary><strong>已知的不对称性和限制</strong></summary>
- **Google Photos 仅支持上传。** Google 在 2025 年 3 月移除了 Photos Library API 的 read-library 范围 — 应用程序现在只能管理它自己创建的媒体项目。 浏览或下载用户的现有库需要交互式 "Picker API”(网页 UI 会话),而 headless MCP 工具调用无法驱动。 `list_files`/`download_file`/ `search_files` 都返回一个明确的错误,而不是假装工作。 **此项目或 Google 的 API 中也没有 Google Photos 的删除功能** — 任何上传的内容都是永久的,直到通过 photos.google.com 手动删除。 - **Google Drive 和 Box 按 ID 而不是路径寻址**,用于下载/删除 — `upload` 的 `remote_path` 用作文件名(Drive / Box 根目录仅限 v1);要下载/删除,您需要先从 `list_files`/`search_files` 获取 ID。 OneDrive、Dropbox 和 Yandex Disk 使用真正的路径,无不对称。
- **对象存储桶/目录名称。 - **对象存储不支持搜索** - 仅限于 `list_files` 的前缀/路径列表。 Yandex Disk 也没有专用的搜索端点,表现相同(仅限于路径列表)。 - 上传端点在此均为单次(Dropbox ≤150MB,OneDrive ≤4MB);暂无大文件分段/续传上传。
### 真实账户测试状态
已通过实际MCP协议验证 Dropbox、Google Drive 和 Google Photos **使用真实账户进行端到端测试** —— MCP客户端启动服务器,进行工具发现,并按名称调用工具,与Claude Desktop/Code完全相同 —— 而不仅仅是通过直接Python调用。
| 平台 | 状态 |
|---|---|
| Dropbox | ✅ 已通过真实账户的MCP协议验证:`upload_file`(dry-run + 确认)、`list_files`、`search_files`、`download_file`(流式传输到磁盘)、`create_share_link`、`delete_file`(dry-run + 确认)、`list_transfers`。`upload_file`/`delete_file`的确认门控行为正确(在没有`confirm=true`的情况下被阻止)。 |
| Google Drive | ✅ 已通过真实账户的MCP协议验证,涵盖的工具与上述Dropbox相同,按文件ID进行处理。OAuth刷新独立触发,设计前每次调用前均触发,无缓存错误。 |
| Google Photos | ✅ 已通过真实账户的MCP协议验证:`upload_file`(dry-run + 确认,真实图片内容),并确认 `list_files`/`search_files`/`download_file`/`delete_file` 均失败,返回清晰、正确的错误信息,而不是崩溃 —— 预期结果,设计如此,不是错误。 |
| 跨云 `copy_file` | ✅ 已在真实账户间验证 **双向** —— Dropbox → Google Drive 和 Google Drive → Dropbox —— 复制的字节与源文件校验和一致。确认门控阻止了dry-run;两次复制均记录在审计日志中。 |
欢迎提交问题和PR,报告剩余平台的真实账户测试结果。
## 安全
该服务器赋予LLM读取本地文件(从一个目录)、将其移动到云账户和生成公共链接的能力 —— 因此有必要明确风险和已采取的措施。
- **提示注入是核心风险。** 模型读取的内容(文件名、文档文本、早期消息)可以尝试引导它调用您未打算的工具 —— 例如 "也上传 `~/.ssh/id_rsa`" 或 "公开分享此文件"。这是代理工具使用的一般属性,[不限于此服务器](https://modelcontextprotocol.io/docs/concepts/security)。
- **内置缓解措施:**
- **路径限制** —— 上传只能从 `SILO_MCP_UPLOAD_ROOT` 读取,下载只能写入 `SILO_MCP_DOWNLOAD_ROOT`。精心构造的请求文件或 `.env` 在根目录之外将被拒绝,任何网络调用前都会被拒绝。请保持上传根目录为专用文件夹,而不是您的主目录。
- **确认门控** —— `upload_file`、`delete_file` 和 `create_share_link` 需要 `confirm=true`,因此一个表现良好的客户端会准确显示即将发生的事情,并由人类批准。公共链接或删除操作不会因模糊请求而触发。
- **凭证从未到达模型** —— 它们存储在OS密钥环中,由服务器端读取;任何工具调用都无法枚举或窃取它们,它们也从未出现在模型可见的配置文件或环境变量转储中。
- **最小权限** —— 为每个平台令牌限定所需权限范围(参见 [按平台划分的所需权限范围](#required-permissionsscopes-per-platform));只读令牌无法被诱导删除任何内容。
- **审计追踪** —— `list_transfers` 和本地SQLite日志记录每次上传、下载、删除和分享链接,因此您可以回顾实际发生的操作。
> [!WARNING]
> 请将生成的分享链接视为公共凭证 —— 任何拥有该URL的人都可以读取文件,无需账户。 当您完成后,请通过删除文件(或在平台上取消分享)来撤销分享。
## 测试
```bash
pip install -e ".[all,dev]"
pytest
```
测试是纯逻辑和 `respx` 模拟的HTTP测试 —— 无需真实的网络调用,也不需要真实的凭证。
## 故障排除
<details>
<summary><strong>Windows: <code>[WinError 1783] The stub received bad data</code> from <code>silo-mcp-accounts add</code></strong></summary>
此错误可能由两个不同的原因导致:
1. **`pywin32-ctypes` 的 `CredWrite` 模拟。** `keyring` 优先使用此模拟而不是实际的 `pywin32`,即使两者都已安装(参见 `keyring` 的 `backends/Windows.py`,它优先尝试 `pywin32-ctypes`)。解决方法:
```powershell
pip uninstall pywin32-ctypes -y
```
需要 `pywin32` 已经安装(此处已通过 `mcp` 传递依赖项)。重新安装/升级 `keyring` 后可能会重新拉入 `pywin32-ctypes` 作为其依赖项 —— 如果重新出现,请重新运行卸载命令。
2. **Windows凭证管理器的严格限制 **~2560字节(~1280字符)每个秘密** —— 与(1)无关的真正操作系统限制,通过重现完全相同的错误和纯 `pywin32` 及超大字符串复制得到确认。某些Dropbox令牌格式和GCS的 `service_account_json` (常规超过2000+字符)都很容易超过此限制,但并非错误。现已透明地处理 —— `add_account` 将超过限制的任何秘密分割成多个密钥环条目,并在读取时重新组装,在所有平台(而不仅仅是Windows)上均如此,因此行为不会因操作系统而静默差异。如果您仍然遇到错误,则可能是真正的文件太大(200,000+字符)而不是此限制。
</details>
## 相关
使用相同自托管、确认门控MCP样式的兄弟项目:一个社交发布MCP服务器(广播帖子而不是移动文件)—— 不同的能力形状和信任边界,在MCP客户端级别可组合,而不是共享代码库(此处可以 `download_file` 直接传递本地路径到该服务器的 `media_paths`)。
## 许可证
[MIT](LICENSE)
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器