feishu-user-plugin 1.3.8 → 1.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/.claude-plugin/plugin.json +12 -2
  2. package/CHANGELOG.md +100 -12
  3. package/README.en.md +610 -0
  4. package/README.md +292 -532
  5. package/package.json +12 -5
  6. package/proto/lark.proto +10 -0
  7. package/scripts/explore-card-protobuf.js +144 -0
  8. package/scripts/explore-image-minimize.js +163 -0
  9. package/scripts/generate-og-image.js +39 -0
  10. package/scripts/generate-release-artifacts.js +318 -0
  11. package/scripts/probe-feishu-docx.js +203 -0
  12. package/scripts/sync-team-skills.sh +109 -7
  13. package/skills/feishu-user-plugin/SKILL.md +76 -4
  14. package/skills/feishu-user-plugin/references/CLAUDE.md +74 -54
  15. package/src/auth/credentials.js +36 -0
  16. package/src/cli.js +86 -45
  17. package/src/clients/user.js +15 -13
  18. package/src/events/cursor.js +103 -0
  19. package/src/events/event-buffer.js +8 -5
  20. package/src/events/event-log.js +151 -0
  21. package/src/events/index.js +8 -1
  22. package/src/events/lockfile.js +126 -0
  23. package/src/events/owner.js +73 -0
  24. package/src/events/ws-server.js +95 -25
  25. package/src/oauth.js +48 -7
  26. package/src/resolver.js +10 -0
  27. package/src/server.js +248 -29
  28. package/src/setup.js +99 -25
  29. package/src/test-all.js +12 -9
  30. package/src/test-events-cursor.js +56 -0
  31. package/src/test-events-lockfile.js +36 -0
  32. package/src/test-events-log.js +67 -0
  33. package/src/test-events-owner.js +64 -0
  34. package/src/test-fixtures/doc-blocks/sample-1.json +1256 -0
  35. package/src/test-read-doc-markdown.js +61 -0
  36. package/src/test-switch-profile.js +171 -0
  37. package/src/tools/diagnostics.js +10 -3
  38. package/src/tools/docs.js +93 -3
  39. package/src/tools/events.js +143 -33
  40. package/src/tools/messaging-bot.js +2 -3
  41. package/src/tools/messaging-user.js +23 -14
  42. package/src/tools/profile.js +12 -7
