内容
# ClaudeNasConnector
> 为 Claude 提供一个 Hermes 风格的代理之家 — 在您自己的 NAS 上。
自托管 [MCP](https://modelcontextprotocol.io) 服务器,将您的 NAS 转变为**个人 Claude 连接器**。类似于 [Hermes](https://hermes-agent.nousresearch.com) 等代理框架,它为您的助手提供持久的身份和真正的覆盖范围 — 但它运行在**您的**网络中,并直接连接到您已经使用的 Claude 应用程序。
添加一次作为*自定义连接器*,Claude 获得:
- 🧠 **一致的记忆**,存活在您的 NAS 上,并在所有设备上跟踪您
- 📱 **随时随地工作** — 相同的脑袋在桌面端**和**移动端,一个账户,一个状态
- 🗂️ **技能路由器** — 您的技能存活在 NAS 上;Claude *搜索* 它们,加载正确的一个(渐进式披露),并在运行时 *学习* 新的技能(`skill_write`)
- 🛠️ **工具即数据** — 使用 `service_add` 注册任何 API,并通过 `call_service` 调用它;新的集成不需要代码,也不需要重新部署
- 🔐 **加密的秘密保险箱** — 通过连接器存储 API 密钥/令牌(从移动端工作);在休息时加密,永远不会显示回来
- 🧭 **自我描述** — 任何连接的 LLM 接收使用说明 + 一个 `guide` 工具,并被告知在物理/出站操作之前确认
- 🤝 **多代理就绪** — 共享内存 + 注册表,以便多个代理可以共享一个大脑
模型保留在 Anthropic 的云中。**您的数据、技能和秘密保留在您的 NAS 上。** Claude 通过 HTTPS 连接器与此服务器通信;服务器在内部使用您的本地凭证,并且永远不会将它们交给模型。
> ✅ **状态:工作中。** 内存、技能路由器、通用服务调用器、加密秘密保险箱和 OAuth(通过您自己的 OIDC 提供商)都已上线 — 而且连接器是*自我描述的*。**在没有 [身份验证](#authentication) 的情况下不要公开暴露它。**
## 工作原理
```
Claude 应用程序(桌面端/移动端) · 一个或多个代理
│ 自定义连接器(HTTPS,从 Anthropic 的云)
▼
反向代理(Zoraxy / Caddy / nginx / Traefik …)
│
▼
ClaudeNasConnector (此容器,在您的 NAS 上)
│ 使用本地文件和秘密
▼
内存 · 技能(可搜索) · 服务和 API · 秘密保险箱
```
## 功能(工具一览)
| 组 | 工具 | 功能 |
|-------|-------|--------------|
| 健康 | `ping` | 连接性检查 |
| 内存 | `memory_write` · `memory_read` · `memory_list` · `memory_search` · `memory_delete` | NAS 上的持久、作用域命名空间事实 |
| 技能 | `skill_search` · `skill_list` · `skill_load` · `skill_resource` · `skill_write` | 可搜索的知识;运行时学习新技能 |
| 服务 | `service_add` · `service_list` · `call_service` | 注册和调用任何 API 作为数据 |
| 秘密 | `secret_set` · `secret_list` · `secret_delete` | 加密保险箱;值永远不会返回 |
| 指南 | `guide` | 自我描述(也作为连接时的服务器 `instructions` 发送) |
新功能作为**数据**(技能、服务配置、秘密)添加 — 而不是代码,无需重新部署。
## 项目结构
```
ClaudeNasConnector/
├── app/ # 服务器代码(FastMCP)
│ ├── server.py # 入口点 — 连接身份验证 + 注册工具模块
│ ├── memory.py # 内存工具
│ ├── skills.py # 技能路由器
│ ├── services.py # 通用允许列表服务调用者
│ ├── secrets_store.py# 加密秘密保险箱
│ ├── guide.py # 自我描述使用指南(DE/EN)
│ └── requirements.txt
├── data/ # 持久的、人类可读的状态(git 忽略的内容)
│ ├── memory/ # 内存文件 — Claude 记得关于你的什么
│ ├── skills/ # 技能库 — <skill>/SKILL.md 路由器搜索
│ ├── services/ # 服务配置(集成作为数据)
│ ├── vault/ # 加密秘密(secret_set)
│ ├── auth/ # OAuth 客户端注册(持久化)
│ └── work/ # 文件工作流 / 临时文件(CAD、导出、大文件)
├── secrets/ # 本地凭证(.env)— 永远不要离开 NAS
├── logs/ # 容器日志
├── docs/ # 架构和 Claude 项目指令模板
├── Dockerfile # 构建镜像(依赖项在构建时安装)
├── entrypoint.sh # 在运行时将特权降级为 PUID:PGID(gosu)
├── docker-compose.yml
└── .env.example
```
> **没有 `deps/` 文件夹?** 正确 — 依赖项在构建时**编译到镜像**中,因此没有安装启动的卷。`data/`、`logs/` 和 `secrets/` 文件夹通过 `.gitkeep` 保留其结构;它们的内容被 git 忽略,因此不会提交任何私有内容。
## 内存、技能和技能路由器
这是项目的核心 — 使 Claude *本身* 便携,而不仅仅是聊天。
- **内存** 以 `data/memory` 下的普通文件形式存在。工具(`memory_read` / `memory_write` / `memory_list`)让 Claude 回忆和更新它对你的了解 — 在每个设备上都相同。
- **技能** 以 `data/skills` 下的文件夹形式存在(`<skill>/SKILL.md` + 资源)。路由器工具 — `skill_search` / `skill_load` / `skill_resource` — 让 Claude 为请求找到合适的技能并拉入**仅需的内容**(渐进式披露,与工具搜索相同的理念)。
- **一次性连接。** 在您的 Claude **项目**中添加一个简短的指令,以便助手始终首先咨询路由器 — 请参阅 [`docs/claude-project-instructions.md`](docs/claude-project-instructions.md)。之后,“找到合适的技能/工具并应用它”就会发生,从任何设备。
## 工具和集成(作为数据)
新的集成不需要新的代码。**服务** 是一个小的配置,您在运行时使用 `service_add` 注册(存储在 `data/services` 下);然后 `call_service` 调用它 — 只有注册的服务被允许,并且身份验证令牌在服务器端从环境变量 (`token_env`) 中注入,永远不会存储在数据中或返回给模型。将服务与 `skill_write` 技能配对,解释如何使用它,并通过**无需重新部署**即可实现新的功能。
## 多代理就绪
设计允许多个代理共享一个 NAS 大脑而不会相互冲突:
- **命名空间内存** — 内存通过作用域寻址,因此代理共享公共知识同时保留私有笔记(`shared` 与每个代理)。
- **共享技能和工具注册表** — 每个代理查询相同的 `skill_search` 和工具集;添加一次功能,所有代理都可使用。
- **每个代理的工作空间** — `data/work` 下的隔离工作目录,用于并行任务。
- **(计划中)代理收件箱** — 代理到代理和代理到您的消息的追加通道,如 Hermes。
完整的编排(启动/协调子代理)正在路线图中;上述接口已就绪,以便它可以**无需重写**即可实现。参阅 [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)。
## 要求
- 运行 **Docker** (Compose v2)的 NAS 或服务器。
- 反向代理,通过公共 HTTPS 提供容器服务 — Claude 从 Anthropic 的云连接,因此端点必须可以从互联网访问。
- 指向您的代理的域名/子域名。
- 支持自定义连接器的 **Claude 计划** (免费版仅限一个;Pro/Max/Team/Enterprise 支持更多)。
## 快速入门
```bash
git clone git@github.com:IkarusMK/ClaudeNasConnector.git
cd ClaudeNasConnector
cp .env.example .env # 调整 PUID / PGID / HOST_PORT / TZ
docker compose up -d --build
```
MCP 端点在 `http://<host>:8787/mcp` 处提供服务。
### 暴露它并添加连接器
1. 将子域名(例如 `agent.example.com`)指向您的反向代理。
2. 将该主机代理到 `http://<nas-ip>:8787`,使用 HTTPS。
- 上游是**纯 HTTP** — 不要启用“到上游的 TLS”。
- 如果您的代理使用地理阻塞,**允许 Anthropic 的区域(美国)**,否则连接器无法到达您。
3. 在 Claude 应用程序中:**设置 → 连接器 → 添加自定义连接器** → URL `https://agent.example.com/mcp`。
4. 测试:要求 Claude 调用 `ping` 工具。
## 配置
所有配置都存活在 `.env` 中(从 `.env.example` 复制):
| 变量 | 默认值 | 描述 |
|-------------|---------|-------------|
| `HOST_PORT` | `8787` | 服务器发布的宿主端口(容器内部始终监听 `8787`) |
| `PUID` | `1000` | 进程运行的用户 ID(文件所有权) |
| `PGID` | `1000` | 进程运行的组 ID |
| `TZ` | `UTC` | 容器时区 |
## 身份验证
在公开连接器之前,请使用 OAuth 保护它。它使用**您自己的 OIDC 身份提供商**作为登录后端 — Pocket ID、Authentik、Keycloak、Auth0,任何具有标准 OIDC 发现功能的提供商。FastMCP 的 OIDC 代理处理 Claude 连接器所说的 MCP 端的 OAuth 2.1 流(动态客户端注册 + PKCE);您的提供商只需执行实际登录。
> ℹ️ **不要** 在 `/mcp` 端点前放置浏览器/转发身份验证(反向代理 SSO)。Claude 连接器是一个*机器*客户端,无法遵循交互式登录重定向。身份验证必须发生在 MCP 层,这正是此操作的作用。
通过在 `.env` 中设置以下内容来启用它(参见 `.env.example`):
| 变量 | 示例 |
|----------|---------|
| `OIDC_CONFIG_URL` | `https://id.example.com/.well-known/openid-configuration` |
| `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` | 从您在提供商中注册的客户端 |
| `BASE_URL` | `https://agent.example.com` (此服务器的公共 URL) |
| `JWT_SIGNING_KEY` | `openssl rand -hex 32` |
在您的提供商中注册 OAuth 客户端,跳转 URI 为 **`<BASE_URL>/auth/callback`**。然后(重新)添加 Claude 中的自定义连接器 — 它将通过您的提供商的登录界面。 当 OIDC 变量未设置时,服务器以开放状态运行(仅限本地测试)。
## 安全
- 此服务器通过您的代理从公共互联网可访问。**在公开之前启用 [身份验证](#authentication)** — 任何到达端点的人都可以调用其工具。
- 将所有真实凭证(API 令牌等)保留在您的 NAS 上 — 要么在 `.env` 中,要么通过连接器使用 `secret_set` 设置(在 `data/vault` 中加密存储,引用名称,**永远不会返回给模型**)。仅在服务器端使用。
- `.env` 和 `data/` 内容被 git 忽略 — 永远不要提交秘密。
## 故障排除
在反向代理和 OIDC 提供商后面运行时获得的艰难笔记。
启用详细日志以查看身份验证失败的真实原因:
```yaml
# docker-compose.yml → environment:
FASTMCP_LOG_LEVEL: "DEBUG"
```
| 症状 | 原因和解决方法 |
|---------|-------------|
| 登录成功,但 Claude 表示 *"连接时返回了一个错误"*;日志显示 `Token verified successfully` 然后 **`Token missing required scopes`** | 代理颁发的 MCP 令牌不带有上游 OIDC 作用域作为声明。**不要设置 `required_scopes`** — 成功登录就足够了。(已在 `server.py` 中删除。) |
| 日志立即显示 `Issued new FastMCP tokens`,接着是 **`Bearer token rejected`** (401 `invalid_token`) | 在终止 TLS 的代理后面,uvicorn 忽略了 `X-Forwarded-Proto`,因此服务器计算出一个 `http://` URL 并拒绝了自己的 `https` 受众令牌。设置 **`FORWARDED_ALLOW_IPS: "*"`** (已在 `docker-compose.yml` 中)。 |
| 日志警告 **`disk client_storage unavailable (Fernet key must be 32 url-safe base64-encoded bytes)`** | `STORAGE_ENCRYPTION_KEY` 不是有效的 Fernet 密钥(它与 `JWT_SIGNING_KEY` 不同)。生成:`python -c "from cryptography.fernet import Fernet;print(Fernet.generate_key().decode())"`。或者省略它以使用未加密(仍然持久)的存储。 |
| 曾经有效,然后在重新创建容器后,使用**旧客户端 ID** 返回 `Bearer token rejected` | OAuth 客户端存储是临时的,被清除。持久的 `data/auth` (此仓库)修复了它。要清除 Claude 端的卡住客户端:删除连接器,完全退出并重新打开应用程序,重新添加。 |
| 连接器根本无法连接;代理返回一个登录 **网页** | 您在 `/mcp` 前面放置了反向代理 SSO/转发身份验证。机器客户端无法进行交互式登录 — **删除它**;身份验证属于 MCP 层(此服务器)。 |
## 路线图
- [x] 骨架:`ping` 工具 + HTTPS 上的远程 MCP
- [x] 身份验证:通过您自己的 OIDC 提供商(Pocket ID、Authentik、Keycloak、Auth0 等)进行 OAuth 2.1
- [x] 内存工具(`memory_write` / `memory_read` / `memory_list` / `memory_search` / `memory_delete`),用于多代理的作用域命名空间
- [x] 技能路由器(`skill_search` / `skill_list` / `skill_load` / `skill_resource` / `skill_write`)
- [x] 通用服务调用者(`call_service` / `service_add` / `service_list`)— 集成作为数据 + 技能,无需重新部署
- [x] 加密秘密保险箱(`secret_set` / `secret_list` / `secret_delete`)— 通过连接器设置秘密;值在休息时加密,永远不会返回
- [x] 自我描述:连接时的服务器 `instructions` + 一个 `guide` 工具,以便任何 LLM 立即了解连接器是什么以及如何使用它
- [ ] 捆绑服务配置和技能(Home Assistant、Mealie 等)
- [ ] 多代理:代理收件箱 + 子代理编排
- [ ] GHCR 上的预构建镜像
## 许可证
[MIT](LICENSE) © 2026 IkarusMK
连接信息
猜你喜欢
暂无推荐服务器