Skip to main content
Glama
README.md
# Feishu MCP Gateway

一个部署在 **Cloudflare Workers** 上的飞书远程 MCP 网关,用于把飞书官方 MCP 安全地接入 ChatGPT 等支持 Streamable HTTP MCP 的客户端。

本项目使用 **飞书 User Access Token(UAT)**,而不是 Tenant Access Token(TAT)。首次部署后通过浏览器完成一次飞书 OAuth 授权,Worker 会保存并自动刷新 UAT,后续所有 MCP 请求都以被授权用户本人的身份访问飞书资源。

> 适合:希望让 AI 搜索、读取、创建、更新自己的飞书云文档,并且不想把飞书 Token 直接暴露给 MCP 客户端的场景。

## 工作方式

```mermaid
flowchart LR
    A[ChatGPT / MCP Client] -->|/mcp/随机密钥| B[Cloudflare Worker]
    B -->|X-Lark-MCP-UAT| C[飞书官方 MCP]
    B --> D[Durable Object]
    D -->|保存 / 刷新 UAT| E[飞书 OAuth]
```

核心设计:

- 使用飞书官方远程 MCP:`https://mcp.feishu.cn/mcp`
- 通过 OAuth 获取代表当前用户的 `user_access_token`
- 使用 Durable Object 保存 token,并串行刷新一次性 `refresh_token`
- MCP 地址使用高强度随机路径密钥保护
- `FEISHU_APP_SECRET`、`MCP_GATEWAY_KEY` 仅作为 Cloudflare Secret 保存
- ChatGPT 永远拿不到飞书 UAT、refresh token 或 App Secret

## 默认开放的飞书能力

默认 `wrangler.toml` 中允许:

| Tool | 作用 |
|---|---|
| `search-user` | 搜索企业内用户 |
| `get-user` | 获取用户信息 |
| `fetch-file` | 获取云文档中的文件/图片 |
| `search-doc` | 搜索云文档 |
| `create-doc` | 创建云文档 |
| `fetch-doc` | 读取云文档 |
| `update-doc` | 更新云文档 |
| `list-docs` | 获取知识空间节点下文档列表 |
| `get-comments` | 查看文档评论 |
| `add-comments` | 添加文档评论 |

可以通过 `wrangler.toml` 的 `LARK_ALLOWED_TOOLS` 缩小工具范围。若只需要读取,建议移除创建、更新和评论写入工具,同时在飞书后台关闭对应写权限。

## 前置条件

你需要:

- 一个飞书企业自建应用
- Cloudflare 账号
- Node.js 20+
- 一个支持远程 MCP / Streamable HTTP 的客户端,例如 ChatGPT Developer Mode

## 操作流程

### 1. 配置飞书自建应用

在飞书开放平台创建或进入一个自建应用,并确保你本人位于应用的可用范围内。

在 **权限管理** 中开通 `wrangler.toml` 的 `LARK_OAUTH_SCOPES` 所列用户身份权限。默认包含云文档、知识库、评论、通讯录查询等权限。

其中必须包含:

```text
offline_access
```

它用于获取 `refresh_token`,让 Worker 可以长期自动刷新 UAT。

如果应用安全设置中存在“刷新 user_access_token”相关开关,也需要开启并重新发布应用版本。

### 2. 克隆并安装

```powershell
git clone https://github.com/wangling-miao/feishu-mcp.git
cd feishu-mcp
npm install
npm run typecheck
```

### 3. 生成随机 MCP 密钥

```powershell
python -c "import secrets; print(secrets.token_urlsafe(32))"
```

保存输出结果,后面会作为 `MCP_GATEWAY_KEY`。

### 4. 配置 Cloudflare Secrets

复制示例文件:

```powershell
Copy-Item .env.production.example .env.production
```

编辑 `.env.production`:

```dotenv
FEISHU_APP_ID=cli_xxxxxxxxxxxxxxxx
FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
MCP_GATEWAY_KEY=你的高强度随机密钥
```

> `.env.production` 已被 `.gitignore` 排除。不要把真实 App Secret 或随机密钥提交到 Git。

### 5. 部署到 Cloudflare Workers

```powershell
npx wrangler@latest login
npx wrangler@latest deploy --secrets-file .env.production
```

部署完成后会得到类似:

```text
https://feishu-mcp-gateway.<account>.workers.dev
```

### 6. 配置飞书 OAuth 回调地址

回到飞书开放平台,在应用的 **安全设置 / 重定向 URL** 中添加:

