Cloudflare Workers的MCP中继
一个安全的HTTP长轮询中继,允许从任何地方远程访问KOReader MCP服务器。这项服务弥合了您的电子阅读器和Claude Desktop或Claude Mobile等MCP客户端之间的差距。
特性
- OAuth 2.1身份验证(授权码+PKCE):使用设备生成的密码进行安全访问
- 符合MCP授权:实现RFC 9728受保护资源元数据
- HTTP长轮询:适用于任何HTTP客户端(不需要WebSocket)
- Cloudflare持久对象:持久设备状态和请求队列
- 基于设备的ID:从您的设备型号导出的ID(例如。,
KoboClara-abc1) - 零知识密码:密码在设备上生成并散列,从不以明文形式发送
运作原理
sequenceDiagram
participant D as E-Reader (KOReader)
participant R as MCP Relay (Cloudflare)
participant C as Client (Claude / MCP)
Note over D: Generate deviceId + passcode locally
Note over D: Hash passcode with SHA-256
Note over D,R: 1) Device registration
D->>R: POST /register (passcodeHash)
R-->>D: 200 OK (relayUrl, tokenEndpoint)
Note over D: Display passcode to user
Note over C,R: 2) Client authenticates via OAuth
C->>R: GET /authorize (login UI)
C->>R: POST /oauth/token (auth code exchange)
R-->>C: 200 OK (access_token)
Note over D,R: 3) Device polls for requests
loop Long-poll cycle
D->>R: GET /poll
alt Request available
C->>R: POST /mcp (Authorization: Bearer token)
R->>R: Validate access token
R-->>D: 200 OK ({type:request})
Note over D: Process request locally
D->>R: POST /response
R-->>C: 200 OK (MCP response)
else Timeout
R-->>D: 200 OK ({type:ping})
end
end身份验证流程
1.设备注册
当设备注册时,它 在本地生成凭据 (设备型号中的deviceId+后缀,随机6位密码),并仅将密码的SHA-256哈希发送给中继器:
# Device generates: deviceId="KoboClara-abc1", passcode="123456"
# Device computes: passcodeHash=SHA256("123456")
curl -X POST https://mcp-relay.example.com/register \
-H "Content-Type: application/json" \
-d '{
"deviceId": "KoboClara-abc1",
"deviceName": "My Kobo",
"passcodeHash": "8d969eef6ecad3c29a3a629280e686cf...",
"version": "2.0.0"
}'
# Response:
{
"type": "registered",
"deviceId": "KoboClara-abc1",
"relayUrl": "https://mcp-relay.example.com/mcp",
"tokenEndpoint": "https://mcp-relay.example.com/oauth/token"
}密码显示在用户的设备上。他们需要在MCP客户端(Claude Desktop等)中输入它以进行身份验证。
2.获取访问令牌
MCP客户端使用OAuth 2.1授权码流和PKCE进行身份验证。
3.发出经过身份验证的MCP请求
将访问令牌包含在 Authorization 头球
curl -X POST https://mcp-relay.example.com/mcp \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "method": "resources/list", "id": 1}'OAuth元数据端点
中继实现了RFC 9728受保护资源元数据:
/mcp/.well-known/oauth-protected-resource-受保护的资源元数据/.well-known/oauth-authorization-server-授权服务器元数据
部署
先决条件
- npm/bun
- Cloudflare帐户(_免费层级_ 作品!)
部署到Cloudflare
- 安装依赖项
bun install- 登录Cloudflare
bun run wrangler login- (可选)为令牌签名设置JWT密钥
bun run wrangler secret put JWT_SECRET
# Enter a random string when prompted- 部署
bun run wrangler deploy您的继电器将在以下地点可用: https://mcp-relay..workers.dev/
API终点
GET /
返回中继信息和可用端点。
OAuth端点
| 端点 | 方法 | 描述 |
|---|---|---|
/authorize | GET | 授权端点(登录) |
/.well-known/oauth-protected-resource | GET | 受保护资源元数据 |
/.well-known/oauth-authorization-server | GET | 授权服务器元数据 |
/oauth/token | POST | 令牌端点(身份验证码交换) |
设备端点(由KOReader使用)
| 端点 | 方法 | 描述 |
|---|---|---|
/register | POST | 使用密码哈希注册设备 |
/poll | GET | 传入MCP请求的长轮询 |
/response | POST | 发送对转发请求的响应 |
/pong | POST | 保持心跳活跃(可选) |
所有设备端点都需要 X-Device-Id 标题(除 /register,其中包括 deviceId 在JSON正文中)。
客户端端点(由Claude/MCP客户端使用)
| 端点 | 方法 | 描述 |
|---|---|---|
/mcp | POST | 发送MCP请求(需要Bearer令牌) |
/status | GET | 检查设备是否在线(需要 X-Device-Id 标题或 device_id 查询) |
安全
密码安全
- 设备生成:密码仅在设备上生成和显示
- 零知识:Relay仅接收并存储SHA-256哈希值
- 从不以明文传输:实际密码永远不会离开设备
- 重新注册验证:重新连接必须提供匹配的密码哈希
令牌安全
- 短期代币:访问令牌将在1小时后过期
- 受众验证:令牌绑定到中继资源
最佳实践
跟随 MCP安全最佳实践:
- ✅ HTTPS用于所有通信
- ✅ 令牌受众验证
- ✅ 无令牌传递
- ✅ 适当的
WWW-Authenticate挑战与resource_metadata - ✅ 短期访问令牌
- ✅ 安全凭证存储(散列密码)
配置
| 参数 | 值 | 说明 |
|---|---|---|
| 轮询超时 | 30s | 中继返回前等待多长时间 ping |
| 请求超时 | 30s | 客户端等待设备响应的时间 |
| 会话超时 | 2min | 设备在无活动后被视为离线 |
| 令牌过期 | 1小时 | 访问令牌的有效期 |
| 设备ID格式 | {model}-xxxx | 设备型号+4字符随机后缀 |
| 密码格式 | 6位数字 | 随机数字密码 |
成本估算(Cloudflare免费套餐)
| 资源 | 可用层限制 | 预期使用量 |
|---|---|---|
| 工人请求 | 10000/天 | ~1000/天 |
| 持久对象请求 | 100000/月 | ~10000/月 |
| 持久对象存储 | 1 GB | ~ 1 MB |
