HTTP长轮询中继服务

MCP Relay for Cloudflare Workers 是一个安全的HTTP长轮询中继服务,支持通过OAuth 2.1认证实现远程访问KOReader MCP服务器,适用于电子阅读器与MCP客户端之间的通信。

  • by laughedelic
  • TypeScript
  • 本地部署
  • 更新于
在 GitHub 查看 laughedelic/koreader-mcp-cloud-relay

使用说明

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

  1. 安装依赖项

    bun install
    
  2. 登录Cloudflare

    bun run wrangler login
    
  3. (可选)为令牌签名设置JWT密钥

    bun run wrangler secret put JWT_SECRET
    # Enter a random string when prompted
    
  4. 部署

    bun run wrangler deploy
    

您的继电器将在以下地点可用: https://mcp-relay.<your-subdomain>.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