package/README.md CHANGED
@@ -3,606 +3,366 @@
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
4
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-green.svg)](https://nodejs.org)
5
5
  [![MCP](https://img.shields.io/badge/MCP-Compatible-purple.svg)](https://modelcontextprotocol.io)
6
- [![Tools](https://img.shields.io/badge/Tools-82-orange.svg)](#tools)
6
+ [![Tools](https://img.shields.io/badge/Tools-84-orange.svg)](#工具索引84-个)
7
+ [![npm](https://img.shields.io/npm/v/feishu-user-plugin.svg)](https://www.npmjs.com/package/feishu-user-plugin)
7
8
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
8
9
 
9
- **All-in-one Feishu/Lark MCP Server -- 82 tools, 9 skills, 3 auth layers for messaging, docs, bitable, calendar, tasks, drive, OKR, and more.**
10
+ **中文** · [English](README.en.md) · [Docs](https://ethanqc.github.io/feishu-user-plugin/) · [CHANGELOG](CHANGELOG.md) · [npm](https://www.npmjs.com/package/feishu-user-plugin)
10
11
 
11
- The only MCP server that lets you send messages as your **personal identity** (not a bot), while also integrating the full official Feishu API. Works with Claude Code, Cursor, Windsurf, OpenClaw, and any MCP-compatible client.
12
+ 飞书 / Lark MCP 服务器,覆盖 IM、文档、多维表格、知识库、云空间、日历、任务 v2、OKR、实时事件。**84 tools · 3 auth layers · 9 MCP prompts · MIT licensed · Node ≥18**。
12
13
 
13
- ## Highlights
14
+ 兼容 Claude Code、Codex、Cursor、Windsurf、VS Code、Claude Desktop、OpenClaw 等 MCP 客户端。
14
15
 
15
- - **Send as yourself** -- Messages show your real name, not a bot. Supports text, rich text, images, files, stickers, and audio.
16
- - **Read everything** -- Group chats via bot API, P2P (direct messages) via OAuth UAT.
17
- - **Full Feishu suite** -- Docs, Bitable, Wiki, Drive, Calendar, Tasks, Contacts -- all in one plugin.
18
- - **3 auth layers** -- Cookie-based user identity, app credentials (Official API), and OAuth UAT (P2P reading).
19
- - **Group management** -- Create groups, add/remove members, pin messages, emoji reactions.
20
- - **Document editing** -- Not just read/create, but insert/update/delete content blocks.
21
- - **Calendar & Tasks** -- Create events, check free/busy, manage tasks.
22
- - **9 slash commands** for Claude Code -- `/send`, `/reply`, `/search`, `/digest`, `/doc`, `/table`, `/wiki`, `/drive`, `/status`
23
- - **Auto session management** -- Cookie heartbeat every 4h, UAT auto-refresh with token rotation.
24
- - **Real-time events** (v1.3.8) -- `get_new_events` drains an in-memory queue of incoming Feishu messages — react to replies / group activity within seconds without polling. WS connection auto-starts on boot.
25
- - **Multi-platform** -- Claude Code, Cursor, Windsurf, VS Code, OpenClaw.
16
+ 与其他飞书 MCP 的区别:基于 cookie + protobuf 协议路径,支持以**用户本人身份**发消息——飞书官方开放 API 没有 `send_as_user` 权限点,机器人 token 发出的消息一律标 `sender_type: "app"`。
26
17
 
27
- ## Why This Exists
18
+ ## 三层鉴权
28
19
 
29
- Feishu's official API has a hard limitation: **there is no `send_as_user` scope**. Even with `user_access_token` (OAuth), messages still show `sender_type: "app"`.
20
+ | 鉴权层 | 凭证 | 覆盖能力 | 工具数 |
21
+ |---|---|---|---|
22
+ | 用户身份(cookie + protobuf) | `LARK_COOKIE` | 以用户身份发文本 / 图片 / 文件 / 富文本 / @ / 批量 | 8 |
23
+ | 官方 API(机器人) | `LARK_APP_ID` + `LARK_APP_SECRET` | 群消息读写、文档、多维表格、知识库、云空间、日历、任务 v2、OKR、联系人、实时事件 WS | 70+ |
24
+ | 用户 OAuth UAT | `LARK_USER_ACCESS_TOKEN` + `LARK_USER_REFRESH_TOKEN` | P2P 私聊读取、用户 chat 列表;写入文档 / Bitable / 日历 资源时以用户为 owner | 2 显式 + 全工具 UAT-first |
30
25
 
31
- This project combines three auth layers into one plugin:
26
+ 三层独立 —— 配置任意一层,对应工具可用。
32
27
 
33
- ```
34
- User Identity (cookie): You -> Protobuf -> Feishu (messages appear as YOU)
35
- Official API (app token): You -> REST API -> Feishu (docs, tables, wiki, drive)
36
- User OAuth (UAT): You -> REST API -> Feishu (read P2P chats, list all chats)
37
- ```
38
-
39
- **One plugin. Everything Feishu. No other MCP needed.**
40
-
41
- ## Quick Start
42
-
43
- ### Option 1: npx (recommended)
44
-
45
- ```bash
46
- npx feishu-user-plugin
47
- ```
48
-
49
- No installation needed. The package runs directly via npx.
50
-
51
- ### Option 2: Clone and run locally
28
+ ## 安装
52
29
 
53
30
  ```bash
54
- git clone https://github.com/EthanQC/feishu-user-plugin.git
55
- cd feishu-user-plugin
56
- npm install
57
- npm start
31
+ npx feishu-user-plugin setup --app-id <APP_ID> --app-secret <APP_SECRET>
32
+ npx feishu-user-plugin oauth # 拿用户 OAuth UAT
33
+ # 重启 Claude Code / Codex
58
34
  ```
59
35
 
60
- ## Create Your Feishu App
61
-
62
- To use the Official API tools (docs, tables, wiki, drive, bot messaging), you need to create a Feishu app:
63
-
64
- ### Step 1: Create the App
65
-
66
- 1. Go to [Feishu Open Platform](https://open.feishu.cn/app) and log in
67
- 2. Click **Create Custom App** (创建自建应用) -- you must choose **Custom App** (自建应用), NOT marketplace/third-party types
68
- 3. Fill in the app name and description, then create it
69
-
70
- ### Step 2: Enable Bot Capability
71
-
72
- 1. In your app settings, go to **Add Capabilities** (添加应用能力)
73
- 2. Enable **Bot** (机器人)
74
-
75
- ### Step 3: Add Permissions (Scopes)
76
-
77
- Go to **Permissions & Scopes** (权限管理) and add the following scopes:
78
-
79
- | Scope | Purpose |
80
- |-------|---------|
81
- | `im:message` | Send messages as bot |
82
- | `im:message:readonly` | Read message history |
83
- | `im:chat:readonly` | List and read chats |
84
- | `docx:document` | Read and create documents |
85
- | `docx:document:readonly` | Read documents |
86
- | `bitable:record` | Read and write Bitable records |
87
- | `wiki:wiki:readonly` | Read wiki spaces and nodes |
88
- | `drive:drive:readonly` | List Drive files and folders |
89
- | `contact:user.base:readonly` | Look up users by email/mobile |
90
-
91
- > Add more scopes as needed depending on which tools you use.
92
-
93
- ### Step 4: Get App Credentials
94
-
95
- 1. Go to **Credentials & Basic Info** (凭证与基础信息)
96
- 2. Copy the **App ID** (`cli_xxxxxxxxxxxx`) and **App Secret**
97
- 3. Set them as `LARK_APP_ID` and `LARK_APP_SECRET` in your environment
98
-
99
- ### Step 5: Publish and Approve
100
-
101
- 1. **Create a version** and submit it for review (创建版本)
102
- 2. Have your organization admin approve the app (管理员审核)
103
- 3. After approval, the app is live
36
+ cookie 获取:跟 Claude Code 说一句"帮我设置飞书 cookie"会自动经 Playwright 扫码登录抓取;手动方式在 feishu.cn DevTools Network 标签从请求头 Cookie 整行复制(不要用 `document.cookie` 或 Application > Cookies 标签—— HttpOnly 的 `session` / `sl_session` 拿不到)。
104
37
 
105
- ### Step 6: Add Bot to Group Chats
38
+ 没有 APP_ID / SECRET 见下面 [创建飞书应用](#创建飞书应用)。
106
39
 
107
- Add your bot to the group chats where you want it to read messages. The bot can only access chats it has been added to.
40
+ ## 用法
108
41
 
109
- ## Environment Variables
110
-
111
- | Variable | Required For | Description |
112
- |----------|-------------|-------------|
113
- | `LARK_COOKIE` | User identity tools | Feishu web session cookie string. Needed for `send_to_user`, `send_to_group`, `search_contacts`, etc. |
114
- | `LARK_APP_ID` | Official API tools | App ID from Feishu Open Platform. Needed for `read_messages`, docs, tables, wiki, drive. |
115
- | `LARK_APP_SECRET` | Official API tools | App Secret from Feishu Open Platform. Used together with `LARK_APP_ID`. |
116
- | `LARK_USER_ACCESS_TOKEN` | P2P chat reading | OAuth user token. Needed for `read_p2p_messages` and `list_user_chats`. Obtained via `node src/oauth.js`. |
117
- | `LARK_USER_REFRESH_TOKEN` | UAT auto-refresh | Refresh token for automatic UAT renewal. Obtained together with UAT via OAuth flow. |
118
-
119
- All five variables are required for full functionality. Configure all of them during setup.
120
-
121
- ## How to Get Your Cookie
122
-
123
- **Option A: Automated via Playwright MCP (recommended, zero manual copying)**
124
-
125
- First, install Playwright MCP if you don't have it:
126
- ```bash
127
- npx @anthropic-ai/claude-code mcp add playwright -- npx @anthropic-ai/mcp-server-playwright
128
42
  ```
129
-
130
- Then just tell Claude Code: **"Help me set up my Feishu cookie"**
131
-
132
- Claude Code will automatically:
133
- 1. Open feishu.cn in a browser via Playwright
134
- 2. Show you the QR code — scan it with Feishu mobile app
135
- 3. Extract the full cookie (including HttpOnly) via `context.cookies()`
136
- 4. Write it to your `.mcp.json` LARK_COOKIE field
137
- 5. Prompt you to restart Claude Code
138
-
139
- **Option B: Manual (via Network tab)**
140
-
141
- 1. Open [feishu.cn/messenger](https://www.feishu.cn/messenger/) in your browser and log in
142
- 2. Open DevTools (`F12` or `Cmd+Option+I`)
143
- 3. Go to the **Network** tab → check **Disable cache** → press `Cmd+R` to reload
144
- 4. Click the first request in the list (usually the page itself)
145
- 5. In the right panel, find **Request Headers** → **Cookie:** → right-click → **Copy value**
146
- 6. Set it as `LARK_COOKIE` in your environment
147
-
148
- > Do NOT use `document.cookie` in the Console or copy from Application → Cookies tab — they miss HttpOnly cookies (`session`, `sl_session`) required for auth.
149
-
150
- > The server automatically refreshes the session via heartbeat every 4 hours. The `sl_session` cookie has a 12-hour max-age.
151
-
152
- ## Set Up OAuth (Required for P2P Chat Reading)
153
-
154
- To enable `read_p2p_messages` and `list_user_chats`:
155
-
156
- 1. Your Feishu app must be a **Custom App** (自建应用), NOT marketplace/third-party
157
- 2. Add scopes: `im:message`, `im:message:readonly`, `im:chat:readonly`
158
- 3. In your app's **Security Settings** (安全设置), add the OAuth redirect URI: `http://127.0.0.1:9997/callback`
159
- 4. **Important**: Make sure "对外共享" (external sharing) is **disabled** in your app version settings — enabling it marks the app as b2c/b2b type, which blocks P2P chat access
160
- 5. Run the authorization flow:
161
-
162
- ```bash
163
- # If you cloned the repo:
164
- node src/oauth.js
165
-
166
- # If you installed via npx:
167
- cd $(npm root -g)/feishu-user-plugin && node src/oauth.js
168
- # Or clone the repo just for the OAuth step, then use npx for daily use
43
+ 你:帮我以我身份给王小明发:今天的代码 review 我看完了,有 3 个 nit
44
+ Claude:[调用 send_to_user] Sent
169
45
  ```
170
46
 
171
- A browser window will open for OAuth consent. The token is saved to `.env` automatically and auto-refreshes at runtime. Add both `LARK_USER_ACCESS_TOKEN` and `LARK_USER_REFRESH_TOKEN` from `.env` to your MCP config's `env` section.
172
-
173
- ## MCP Client Configuration
174
-
175
- ### Claude Code
176
-
177
- Add to your project's `.mcp.json` (or `~/.claude/.mcp.json` for global):
178
-
179
- **Using npx:**
180
-
181
- ```json
182
- {
183
- "mcpServers": {
184
- "feishu": {
185
- "command": "npx",
186
- "args": ["-y", "feishu-user-plugin"],
187
- "env": {
188
- "LARK_COOKIE": "your-cookie-string",
189
- "LARK_APP_ID": "cli_xxxxxxxxxxxx",
190
- "LARK_APP_SECRET": "your-app-secret",
191
- "LARK_USER_ACCESS_TOKEN": "your-uat",
192
- "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
193
- }
194
- }
195
- }
196
- }
47
+ ```
48
+ 你:总结"工程组"群今天 9 点之后的讨论,发个日报到 #日报频道
49
+ Claude:[read_messages 总结 → send_to_group] Sent
197
50
  ```
198
51
 
199
- **Using a local clone:**
52
+ ## 创建飞书应用
53
+
54
+ `LARK_APP_ID` / `LARK_APP_SECRET` 是用 Official API(70+ 工具)的前置条件:
55
+
56
+ 1. [飞书开放平台](https://open.feishu.cn/app) 登录 → 创建**自建应用**(不能选商店应用 / 第三方应用,否则 P2P 读取会被锁)
57
+ 2. 添加应用能力 → 启用机器人
58
+ 3. 权限管理 → 添加 scope:
59
+ - 消息:`im:message`、`im:message:readonly`、`im:chat:readonly`
60
+ - 文档:`docx:document`、`bitable:record`、`wiki:wiki:readonly`、`drive:drive:readonly`
61
+ - 联系人:`contact:user.base:readonly`
62
+ - 按需:`okr:okr:readonly`、`calendar:calendar:readonly`、`task:task`、`drive:drive`、`docs:document.media:upload`、`wiki:wiki` 等
63
+ 4. 凭证与基础信息 → 复制 App ID(`cli_xxx`)+ App Secret
64
+ 5. 创建版本 → 提交审核 → 管理员审批
65
+ 6. 把 bot 加到要读消息的群里
66
+
67
+ ## 工具索引(84 个)
68
+
69
+ 完整工具列表 + 参数 + 跨域注意事项见 [CLAUDE.md](CLAUDE.md)。
70
+
71
+ ### 用户身份 —— 消息(cookie protobuf,8 个)
72
+
73
+ | 工具 | 说明 |
74
+ |---|---|
75
+ | `send_to_user` | 按名搜用户 + 发文本,一步完成 |
76
+ | `send_to_group` | 按名搜群 + 发文本,一步完成 |
77
+ | `send_as_user` | 按 chat ID 发文本,支持回复线程(`root_id` / `parent_id`) |
78
+ | `send_image_as_user` | 以用户身份发图(v1.3.9) |
79
+ | `send_file_as_user` | 以用户身份发文件(需先 `upload_file`) |
80
+ | `send_post_as_user` | 富文本:标题 + 段落 + @ + 超链 |
81
+ | `send_card_as_user` | 飞书交互卡片(机器人通道;cookie 通道服务端关闭,仅 bot 路径可用) |
82
+ | `batch_send` | 一次发多条到不同 chat(text / image / file / post) |
83
+
84
+ ### 用户身份 —— 联系人 / 信息(cookie,5 个)
85
+
86
+ | 工具 | 说明 |
87
+ |---|---|
88
+ | `search_contacts` | 搜用户 / bot / 群 |
89
+ | `create_p2p_chat` | 创建或获取 P2P chat |
90
+ | `get_chat_info` | 群详情(接受 `oc_xxx` 或 numeric) |
91
+ | `get_user_info` | 用户名 / 头像查询 |
92
+ | `get_login_status` | 三层鉴权健康检查(实际跑一次 UAT 调用,不只看配置) |
93
+
94
+ ### 用户 OAuth UAT —— P2P 读取(2 个)
95
+
96
+ | 工具 | 说明 |
97
+ |---|---|
98
+ | `read_p2p_messages` | 读私聊历史(外部群自动 fallback) |
99
+ | `list_user_chats` | 用户加入的所有群(仅群,不含 P2P;P2P 用 `search_contacts` → `create_p2p_chat`) |
100
+
101
+ ### 官方 API —— IM(15 个)
102
+
103
+ | 工具 | 说明 |
104
+ |---|---|
105
+ | `list_chats` | 列 bot 加入的所有 chat |
106
+ | `read_messages` | 读群消息(接受 chat 名 / `oc_xxx` / numeric;外部群自动 UAT fallback;merge_forward 自动展开) |
107
+ | `send_message_as_bot` | 机器人发消息 |
108
+ | `reply_message` | 机器人回复 |
109
+ | `forward_message` | 转发到其他 chat(自动识别 receive_id_type) |
110
+ | `delete_message` | 撤回 / 删除 bot 消息 |
111
+ | `update_message` | 编辑已发消息(仅支持 text / interactive) |
112
+ | `add_reaction` / `delete_reaction` | 表情回应 |
113
+ | `pin_message` | 置顶 |
114
+ | `create_group` / `update_group` | 建群 / 改群 |
115
+ | `list_members` / `manage_members` | 群成员 list / add / remove(注意 `member_id_type` 与 ID 类型匹配) |
116
+ | `download_message_resource` | 下载消息附件(image / file,> 2 MiB 必须 `save_path`) |
117
+
118
+ ### 官方 API —— 文档(7 个)
119
+
120
+ | 工具 | 说明 |
121
+ |---|---|
122
+ | `search_docs` | 关键词搜文档 |
123
+ | `read_doc` | 结构化 JSON |
124
+ | `read_doc_markdown` | v1.3.9 直接返回 markdown,~60% token 节省(适合 RAG / 总结) |
125
+ | `get_doc_blocks` | 块树 |
126
+ | `create_doc` | 创建文档(可选 `wiki_space_id` 直接落知识库) |
127
+ | `manage_doc_block` | 块 create / update / delete(image_path / file_path / image_token / file_token 快捷) |
128
+ | `download_doc_image` | 下载文档内嵌图片 |
129
+
130
+ ### 官方 API —— 多维表格 Bitable(6 个,v1.3.7 整合)
131
+
132
+ | 工具 | actions | 说明 |
133
+ |---|---|---|
134
+ | `manage_bitable_app` | create / copy / get_meta | 应用级(创建可指定 `wiki_space_id` 直接落 Wiki) |
135
+ | `manage_bitable_table` | list / create / update / delete | 数据表 CRUD |
136
+ | `manage_bitable_field` | list / create / update / delete | 字段(update 必须传 `type` 即使只改名) |
137
+ | `manage_bitable_view` | list / create / delete | 视图(grid / kanban / gallery / form / gantt / calendar) |
138
+ | `manage_bitable_record` | search / get / create / update / delete | 记录 CRUD(数组:单条或最多 500) |
139
+ | `upload_bitable_attachment` | — | 上传附件,返回 `file_token` |
140
+
141
+ ### 官方 API —— 知识库 Wiki(9 个)
142
+
143
+ | 工具 | 说明 |
144
+ |---|---|
145
+ | `list_wiki_spaces` | 列空间(UAT-first) |
146
+ | `search_wiki` | 搜知识库 |
147
+ | `list_wiki_nodes` | 列节点 |
148
+ | `get_wiki_node` | 节点 → obj_token 解析(接受 wiki node token 或 obj_token) |
149
+ | `create_wiki_node` | 创建节点(doc / sheet / bitable / mindnote / file / docx / slides) |
150
+ | `update_wiki_node` | 改名(内容编辑用 docx / bitable 工具) |
151
+ | `move_wiki_node` | 移动 |
152
+ | `copy_wiki_node` | 深拷贝 |
153
+ | `delete_wiki_node` | 删除 wiki 节点指针(底层 drive 资源用 `manage_drive_file(action=delete)` 删) |
154
+
155
+ ### 官方 API —— 云空间 Drive(5 个)
156
+
157
+ | 工具 | 说明 |
158
+ |---|---|
159
+ | `list_files` | 列文件夹内文件 |
160
+ | `create_folder` | 建文件夹 |
161
+ | `manage_drive_file` | copy / move / delete(必须传 `type`) |
162
+ | `upload_image` / `upload_file` | 上传图片 / 文件,返回 key |
163
+ | `upload_drive_file` | 上传到 Drive 文件夹(可选 `wiki_space_id` 直接挂 Wiki 节点) |
164
+
165
+ ### 官方 API —— OKR(6 个)
166
+
167
+ | 工具 | 说明 |
168
+ |---|---|
169
+ | `list_user_okrs` | 列指定用户的 OKR(必须传 user_id) |
170
+ | `get_okrs` | 批量取详情(objectives + key results + progress + alignments) |
171
+ | `list_okr_periods` | 列周期(季度 / 年度) |
172
+ | `create_okr_progress_record` | 添加进展记录(v1.3.7,需 `okr:okr.content:write`) |
173
+ | `list_okr_progress_records` | 列进展记录(从 `get_okrs` 提取 triples) |
174
+ | `delete_okr_progress_record` | 删进展记录 |
175
+
176
+ ### 官方 API —— 日历(8 个,写入 v1.3.7)
177
+
178
+ | 工具 | 说明 |
179
+ |---|---|
180
+ | `list_calendars` | 列日历(primary + 共享 + 订阅) |
181
+ | `list_calendar_events` | 列事件(指定时间窗) |
182
+ | `get_calendar_event` | 事件详情(参与人 / 地点 / 会议链接 / 附件) |
183
+ | `create_calendar_event` | 建事件(需 `calendar:calendar.event:write`) |
184
+ | `update_calendar_event` | 改事件 |
185
+ | `delete_calendar_event` | 删事件(可选 `meeting_chat_id` 同时解散关联会议群) |
186
+ | `respond_calendar_event` | RSVP(accept / decline / tentative) |
187
+ | `get_freebusy` | 多人 freebusy 查询 |
188
+
189
+ ### 官方 API —— 任务 v2(7 个,v1.3.7 新域)
190
+
191
+ 标识符是 `task_guid`(不是 v1 的 numeric `task_id`),需 `task:task` scope。
192
+
193
+ | 工具 | 说明 |
194
+ |---|---|
195
+ | `list_tasks` | 列当前用户任务 |
196
+ | `get_task` | 详情 |
197
+ | `create_task` | 建任务(summary 必填) |
198
+ | `update_task` | 改任务(必传 `update_fields=[...]`,飞书只 patch 列出字段) |
199
+ | `complete_task` | 完成 / 取消完成 |
200
+ | `delete_task` | 删 |
201
+ | `manage_task_members` | add / remove 成员(assignee / follower) |
202
+
203
+ ### 插件层 —— 诊断与多账号(4 个)
204
+
205
+ | 工具 | 说明 |
206
+ |---|---|
207
+ | `get_login_status` | 三层鉴权健康检查 |
208
+ | `list_profiles` | 列可用 profile(默认 + LARK_PROFILES_JSON / credentials.json) |
209
+ | `switch_profile` | 切 profile(缓存的 client 实例下次调用重建) |
210
+ | `manage_profile_hints` | 查 / 改 / 清 自动切换缓存(list / set / clear) |
211
+
212
+ ### 插件层 —— 实时事件(2 个,v1.3.9)
213
+
214
+ | 工具 | 说明 |
215
+ |---|---|
216
+ | `get_new_events` | 拉取增量事件(peek=true 不推进 cursor;filter by event_type / chat_id / since_seconds / profile) |
217
+ | `manage_ws_status` | info / reconnect / claim / rotate / reconfig(诊断 / 重连 / 抢锁 / 强制 events.jsonl 轮转 / 不重启重新订阅) |
218
+
219
+ ## 9 个 MCP prompts(slash commands)
220
+
221
+ | Prompt | 说明 |
222
+ |---|---|
223
+ | `/send` | 以用户身份发消息 |
224
+ | `/reply` | 读最近消息然后回 |
225
+ | `/digest` | 群 / P2P 最近消息总结 |
226
+ | `/search` | 搜联系人 / 群 |
227
+ | `/doc` | 搜 / 读 / 建文档 |
228
+ | `/table` | 操作多维表格 |
229
+ | `/wiki` | 搜知识库 |
230
+ | `/drive` | 列云空间 / 建文件夹 |
231
+ | `/status` | 检查三层鉴权状态 |
232
+
233
+ ## 客户端配置
234
+
235
+ 环境变量配置一致,配置文件路径和顶层键不同。
236
+
237
+ **统一 env 块**:
200
238
 
201
239
  ```json
202
240
  {
203
- "mcpServers": {
204
- "feishu": {
205
- "command": "node",
206
- "args": ["/absolute/path/to/feishu-user-plugin/src/index.js"],
207
- "env": {
208
- "LARK_COOKIE": "your-cookie-string",
209
- "LARK_APP_ID": "cli_xxxxxxxxxxxx",
210
- "LARK_APP_SECRET": "your-app-secret",
211
- "LARK_USER_ACCESS_TOKEN": "your-uat",
212
- "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
213
- }
214
- }
241
+ "command": "npx",
242
+ "args": ["-y", "feishu-user-plugin"],
243
+ "env": {
244
+ "LARK_COOKIE": "your-cookie-string",
245
+ "LARK_APP_ID": "cli_xxxxxxxxxxxx",
246
+ "LARK_APP_SECRET": "your-app-secret",
247
+ "LARK_USER_ACCESS_TOKEN": "your-uat",
248
+ "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
215
249
  }
216
250
  }
217
251
  ```
218
252
 
219
- Then just say things like:
220
- - "Send a message to Alice saying the meeting is at 3pm"
221
- - "What did the engineering group chat about today?"
222
- - "Search for docs about MCP"
253
+ **安放位置**:
223
254
 
224
- ### Claude Desktop
255
+ | 客户端 | 配置文件 | 顶层键 |
256
+ |---|---|---|
257
+ | Claude Code | `~/.claude.json`(推荐全局) / `.mcp.json` | `mcpServers.feishu-user-plugin` |
258
+ | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) | `mcpServers.feishu` |
259
+ | Codex | `~/.codex/config.toml` | `[mcp_servers.feishu-user-plugin]`(TOML) |
260
+ | Cursor | `.cursor/mcp.json`(项目级) | `mcpServers.feishu` |
261
+ | VS Code (Copilot) | `.vscode/mcp.json` | `servers.feishu`(注意是 `servers`,不是 `mcpServers`) |
262
+ | OpenClaw | `~/.openclaw/openclaw.json` | `mcp.servers.feishu-user-plugin` |
263
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers.feishu` |
225
264
 
226
- Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
265
+ **自动化设置**:
227
266
 
228
- ```json
229
- {
230
- "mcpServers": {
231
- "feishu": {
232
- "command": "npx",
233
- "args": ["-y", "feishu-user-plugin"],
234
- "env": {
235
- "LARK_COOKIE": "your-cookie-string",
236
- "LARK_APP_ID": "cli_xxxxxxxxxxxx",
237
- "LARK_APP_SECRET": "your-app-secret",
238
- "LARK_USER_ACCESS_TOKEN": "your-uat",
239
- "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
240
- }
241
- }
242
- }
243
- }
267
+ ```bash
268
+ npx feishu-user-plugin setup # 默认写 Claude Code (~/.claude.json)
269
+ npx feishu-user-plugin setup --client codex # Codex (~/.codex/config.toml)
270
+ npx feishu-user-plugin setup --client both # Claude Code + Codex 都写
271
+ npx feishu-user-plugin setup --activate # 激活当前 profile
244
272
  ```
245
273
 
246
- ### Cursor
274
+ 各客户端完整 JSON 模板见 [README.en.md `MCP Client Configuration` 段](README.en.md#mcp-client-configuration)。
247
275
 
248
- Add to `.cursor/mcp.json` in your project:
276
+ ## 多账号(v1.3.8 / v1.3.9)
249
277
 
250
- ```json
251
- {
252
- "mcpServers": {
253
- "feishu": {
254
- "command": "npx",
255
- "args": ["-y", "feishu-user-plugin"],
256
- "env": {
257
- "LARK_COOKIE": "your-cookie-string",
258
- "LARK_APP_ID": "cli_xxxxxxxxxxxx",
259
- "LARK_APP_SECRET": "your-app-secret",
260
- "LARK_USER_ACCESS_TOKEN": "your-uat",
261
- "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
262
- }
263
- }
264
- }
265
- }
266
- ```
267
-
268
- ### VS Code (Copilot)
269
-
270
- Add to `.vscode/mcp.json` in your project:
278
+ `~/.feishu-user-plugin/credentials.json` 支持多 profile(默认 + 任意附加),单台机器一处配置覆盖多个飞书账号 / 多个企业。
271
279
 
272
- ```json
273
- {
274
- "servers": {
275
- "feishu": {
276
- "type": "stdio",
277
- "command": "npx",
278
- "args": ["-y", "feishu-user-plugin"],
279
- "env": {
280
- "LARK_COOKIE": "your-cookie-string",
281
- "LARK_APP_ID": "cli_xxxxxxxxxxxx",
282
- "LARK_APP_SECRET": "your-app-secret",
283
- "LARK_USER_ACCESS_TOKEN": "your-uat",
284
- "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
285
- }
286
- }
287
- }
288
- }
280
+ ```bash
281
+ npx feishu-user-plugin list-profiles
282
+ npx feishu-user-plugin switch-profile <name>
283
+ npx feishu-user-plugin keepalive --all # 跨 profile keepalive
289
284
  ```
290
285
 
291
- ### OpenClaw
286
+ 读路径工具(`read_*` / `list_*` / `get_*` / `search_*` / `download_*`)失败码 91403 / 1254301 / 1254000 / 99991672 / HTTP 403 时自动跨 profile retry。写路径不自动切(避免错号创建资源)。
292
287
 
293
- Add to `~/.openclaw/openclaw.json` (note: key path is `mcp.servers`, not `mcpServers`):
288
+ 单调用覆盖:传 `via_profile: "<name>"` 钉到指定 profile,传 `via_profile: "auto"` 给写路径开自动切换。
294
289
 
295
- ```json
296
- {
297
- "mcp": {
298
- "servers": {
299
- "feishu-user-plugin": {
300
- "command": "npx",
301
- "args": ["-y", "feishu-user-plugin"],
302
- "env": {
303
- "LARK_COOKIE": "your-cookie-string",
304
- "LARK_APP_ID": "cli_xxxxxxxxxxxx",
305
- "LARK_APP_SECRET": "your-app-secret",
306
- "LARK_USER_ACCESS_TOKEN": "your-uat",
307
- "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
308
- }
309
- }
310
- }
311
- }
312
- }
313
- ```
314
-
315
- Or via CLI: `openclaw mcp set feishu-user-plugin '{"command":"npx","args":["-y","feishu-user-plugin"],"env":{...}}'`
290
+ 详见 [CLAUDE.md "Multi-profile auto-switch" 段](CLAUDE.md#multi-profile-auto-switch-v138)。
316
291
 
317
- > OpenClaw's built-in Feishu channel handles receiving messages (bot identity). This plugin adds user identity messaging + docs/bitable/calendar/tasks.
292
+ ## 实时事件(v1.3.9 机器级 SSOT)
318
293
 
319
- ### Windsurf
294
+ 机器上单进程持有 WS owner 锁(`~/.feishu-user-plugin/ws-owner.lock`,`O_CREAT|O_EXCL`,30s stale),所有 MCP 进程共享 `~/.feishu-user-plugin/events.jsonl`(10 MB 软 / 20 MB 硬限自动轮转),`events.cursor.json` 是全机所有 harness 共享的 drain cursor —— 每条事件全机恰好一次。
320
295
 
321
- Add to `~/.codeium/windsurf/mcp_config.json`:
322
-
323
- ```json
324
- {
325
- "mcpServers": {
326
- "feishu": {
327
- "command": "npx",
328
- "args": ["-y", "feishu-user-plugin"],
329
- "env": {
330
- "LARK_COOKIE": "your-cookie-string",
331
- "LARK_APP_ID": "cli_xxxxxxxxxxxx",
332
- "LARK_APP_SECRET": "your-app-secret",
333
- "LARK_USER_ACCESS_TOKEN": "your-uat",
334
- "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
335
- }
336
- }
337
- }
338
- }
296
+ ```bash
297
+ mcp call manage_ws_status --action info # 谁在持锁、当前订阅、events.jsonl 大小
298
+ mcp call manage_ws_status --action claim --force true # 跨进程抢锁
339
299
  ```
340
300
 
341
- ## Tools (80 total)
342
-
343
- ### User Identity -- Messaging (10 tools, cookie auth)
344
-
345
- | Tool | Description |
346
- |------|-------------|
347
- | `send_to_user` | Search user by name + send text -- one step |
348
- | `send_to_group` | Search group by name + send text -- one step |
349
- | `send_as_user` | Send text to any chat by ID, supports reply threading |
350
- | `send_image_as_user` | Send image (requires `image_key` from `upload_image`) |
351
- | `send_file_as_user` | Send file (requires `file_key` from `upload_file`) |
352
- | `send_post_as_user` | Send rich text with title + formatted paragraphs |
353
- | `batch_send` | Fan-out send to multiple targets in one call (text / image / file / post). v1.3.6 |
354
- | `send_card_as_user` | Send a Feishu interactive card. v1.3.6 default routes through bot identity; user-identity is reserved for v1.3.7. |
355
-
356
- ### User Identity -- Contacts & Info (5 tools, cookie auth)
357
-
358
- | Tool | Description |
359
- |------|-------------|
360
- | `search_contacts` | Search users, bots, or group chats by name |
361
- | `create_p2p_chat` | Create/get P2P (direct message) chat |
362
- | `get_chat_info` | Group details (supports both oc_xxx and numeric ID) |
363
- | `get_user_info` | User display name lookup by user ID |
364
- | `get_login_status` | Check cookie, app credentials, and UAT status |
365
-
366
- ### User OAuth UAT -- P2P Chat Reading (2 tools)
367
-
368
- | Tool | Description |
369
- |------|-------------|
370
- | `read_p2p_messages` | Read P2P (direct message) history |
371
- | `list_user_chats` | List group chats the user is in |
372
-
373
- ### Official API -- IM (17 tools)
374
-
375
- | Tool | Description |
376
- |------|-------------|
377
- | `list_chats` | List all chats the bot has joined |
378
- | `read_messages` | Read message history (accepts chat name, oc_xxx, or numeric ID) |
379
- | `send_message_as_bot` | Send message as bot to any chat |
380
- | `reply_message` | Reply to a specific message (as bot) |
381
- | `forward_message` | Forward a message to another chat |
382
- | `delete_message` | Recall/delete a bot message |
383
- | `update_message` | Edit a sent bot message |
384
- | `add_reaction` | Add emoji reaction to a message |
385
- | `delete_reaction` | Remove emoji reaction |
386
- | `pin_message` | Pin a message in chat |
387
- | `unpin_message` | Unpin a message |
388
- | `create_group` | Create a new group chat |
389
- | `update_group` | Update group name/description |
390
- | `list_members` | List group members |
391
- | `add_members` | Add users to a group |
392
- | `remove_members` | Remove users from a group |
393
- | `upload_image` / `upload_file` | Upload image/file, returns key for sending |
394
- | `download_message_resource` | v1.3.7 (C2.4): download a message-attached image or file. Args: `message_id`, `key`, `kind=image|file`, `save_path?`. **Required save_path when bytes > 2 MiB** (Anthropic 5 MB inline cap). Replaces v1.3.6 download_image (message mode) + download_file. |
395
- | `download_doc_image` | v1.3.7 (C2.4): download an image embedded in a docx (image_token + optional doc_token). Same 2 MiB cap. Replaces v1.3.6 download_image (docx mode). |
396
-
397
- ### Wiki, OKR, and Calendar (v1.3.4)
398
-
399
- | Tool | Description |
400
- |------|-------------|
401
- | `get_wiki_node` | Resolve a Wiki node token to its underlying obj_type + obj_token + space_id |
402
- | `list_user_okrs` | List a user's OKRs (requires open_id; filter by period_ids) |
403
- | `get_okrs` | Batch-fetch full OKR details (objectives, key results, progress, alignments) |
404
- | `list_okr_periods` | List OKR periods (quarters / years) |
405
- | `list_calendars` | List the current user's calendars (primary + shared + subscribed) |
406
- | `list_calendar_events` | List events in a calendar within a time range |
407
- | `get_calendar_event` | Full event details (attendees, location, meeting link, attachments) |
408
-
409
- All docx / bitable tools' `document_id` / `app_token` parameter also accepts a Wiki node token or a full Feishu URL — the plugin resolves it transparently.
410
-
411
- ### Official API -- Documents (5 tools)
412
-
413
- | Tool | Description |
414
- |------|-------------|
415
- | `search_docs` | Search documents by keyword |
416
- | `read_doc` | Read raw text content |
417
- | `get_doc_blocks` | Get structured block tree |
418
- | `create_doc` | Create a new document |
419
- | `manage_doc_block` | Insert / update / delete blocks (`action=create|update|delete`). Supports generic `children`, image (`image_path`/`image_token`), and file (`file_path`/`file_token`) shortcuts. v1.3.7 consolidates the v1.3.6 trio create_doc_block / update_doc_block / delete_doc_blocks. |
420
-
421
- ### Official API -- Bitable (6 tools, v1.3.7 consolidation)
422
-
423
- | Tool | Actions | Description |
424
- |------|---------|-------------|
425
- | `manage_bitable_app` | create / copy / get_meta | App-level operations (v1.3.7 consolidates create_bitable / copy_bitable / get_bitable_meta) |
426
- | `manage_bitable_table` | list / create / update / delete | Table CRUD (rename via update) |
427
- | `manage_bitable_field` | list / create / update / delete | Field (column) management. `type` required for both create AND update. |
428
- | `manage_bitable_view` | list / create / delete | Views (grid, kanban, gallery, form, gantt, calendar) |
429
- | `manage_bitable_record` | search / get / create / update / delete | Record CRUD. create/update/delete accept arrays — single record or up to 500/call. |
430
- | `upload_bitable_attachment` | — | Upload a file into a Bitable Attachment-type field. Returns `file_token` to write into the field as `[{file_token}]`. v1.3.6 |
431
-
432
- ### Official API -- Calendar (8 tools, write tools v1.3.7)
433
-
434
- | Tool | Description |
435
- |------|-------------|
436
- | `list_calendars` | List accessible calendars |
437
- | `list_calendar_events` | List events in a calendar |
438
- | `get_calendar_event` | Full event details |
439
- | `create_calendar_event` | Create an event (v1.3.7). Requires `calendar:calendar.event:write`. |
440
- | `update_calendar_event` | Patch event fields (v1.3.7) |
441
- | `delete_calendar_event` | Delete an event, optionally dissolve its meeting chat (v1.3.7) |
442
- | `respond_calendar_event` | RSVP as accept / decline / tentative (v1.3.7) |
443
- | `get_freebusy` | Freebusy lookup for `user_ids` in a time range (v1.3.7) |
444
-
445
- ### Official API -- Tasks v2 (7 tools, v1.3.7 new domain)
446
-
447
- Identifier is `task_guid` (not v1's numeric `task_id`). Requires `task:task` scope.
448
-
449
- | Tool | Description |
450
- |------|-------------|
451
- | `list_tasks` | List the current user's tasks (filter by completed / type) |
452
- | `get_task` | Full task detail |
453
- | `create_task` | Create a task (summary required; due/members optional) |
454
- | `update_task` | Patch fields. **`update_fields` is required** — Feishu only updates the listed keys. |
455
- | `complete_task` | Mark complete (or uncomplete with `completed=false`) |
456
- | `delete_task` | Permanent delete |
457
- | `manage_task_members` | `action=add|remove`, members `[{id, role:"assignee"|"follower"}]` |
458
-
459
- ### Official API -- Drive (4 tools)
460
-
461
- | Tool | Description |
462
- |------|-------------|
463
- | `list_files` | List files in a folder |
464
- | `create_folder` | Create a new folder |
465
- | `manage_drive_file` | Copy / move / delete a Drive file (`action=copy|move|delete`, `type` required). v1.3.7 consolidates v1.3.6 copy_file / move_file / delete_file. |
466
- | `upload_drive_file` | Upload a local file into a Drive folder (`drive/v1/files/upload_all`). Optional `wiki_space_id` attaches the upload as a Wiki node atomically. v1.3.6 |
467
-
468
- ### Official API -- Wiki (8 tools)
469
-
470
- | Tool | Description |
471
- |------|-------------|
472
- | `list_wiki_spaces` / `search_wiki` / `list_wiki_nodes` / `get_wiki_node` | Wiki spaces, search, browse + resolve a wiki node to underlying obj_token |
473
- | `create_wiki_node` | Create a new wiki node (doc/sheet/bitable/mindnote/file/docx/slides) inside a space |
474
- | `update_wiki_node` | Rename a wiki node (title only — content edits via docx/bitable tools) |
475
- | `move_wiki_node` | Move a wiki node to a different parent or different space |
476
- | `copy_wiki_node` | Deep-copy a wiki node to a different location (optionally to a different space) |
477
-
478
- ### Plugin -- Profiles (3 tools, v1.3.6 + v1.3.8)
479
-
480
- | Tool | Description |
481
- |------|-------------|
482
- | `list_profiles` | List available identity profiles (default + extras from `LARK_PROFILES_JSON`) and the active one |
483
- | `switch_profile` | Hot-swap active profile; cached client instances rebuild against new credentials |
484
- | `manage_profile_hints` | Inspect/set/clear the resourceKey → profile cache used by the v1.3.8 auto-switch middleware |
485
-
486
- ### Multi-profile auto-switch (v1.3.8)
487
-
488
- When `~/.feishu-user-plugin/credentials.json` has more than one profile, the plugin auto-switches between them on **read** paths when the active profile gets a permission-denied error. The winning profile is cached per resource so subsequent calls go straight to the right account.
489
-
490
- **Whitelist** -- only `read_*`, `list_*`, `get_*`, `search_*`, `download_*` (and the read-action variants of `manage_bitable_*`) get auto-retry. Writes never auto-switch -- they fail loud so you don't accidentally create resources under the wrong account.
491
-
492
- **Triggers** -- error codes 91403, 1254301, 1254000, 99991672, HTTP 403, plus message patterns `access_denied / permission_denied / docx_no_permission / no permission / forbidden`.
493
-
494
- **Per-call override** -- pass `via_profile: "<name>"` in any tool call to pin to that profile (no auto-switch). Pass `via_profile: "auto"` to opt **into** auto-switch on a write call (escape hatch -- be careful).
495
-
496
- **Cache management** -- `manage_profile_hints(action="list" | "set" | "clear", resource_key?, profile?)` lets you inspect or edit the cache.
301
+ 默认订阅 `["im.message.receive_v1"]`。要订阅其他事件(审批 / 日历 / vc / etc),编辑 `credentials.json::profiles[<active>].events`,然后 `manage_ws_status(action=reconfig)` 不重启重新订阅。
497
302
 
498
- Single-profile users (the vast majority): zero behaviour change -- the router short-circuits and `manage_profile_hints` is a no-op.
499
-
500
- ## Claude Code Slash Commands (9 skills)
303
+ 仅支持 feishu.cn —— Lark 国际版(lark.com)的 WSClient 当前不支持。
501
304
 
502
- This plugin includes 9 built-in skills in `skills/feishu-user-plugin/`:
305
+ ## 工程细节
503
306
 
504
- | Skill | Usage | Description |
505
- |-------|-------|-------------|
506
- | `/send` | `/send Alice: meeting at 3pm` | Send message as yourself |
507
- | `/reply` | `/reply engineering-chat` | Read recent messages and reply |
508
- | `/digest` | `/digest engineering-chat 7` | Summarize recent chat messages |
509
- | `/search` | `/search engineering` | Search contacts and groups |
510
- | `/doc` | `/doc search MCP` | Search, read, or create documents |
511
- | `/table` | `/table query appXxx` | Query or create Bitable records |
512
- | `/wiki` | `/wiki search protocol` | Search and browse wiki |
513
- | `/drive` | `/drive list folderToken` | List files or create folders in Drive |
514
- | `/status` | `/status` | Check login and auth status |
307
+ ### Token 生命周期
515
308
 
516
- Skills are automatically available when the plugin is installed.
309
+ | 鉴权层 | Token | 有效期 | 续期 |
310
+ |---|---|---|---|
311
+ | Cookie | `sl_session` | 12h max-age | 4h 心跳自动刷新 |
312
+ | App | `tenant_access_token` | 2h | SDK 自动管理 |
313
+ | User OAuth | `user_access_token` | ~2h | refresh_token 自动刷新,写回 credentials.json |
314
+ | Refresh Token | — | 7 天 | `keepalive` cron 防过期 |
517
315
 
518
- ## Architecture
519
-
520
- ```
521
- Cookie + Proto ┌──────────────────────────────────────┐
522
- ────────────────── >│ internal-api-lark-api.feishu.cn │
523
- ┌──────────────┐ │ /im/gateway/ (Protobuf over HTTP) │
524
- │ MCP Client │ └──────────────────────────────────────┘
525
- │ (Claude, │ App Token (REST) ┌──────────────────────────────────────┐
526
- │ Cursor, │ ────────────────->│ open.feishu.cn/open-apis/ │
527
- │ VS Code) │ │ (Official REST API) │
528
- │ │ └──────────────────────────────────────┘
529
- │ │ User OAuth (REST)┌──────────────────────────────────────┐
530
- │ │ ────────────────->│ open.feishu.cn/open-apis/ │
531
- └──────────────┘ │ (UAT -- P2P chat reading) │
532
- └──────────────────────────────────────┘
316
+ ```bash
317
+ crontab -e
318
+ # 0 */4 * * * npx feishu-user-plugin keepalive >> /tmp/feishu-keepalive.log 2>&1
533
319
  ```
534
320
 
535
- ## Session & Token Lifecycle
321
+ UAT 刷新失败 `invalid_grant` —— refresh token 过期 / 被撤销,重跑 `npx feishu-user-plugin oauth` 然后重启 Claude Code / Codex。
536
322
 
537
- | Auth Layer | Token | Lifetime | Refresh |
538
- |------------|-------|----------|---------|
539
- | Cookie | `sl_session` | 12h max-age | Auto-refreshed every 4h via heartbeat |
540
- | App Token | `tenant_access_token` | 2h | Auto-managed by SDK |
541
- | User OAuth | `user_access_token` | ~2h | Auto-refreshed via `refresh_token`, saved to MCP config |
323
+ ### 凭证存储(v1.3.7+)
542
324
 
543
- When the cookie expires (after ~12-24h without heartbeat), re-login at feishu.cn and update `LARK_COOKIE`. Use `get_login_status` to check health proactively.
325
+ 单一可信源 `~/.feishu-user-plugin/credentials.json`(mode 0600),多 harness 共享。schema [docs/CREDENTIALS-FORMAT.md](docs/CREDENTIALS-FORMAT.md)。
544
326
 
545
- If UAT refresh fails with `invalid_grant`, re-run `npx feishu-user-plugin oauth` and restart Claude Code / Codex. v1.3.5+ also re-reads the persisted MCP config before refreshing, so duplicate MCP processes can adopt a token already rotated by another process instead of retrying a stale refresh token.
546
-
547
- ## Project Structure
548
-
549
- ```
550
- feishu-user-plugin/
551
- ├── .claude-plugin/
552
- │ └── plugin.json # Plugin metadata
553
- ├── skills/
554
- │ └── feishu-user-plugin/
555
- │ ├── SKILL.md # Main skill definition (trigger, tools, auth)
556
- │ └── references/ # 8 skill reference docs + CLAUDE.md
557
- ├── src/
558
- │ ├── index.js # MCP server entry point (78 tools)
559
- │ ├── client.js # User identity client (Protobuf gateway)
560
- │ ├── official.js # Official API client (REST, UAT)
561
- │ ├── utils.js # ID generators, cookie parser
562
- │ ├── oauth.js # OAuth flow for user_access_token
563
- │ ├── test-send.js # Quick CLI test
564
- │ └── test-all.js # Full test suite
565
- ├── proto/
566
- │ └── lark.proto # Protobuf message definitions
567
- ├── .mcp.json.example # MCP server config template
568
- ├── server.json # MCP Registry manifest
569
- ├── .env.example # Configuration template
570
- └── package.json
327
+ ```bash
328
+ npx feishu-user-plugin migrate # dry-run
329
+ npx feishu-user-plugin migrate --confirm # 真写
571
330
  ```
572
331
 
573
- ## Limitations
574
-
575
- - Cookie-based auth requires periodic refresh (auto-heartbeat extends to ~12h; manual re-login needed after that)
576
- - Depends on Feishu's internal Protobuf protocol -- may break if Feishu updates their web client
577
- - Image/file/audio sending requires pre-uploaded keys (upload via Official API or external bridge)
578
- - No real-time message receiving (WebSocket push not yet implemented)
579
- - May violate Feishu's Terms of Service -- use at your own risk
332
+ ### 自动 sync hooks
580
333
 
581
- ## Contributing
334
+ | 阶段 | 触发文件 | 作用 |
335
+ |---|---|---|
336
+ | pre-commit | `CLAUDE.md` staged | 同步到 `AGENTS.md` + skill 引用 |
337
+ | pre-commit | `package.json` / `plugin.json` / `SKILL.md` staged | 三角等价检查(version 必须一致) |
338
+ | pre-commit | `src/server.js` / `src/tools/*` staged | 工具个数 + README 84 tools 徽章必须一致 |
339
+ | pre-commit | `src/*` staged | smoke test |
340
+ | post-merge (main) | 任意 | 自动开 team-skills sync PR |
582
341
 
583
- Issues and PRs welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, code style, and submission guidelines.
342
+ CI(`.github/workflows/validate.yml`)每个 PR 跑同样的 gate。
584
343
 
585
- If Feishu updates their protocol and something breaks, please [open an issue](https://github.com/EthanQC/feishu-user-plugin/issues/new?template=bug_report.md) with the error details.
344
+ ## 已知限制
586
345
 
587
- ### Automated sync hooks
346
+ - **Cookie 寿命**:12-24 小时无心跳过期,需重新登录 feishu.cn 拿 cookie
347
+ - **协议变化**:cookie + protobuf 层依赖飞书 web 客户端的协议,飞书更新可能失效(机器人能力不受影响)
348
+ - **卡片**:cookie 通道发卡片服务端不可用,机器人通道可发
349
+ - **Lark 国际版**:实时事件 WS 不支持
350
+ - **未实现**:`search_messages`(v1.3.10 计划)、md → wiki 同步(v1.3.10 主线)
588
351
 
589
- This repo uses husky to enforce several invariants on every commit:
352
+ 完整 ROADMAP [ROADMAP.md](ROADMAP.md)。
590
353
 
591
- - **CLAUDE.md sync** — staging `CLAUDE.md` automatically regenerates `AGENTS.md` (identical body, different first line) and `skills/feishu-user-plugin/references/CLAUDE.md` (verbatim copy). Both are re-staged in the same commit.
592
- - **Version triangle** — if `package.json`, `.claude-plugin/plugin.json`, or `skills/feishu-user-plugin/SKILL.md` are staged, all three `version` fields must agree or the commit is rejected.
593
- - **Tool-count badge** — if `src/server.js` or any file under `src/tools/` is staged, the `N tools` badge in `README.md` must match the actual `TOOLS.length` exported by `src/server.js`.
594
- - **Smoke test** — any change under `src/` triggers `npm run smoke` to catch schema regressions before commit.
354
+ ## 贡献
595
355
 
596
- CI (`.github/workflows/validate.yml`) runs the same checks on every PR to `main`, so bypassing the local hook still gets caught.
356
+ Issues / PR 欢迎。提交前先看 [CONTRIBUTING.md](CONTRIBUTING.md)。
597
357
 
598
- On the maintainer's machine, a post-merge hook (`scripts/sync-team-skills.sh`) auto-opens a sync PR in the `~/team-skills` repo after every merge to main. The hook silently skips if `~/team-skills` is absent.
358
+ 飞书改协议导致功能挂掉 —— issue 带错误日志即可。
599
359
 
600
360
  ## License
601
361
 
602
362
  [MIT](LICENSE)
603
363
 
604
- ## Acknowledgments
364
+ ## 致谢
605
365
 
606
- - [cv-cat/LarkAgentX](https://github.com/cv-cat/LarkAgentX) -- Original Feishu protocol reverse-engineering (Python)
607
- - [cv-cat/OpenFeiShuApis](https://github.com/cv-cat/OpenFeiShuApis) -- Underlying API research
608
- - [Model Context Protocol](https://modelcontextprotocol.io) -- The MCP standard
366
+ - [cv-cat/LarkAgentX](https://github.com/cv-cat/LarkAgentX) —— 早期飞书 web 协议研究(Python
367
+ - [cv-cat/OpenFeiShuApis](https://github.com/cv-cat/OpenFeiShuApis) —— 底层 API 研究
368
+ - [Model Context Protocol](https://modelcontextprotocol.io) —— MCP 标准 + Anthropic / PulseMCP / GitHub / Stacklok 共维 registry