内容
# mcpelevator
**将 MCP 服务器提升为身份验证的 HTTP 端点。自我托管,一个容器。**
大多数 [MCP](https://modelcontextprotocol.io) 服务器作为 **stdio** 程序提供(`npx -y …`、`uvx …`、命令、Docker 镜像)。 Stido 仅在客户端可以本地启动进程时才有效,而 **手机和大多数“任意设备”设置无法做到这一点**。 mcpelevator 为您运行这些服务器,并将每个服务器公开为远程 **可流式 HTTP** 端点(Claude 移动版、Flutter 客户端等连接到的传输)。添加服务器,按下启动,复制 URL 到您的客户端。(每个服务器的 REST/OpenAPI 接口正在规划中,尚未提供。)
已经远程的服务器也可以:指向 mcpelevator 现有的 Streamable-HTTP/SSE MCP URL(`remote` 运行器),它将该上游代理到与本地服务器相同的身份验证、监督和每个客户端复制菜单后面 - 在远程服务器前面添加持有者身份验证或为每个客户端提供一致的端点非常方便。
协议桥接由 [FastMCP](https://gofastmcp.com) 完成;mcpelevator 是 **控制平面**:围绕它的干净 UI、进程监督、安全性和入职。
## 工作原理
```
[Claude 移动版 / 任意 MCP 客户端] ──可流式 HTTP──┐
▼
FastAPI ─ / SvelteKit SPA (一个容器,一个端口)
─ /api/* 控制平面(SQLite 中的 SSOT)
─ /s/<slug>/mcp 反向代理 ─┐ 身份验证 + 主机/来源在此强制执行
▼
每个启用的服务器:1 个受监督的桥接进程(自己的循环事件循环端口)
FastMCP 代理(stdio 命令 - 或远程 HTTP/SSE URL)→ 可流式 HTTP ← 故障隔离,真实 PID/日志
```
协调器将正在运行的进程收敛到 SQLite 中的期望状态(Kubernetes 风格),因此系统是幂等的,并且在重启后仍然存活。
## 截图
添加服务器,按下启动,复制 URL。仪表板监督每个服务器 - 实时状态,每个服务器的错误,以及用于端点的复制菜单。

<table>
<tr>
<td width="50%"><img src="docs/screenshots/catalog-browse.png" alt="浏览注册表"><br><b>浏览注册表</b> - 搜索公共 MCP 目录。</td>
<td width="50%"><img src="docs/screenshots/catalog-search.png" alt="在注册表中搜索服务器"><br><b>搜索和安装</b> - 选择版本,一键安装。</td>
</tr>
<tr>
<td><img src="docs/screenshots/add-server.png" alt="添加服务器表单"><br><b>添加服务器</b> - npx、uvx、命令或远程 URL。</td>
<td><img src="docs/screenshots/settings.png" alt="设置 - 访问令牌和网络安全"><br><b>设置</b> - 访问令牌、绑定模式和局域网门户。</td>
</tr>
</table>
<details>
<summary>移动版</summary>
<img src="docs/screenshots/servers-mobile.png" alt="在手机宽度视口上的服务器仪表板" width="360">
</details>
## 快速入门(Docker)
```bash
docker compose up --build
# 打开 http://127.0.0.1:8080
```
镜像是包含电池的(Node/npx + Python/uv 预安装),因此 `npx`/`uvx` 服务器无需额外设置即可运行。数据(SQLite + 包缓存)在 `mcpe-data` 卷中持续存在。默认情况下,端口仅发布到主机环回地址。参见 **安全**。
## 快速入门(Unraid)
一个社区应用模板位于
[pacnpal/unraid-templates](https://github.com/pacnpal/unraid-templates)
(`mcpelevator.xml`)。它提供了推荐的自托管盒设置开箱即用:
**主机网络**(因此 LAN 门户看到真实的客户端 IP),应用程序数据持久存储到
`/mnt/user/appdata/mcpelevator`,并且 `MCPE_ALLOW_PRIVATE_LAN=true`,因此无头盒
可以从您的 LAN 在第一次启动时访问 - 管理令牌打印一次到
容器日志中,供您使用它登录。完整的操作指南(安装,首次登录,更新,
故障排除):[docs/unraid.md](docs/unraid.md)。
## 快速入门(本地开发)
```bash
# 后端(控制平面)在 http://127.0.0.1:8080
cd backend && uv sync && uv run uvicorn app.main:app --reload
# 前端(HMR)在 http://localhost:5173,代理 /api 和 /s 到 :8080
cd frontend && npm install && npm run dev
```
或者使用 `Makefile`:`make dev-backend`、`make dev-frontend`、`make build`、`make test`、`make docker`。
## 添加服务器
通过 API(UI 添加流程包装此 API):
```bash
curl -X POST http://127.0.0.1:8080/api/servers -H 'content-type: application/json' -d '{
"name": "内存", "runner": "npx", "command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"], "enabled": true
}'
```
然后将任意 MCP 客户端指向 `http://127.0.0.1:8080/s/memory/mcp`。
**已经远程?** 使用 `remote` 运行器代理现有的 Streamable-HTTP/SSE MCP URL - 无本地进程。启动规范重用相同的字段:`command` 是上游 URL,`args[0]` 是传输(`streamable-http` 或 `sse`),而 `env` 是上游 HTTP 头。
```bash
curl -X POST http://127.0.0.1:8080/api/servers -H 'content-type: application/json' -d '{
"name": "远程 MCP", "runner": "remote",
"command": "https://example.com/mcp", "args": ["streamable-http"],
"env": {"Authorization": "Bearer <上游令牌>"}, "enabled": true
}'
```
粘贴具有远程条目的 `mcpServers` 配置现在将它们导入为 `remote` 服务器而不是跳过:`url`(或 Gemini CLI 的 `httpUrl`)成为上游,而 `type` / `transport` 字段选择传输(默认为 `streamable-http`)。
## 从注册表安装(目录)
不知道包名?**浏览**为您找到服务器。目录搜索公共
MCP 目录并解析所选服务器到启动规范,您可以审查并安装 - 无需
手动输入 `npx -y …`。
- **MCP 注册表**(`registry.modelcontextprotocol.io`)- 官方目录。它的
服务器带有结构化包,因此 npm → `npx` 和 pypi → `uvx` 自动派生
并固定到最新版本(每个卡的下拉列表选择较旧的版本):**一键安装**。
- **Glama**(`glama.ai`)- 一个更大、精选的 **发现** 目录。它不发布
启动命令,因此安装会打开预填充名称 + 所需环境变量键 + 存储库链接的审查表单,供您完成。
**按类型过滤** - 将浏览列表缩小到一个或多个包/注册表类型(`npm`,
`pypi`,`oci`,`nuget`,`mcpb`,`remote`)使用类型芯片。发布远程(HTTP/SSE)端点的服务器可安装为代理的 **`remote`** 服务器:安装将端点的声明头带入审查表单(需要填写的头)以便您在启动前填写上游身份验证。
打开 **浏览** 在标题(或 `/catalog`)。后端代理目录
(`GET /api/catalog/servers`,`GET /api/catalog/server`)以便 SPA 保持同源;
安装发布审查草稿到 `POST /api/servers`,标记为 `source=catalog:<id>`。
添加另一个目录是一个小插件:一个 `Source` 模块 + 一个行在源
注册表 - 参见 [`backend/app/catalog/README.md`](backend/app/catalog/README.md)。
## 配置(环境变量,前缀 `MCPE_`)
| 变量 | 默认值 | 含义 |
|---|---|---|
| `MCPE_HOST` | `127.0.0.1` | 控制平面绑定(Docker 设置为 `0.0.0.0`) |
| `MCPE_PORT` | `8080` | 控制平面端口 |
| `MCPE_PUBLIC_BASE_URL` | _(派生)_ | 客户端使用的绝对 URL(在隧道后面设置) |
| `MCPE_TRUSTED_PROXIES` | _(无)_ | 其对等 IP 地址计为环回地址的主机(反向代理 / Docker 桥接网关) |
| `MCPE_TRUST_DOCKER_HOST` | `false` | 自动检测容器的默认网关(Docker 主机)并信任它用于主机防护,无需硬编码网关 CIDR。选择加入:仅在 **环回** 公开端口(`-p 127.0.0.1:8080:8080`)时安全 - 在用户态代理下,`0.0.0.0` 发布将网关呈现为对等地址,因此启用它信任 LAN 流量。仅环回地址允许;持有者令牌门户仍然适用 |
| `MCPE_ALLOWED_HOSTS` | _(无)_ | 额外主机名的逗号分隔列表,由主机/来源防护始终信任(类似于 `MCPE_PUBLIC_BASE_URL` 主机,用于其他来源)。设置它在 `auto` 下打开控制平面身份验证(该框可通过该主机名访问) |
| `MCPE_ADMIN_TOKEN` | _(无)_ | 破坏性控制平面令牌,在 `/api` 上始终接受 |
| `MCPE_MINT_ADMIN_TOKEN` | `false` | 强制在启动时生成新的管理令牌并打印它(恢复丢失的令牌);在获取后取消设置 |
| `MCPE_ALLOW_PRIVATE_LAN` | `false` | 自托管框的首次启动种子(无头启动);参见 **安全** |
| `MCPE_DATA_DIR` | `./data` | SQLite + 缓存 |
| `MCPE_FRONTEND_DIR` | `../frontend/build` | 构建的 SPA 以提供服务 |
| `MCPE_PORT_RANGE_START` / `_END` | `49200` / `49400` | 桥接进程的环回端口 |
| `MCPE_MAX_RUNNING` | `50` | 并发运行服务器的上限 |
| `MCPE_START_TIMEOUT_S` | `120` | 就绪超时(涵盖 npx/uvx 冷启动) |
## 安全
> **是否希望通过 Internet 公开此内容 - 例如,从 Claude 访问?** 参见
> [docs/claude-web-exposure.md](docs/claude-web-exposure.md)
> 获取两个具体的、安全的方法:**路径 A**(claude.ai **网页/移动版**)-
> Cloudflare 隧道和 Cloudflare Access 自托管应用,具有 **托管 OAuth**,
> 由于网页/移动版和 Desktop 的账户 UI **远程连接器** 仅限 OAuth,
> 并且无法发送持有者;和 **路径 B**(Claude **代码** / **本地配置的**
> 桌面版)- 公共 HTTPS 隧道和 mcpelevator 的内置 **持有者** 身份验证。
> 指南包含确切的 `cloudflared` / 访问步骤,`curl` 检查和
> 连接器注意事项,在依赖网页/移动版之前进行测试。
> **完整威胁模型。** 有关信任边界、攻击故事以及
> 发现按严重程度分类的发现,请参阅 [docs/security.md](docs/security.md)。
两个独立的层保护系统,并且请求必须通过两者。
**主机/来源允许列表**(DNS 重新绑定防御),在每种模式下的每个请求上强制执行。环回 `主机` 仅在请求的 **对等方** 实际从环回地址连接时才被信任,因此远程绑定无法伪造 `主机:localhost`。`公开` 模式添加您允许的主机,并且 `MCPE_PUBLIC_BASE_URL` 中的主机始终被信任。在本地反向代理或 Docker 的桥接网关后面(其中对等方是转发器,而不是真正的客户端),设置 `MCPE_TRUSTED_PROXIES`(CIDR)以信任它。默认的 `docker-compose.yml` 为桥接范围执行此操作,只有环回端口发布才安全。
**本地网络(LAN)访问** - 对于一个自托管盒(Unraid,一个 NAS,一个家庭服务器),您希望从同一网络上的手机或笔记本电脑访问,打开 **允许从本地网络设备访问**(`allow_private_lan` 设置 - 设置页面或 `PATCH /api/settings`)。它允许 `主机` 是 **私有 IP 字面值**(例如 `http://192.168.1.50:8080`)的请求通过防护 **当连接的对等方本身在私有网络上** - 没有每个主机允许列表,并且没有 DNS 重新绑定漏洞,因为重新绑定攻击会提供攻击者的 *域名* 在 `主机` 头中,而不是一个裸私有 IP 字面值。只有 IP 字面值符合条件; 解析为 LAN 地址的主机名仍然被拒绝。绑定套接字在主机外:Docker 镜像已经绑定 `0.0.0.0`,但 **源代码安装** 必须使用 `--host 0.0.0.0` 启动 uvicorn(`uvicorn app.main:app --host 0.0.0.0`)- `MCPE_HOST` 仅提供派生的 URL,它不会移动开发服务器绑定。因为实例现在可以从主机外访问,因此在 `auto` 下启用 LAN 访问会打开控制平面身份验证(因此 `/api` 需要一个管理令牌)。它 **默认关闭**。
*第一次进入*(令牌与访问的鸡和蛋问题):在新安装时,`/api` 从 **环回** 无令牌开放,因此最简单的路径是在盒子上本身生成管理令牌 - 或通过 SSH 隧道(`ssh -L 8080:127.0.0.1:8080 you@box`,然后打开 `http://localhost:8080`)- 它登录浏览器,然后 **打开 LAN 访问**。对于 **无头** 盒子,没有环回浏览器,设置 `MCPE_ALLOW_PRIVATE_LAN=true`(和可选的 `MCPE_ADMIN_TOKEN`):它在第一次启动时播种设置,并且因为这会打开控制平面身份验证,所以启动引导程序会生成一个管理令牌并 **将其打印一次到容器日志**(`docker compose logs`,或 Unraid 的日志查看器)供您使用它从 LAN 登录。环境变量仅播种初始值 - 设置切换在此之后是权威的。
**每个请求的持有者身份验证**,在两个平面上:
- **代理数据平面**(`/s`)使用可插拔的每个服务器身份验证提供程序。 v1 提供 `none` 和 `bearer`(SHA-256 哈希令牌); 设置为 `bearer` 的服务器需要在 `Authorization: Bearer <token>` 中有一个令牌。令牌默认授权每个受保护的服务器,或者您可以在创建令牌时将其范围限定为单个服务器。
- **控制平面**(`/api`)需要具有 `control` 范围的管理令牌。执行按照 `control_plane_auth` 设置:`auto`(默认)在 `bind_mode=expose` 或设置 `MCPE_PUBLIC_BASE_URL` 时需要它(无论哪种方式,实例都可以从主机外访问),因此纯本地安装保持零配置;`always` 始终需要它,即使在环回地址上。 `/api/health`(控制平面活跃度),`/api/health/{slug}` 和 `/api/health/summary`(每个服务器的就绪度,用于负载均衡器),以及 `/api/auth/status` 保持公开。
当强制执行控制平面身份验证时,SPA 显示登录屏幕。管理令牌打印一次到容器日志中在第一次启动时(查找“控制平面身份验证开启”),并且设置页面可以生成一个(它立即登录)。要从 UI 切换到 `expose` 或 `always`,您必须先生成一个管理令牌,因此您不会将自己锁定在外。
`MCPE_ADMIN_TOKEN` 是一个破坏性凭证:当设置时,它始终在 `/api` 上被接受。使用它来恢复丢失的令牌,或用于 CI 和自动化。生成的令牌仅显示一次(仅存储其哈希),因此如果您丢失它并且没有设置 `MCPE_ADMIN_TOKEN`,请设置该变量并重新启动以进入,然后生成一个新的令牌。或者,设置 `MCPE_MINT_ADMIN_TOKEN=true` 并重新启动:引导程序会生成一个新的控制令牌并将其打印到日志中(现有的令牌继续有效)- 在以后取消设置该变量,以免每次重启都生成一个新的令牌。
`docker` 运行器(启动 MCP 服务器的 Docker 镜像)是 **选择加入** 和 **等同于 root**,里程碑 **M7**。
## 项目布局
```
backend/app/ FastAPI 控制平面,监督程序,桥接主机,运行器,身份验证,代理,目录
frontend/ SvelteKit(Svelte 5)SPA,适配器静态
Dockerfile 多阶段:构建 SPA → Python+Node+uv 运行时
```
## 状态/路线图
**今天有效的工作:** 添加服务器(引导表单,粘贴 `mcpServers` 配置 - stdio 或远程,或 **浏览注册表** 并一键安装),监督它,并通过 Streamable HTTP 从任何 MCP 客户端使用它。每个服务器的详细信息包含 **实时日志流**,配置和已发现的工具; 编辑/克隆/删除/启动/停止。**克隆** 一个服务器,以一键方式启动类似配置的副本,并且 **重命名服务器的 slug** 以重新指向其 `/<slug>/` URL(指向旧 slug 的客户端需要重新指向)。**每个客户端的复制** 菜单按生态系统分组 - Claude Code,Claude Desktop(通过 `mcp-remote`),Claude 网页/移动连接器,Codex,ChatGPT 连接器,Gemini CLI,VS Code,通用 `mcpServers` 和原始 URL。运行器:`npx`,`uvx`,`command` 和 `remote`(代理现有的 Streamable-HTTP/SSE MCP URL)。**目录** 浏览具有 **按类型过滤**(npm/pypi/oci/nuget/mcpb/remote)和一键安装,包括远程端点。**身份验证**:用于 `/s` 的持有者令牌(每个服务器作用域为所有服务器或一个),用于 `/api` 的控制平面持有者身份验证,具有管理登录,一个主机/来源允许列表(设置)用于安全暴露,以及一个选择加入的 LAN 访问切换,用于自托管盒。
**计划中:** 每个服务器的 REST/OpenAPI 接口 · `docker` 运行器(将解锁 OCI 目录安装)· 更多目录 ·润饰。