```text
https://feishu-mcp-gateway.<account>.workers.dev/oauth/callback
```

保存配置,并按飞书要求重新发布应用版本。

### 7. 首次绑定你的飞书账号

浏览器访问:

```text
https://feishu-mcp-gateway.<account>.workers.dev/auth/<MCP_GATEWAY_KEY>
```

完成飞书登录和授权后,Worker 会:

1. 获取 authorization code
2. 换取 `user_access_token` 与 `refresh_token`
3. 调用飞书用户信息接口验证 UAT
4. 将 token 安全保存到 Durable Object
5. 后续自动刷新过期 token

检查绑定状态:

```text
https://feishu-mcp-gateway.<account>.workers.dev/auth/status/<MCP_GATEWAY_KEY>
```

正常结果应包含:

```json
{
  "authorized": true,
  "user_name": "...",
  "open_id": "ou_...",
  "has_refresh_token": true
}
```

### 8. 接入 ChatGPT

MCP Endpoint:

```text
https://feishu-mcp-gateway.<account>.workers.dev/mcp/<MCP_GATEWAY_KEY>
```

在 ChatGPT 中:

1. 打开 Developer Mode / 自定义 MCP 配置
2. 添加远程 MCP
3. Transport 选择 Streamable HTTP / Streaming HTTP
4. Authentication 选择 `No Authentication`
5. 填入上面的 MCP Endpoint
6. Scan / Refresh Tools

这里的 `No Authentication` 只表示 ChatGPT 不再额外发送 OAuth Header;真正的网关认证由随机 URL 密钥完成,飞书侧则由 Worker 注入 UAT。

### 9. 测试

项目提供 PowerShell 测试脚本:

```powershell
.\scripts\test.ps1 `
  -BaseUrl "https://feishu-mcp-gateway.<account>.workers.dev" `
  -GatewayKey "你的MCP_GATEWAY_KEY"
```

脚本会检查授权状态、MCP `initialize` 与 `tools/list`。

## 常用维护

### 重新绑定飞书账号

```powershell
Invoke-RestMethod -Method Post `
  "https://feishu-mcp-gateway.<account>.workers.dev/auth/logout/<MCP_GATEWAY_KEY>"
```

然后重新访问:

```text
/auth/<MCP_GATEWAY_KEY>
```

### 修改允许的 MCP 工具

编辑 `wrangler.toml`:

```toml
LARK_ALLOWED_TOOLS = "search-doc,fetch-doc,list-docs"
```

重新部署:

```powershell
npx wrangler@latest deploy --secrets-file .env.production
```

### 修改 OAuth 权限

同步修改:

1. 飞书开放平台中的用户身份权限
2. `wrangler.toml` 中 `LARK_OAUTH_SCOPES`
3. 必要时重新 OAuth 授权

## 常见问题

### `code=20005 msg=invalid access token`

通常表示 UAT 无效或已过期。本项目会自动刷新 UAT;若持续出现,请检查:

- 是否已经通过 `/auth/<KEY>` 完成 OAuth
- `offline_access` 是否已经开通
- refresh token 权限/开关是否生效
- 应用是否重新发布
- 当前用户是否仍在应用可用范围内

### 为什么不用 TAT?

TAT 代表“应用”,不是创建应用的个人。应用不会因为是你创建的,就自动继承你的个人云文档权限。UAT 才代表完成 OAuth 的飞书用户,因此更适合“让 AI 操作我自己的飞书文档”这一场景。

### 能访问哪些文档?

最终权限取决于三层:

1. OAuth 用户本人拥有的资源权限
2. 自建应用申请并获批的用户身份 scope
3. `LARK_ALLOWED_TOOLS` 允许暴露给 MCP 客户端的工具

## 安全建议

- 使用至少 32 字节随机 `MCP_GATEWAY_KEY`
- 不要把真实 `.env.production` 提交 Git
- 不需要写操作时,移除写工具与写权限
- 泄露随机密钥后立即更换并重新部署
- 不要在日志中打印 UAT、refresh token 或 App Secret

## 项目结构

```text
.
├── src/
│   └── index.ts             # Worker、OAuth、MCP 代理和 Durable Object
├── scripts/
│   └── test.ps1             # 部署后测试
├── wrangler.toml            # Cloudflare Worker 配置
├── .env.production.example  # Secret 示例
├── package.json
└── tsconfig.json
```

## License

当前仓库未附加开源许可证;如需二次分发,请先确认授权方式。