feishu-user-plugin 1.3.10 → 1.3.12
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.
- package/.claude-plugin/plugin.json +2 -2
- package/.cursor-plugin/plugin.json +27 -0
- package/.mcpb/manifest.json +91 -0
- package/CHANGELOG.md +118 -0
- package/PRIVACY.md +105 -0
- package/README.en.md +130 -413
- package/README.md +88 -258
- package/package.json +5 -3
- package/scripts/build-mcpb.js +119 -0
- package/scripts/check-description-drift.js +73 -0
- package/scripts/check-docs-sync.js +7 -16
- package/scripts/check-mcp-registry-version.js +43 -0
- package/scripts/check-mcpb-version.js +33 -0
- package/scripts/check-scopes.js +99 -0
- package/scripts/check-tool-count.js +4 -3
- package/scripts/check-version.js +5 -0
- package/scripts/sync-claude-md.sh +3 -4
- package/scripts/sync-team-skills.sh +72 -57
- package/scripts/verify-app-name.js +64 -0
- package/skills/feishu-user-plugin/SKILL.md +3 -3
- package/skills/feishu-user-plugin/references/search.md +3 -3
- package/src/auth/credentials-monitor.js +185 -0
- package/src/auth/credentials.js +49 -0
- package/src/auth/identity-state.js +204 -0
- package/src/auth/lark-desktop.js +135 -0
- package/src/auth/uat.js +49 -35
- package/src/cli.js +87 -0
- package/src/clients/official/base.js +145 -14
- package/src/clients/official/calendar.js +3 -1
- package/src/clients/official/im.js +76 -2
- package/src/clients/official/okr.js +2 -1
- package/src/error-codes.js +40 -0
- package/src/events/lockfile.js +40 -4
- package/src/events/owner.js +11 -2
- package/src/index.js +1 -1
- package/src/logger.js +11 -5
- package/src/oauth.js +46 -10
- package/src/server.js +102 -37
- package/src/setup.js +44 -0
- package/src/test-all.js +40 -0
- package/src/test-cli-tool.js +87 -0
- package/src/test-credentials-monitor.js +124 -0
- package/src/test-display-label.js +88 -0
- package/src/test-error-codes.js +85 -0
- package/src/test-identity-state.js +172 -0
- package/src/test-lark-desktop.js +300 -0
- package/src/test-lockfile-pid.js +90 -0
- package/src/test-lru-cache.js +145 -0
- package/src/test-negative-cache.js +85 -0
- package/src/test-populate-sender-names.js +98 -0
- package/src/test-search-messages.js +101 -0
- package/src/test-send-shape.js +115 -0
- package/src/test-via-user.js +94 -0
- package/src/test-with-uat-retry.js +135 -0
- package/src/tools/_registry.js +24 -1
- package/src/tools/calendar.js +5 -5
- package/src/tools/im-read.js +52 -4
- package/src/tools/messaging-user.js +1 -1
- package/src/utils.js +83 -0
- package/scripts/generate-og-image.js +0 -39
- package/skills/feishu-user-plugin/references/CLAUDE.md +0 -523
package/README.md
CHANGED
|
@@ -1,41 +1,34 @@
|
|
|
1
|
-
# feishu-user-plugin
|
|
1
|
+
# feishu-user-plugin —— 飞书 MCP 服务器 + CLI 工具
|
|
2
2
|
|
|
3
3
|
[](LICENSE)
|
|
4
4
|
[](https://nodejs.org)
|
|
5
5
|
[](https://modelcontextprotocol.io)
|
|
6
|
-
[](docs/TOOLS.md)
|
|
7
7
|
[](https://www.npmjs.com/package/feishu-user-plugin)
|
|
8
8
|
[](CONTRIBUTING.md)
|
|
9
9
|
|
|
10
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)
|
|
11
11
|
|
|
12
|
-
飞书 / Lark MCP 服务器,覆盖 IM、文档、多维表格、知识库、云空间、日历、任务 v2、OKR、实时事件。**
|
|
12
|
+
飞书 / Lark MCP 服务器,覆盖 IM、文档、多维表格、知识库、云空间、日历、任务 v2、OKR、实时事件。**85 工具 · 3 层鉴权 · 9 MCP prompts · MIT licensed · Node ≥18**。
|
|
13
13
|
|
|
14
14
|
兼容 Claude Code、Codex、Cursor、Windsurf、VS Code、Claude Desktop、OpenClaw 等 MCP 客户端。
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
用户身份发消息有两条路径:**飞书官方 OAuth scope `im:message.send_as_user`**(需要创建自建应用 + 管理员审批),或本仓的 **cookie + protobuf 路径**(零应用门槛,cookie 抓出来就跑)。本仓不再是物理性独家,但仍然是"个人开发者 / 没有管理员权限 / 想快速试"场景的简便选项。
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## 与官方对比(飞书 2026 年也发了 MCP + CLI)
|
|
19
19
|
|
|
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 |
|
|
20
|
+
- [`larksuite/lark-openapi-mcp`](https://github.com/larksuite/lark-openapi-mcp) —— 官方 OpenAPI MCP,**⚠ Beta** + 最后更新 2025-08(9 个月前),README 明文不支持文件上传下载、不支持文档编辑;1271 个 endpoint 工具但 preset.default 仅 ~20,其余"未做兼容性测试"
|
|
21
|
+
- [`larksuite/cli`](https://github.com/larksuite/cli) —— 官方 CLI(9.9k stars,活跃),17 业务域 200+ commands + 24 AI Agent Skills,**已支持 `+messages-send --as user`**(走 OAuth scope `im:message.send_as_user`),但 **CLI 形态而不是 MCP**,Codex / Cursor / Windsurf 等用它要 shell out
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## 安装
|
|
23
|
+
**什么时候用本仓**:
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# 重启 Claude Code / Codex
|
|
34
|
-
```
|
|
25
|
+
- 想以用户身份发消息 / 读 P2P 私聊但**不想 / 不能创建飞书自建应用**(个人开发者 / 没管理员权限)—— cookie 路径零门槛
|
|
26
|
+
- 用 MCP 协议(Codex / Cursor / Windsurf / VS Code 等)+ 不需要邮件 / 审批 / HR / 会议纪要等本仓未覆盖的域
|
|
27
|
+
- 多 MCP 客户端共存且需要"实时事件全机精确投递一次"(v1.3.9+ 机器级 WS SSOT)
|
|
35
28
|
|
|
36
|
-
|
|
29
|
+
**什么时候用官方**:需要邮件 / 审批 / 考勤 / HR / 招聘 / 会议纪要等业务系统域;或已有飞书应用 + 管理员批了 OAuth scope,偏好官方长期稳定路径。
|
|
37
30
|
|
|
38
|
-
|
|
31
|
+
完整诚实对比见 [docs/COMPARISON.md](docs/COMPARISON.md)。
|
|
39
32
|
|
|
40
33
|
## 用法
|
|
41
34
|
|
|
@@ -49,172 +42,36 @@ Claude:[调用 send_to_user] Sent
|
|
|
49
42
|
Claude:[read_messages → 总结 → send_to_group] Sent
|
|
50
43
|
```
|
|
51
44
|
|
|
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)
|
|
45
|
+
## 安装
|
|
177
46
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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 查询 |
|
|
47
|
+
```bash
|
|
48
|
+
npx feishu-user-plugin setup --app-id <APP_ID> --app-secret <APP_SECRET>
|
|
49
|
+
npx feishu-user-plugin oauth # 拿用户 OAuth UAT
|
|
50
|
+
# 重启 Claude Code / Codex
|
|
51
|
+
```
|
|
188
52
|
|
|
189
|
-
|
|
53
|
+
cookie 获取(Playwright 自动扫码 / DevTools 手动)、创建飞书应用、各客户端配置详见 [docs/AUTH-SETUP.md](docs/AUTH-SETUP.md)。
|
|
190
54
|
|
|
191
|
-
|
|
55
|
+
## 三层鉴权
|
|
192
56
|
|
|
193
|
-
|
|
|
194
|
-
|
|
195
|
-
| `
|
|
196
|
-
| `
|
|
197
|
-
| `
|
|
198
|
-
| `update_task` | 改任务(必传 `update_fields=[...]`,飞书只 patch 列出字段) |
|
|
199
|
-
| `complete_task` | 完成 / 取消完成 |
|
|
200
|
-
| `delete_task` | 删 |
|
|
201
|
-
| `manage_task_members` | add / remove 成员(assignee / follower) |
|
|
57
|
+
| 鉴权层 | 凭证 | 覆盖能力 | 工具数 |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| 用户身份(cookie + protobuf) | `LARK_COOKIE` | 以用户身份发文本 / 图片 / 文件 / 富文本 / @ / 批量 | 8 |
|
|
60
|
+
| 官方 API(机器人) | `LARK_APP_ID` + `LARK_APP_SECRET` | 群消息读写、文档、多维表格、知识库、云空间、日历、任务 v2、OKR、联系人、实时事件 WS | 70+ |
|
|
61
|
+
| 用户 OAuth UAT | `LARK_USER_ACCESS_TOKEN` + `LARK_USER_REFRESH_TOKEN` | P2P 私聊读取、用户 chat 列表;写入文档 / Bitable / 日历 资源时以用户为 owner | 2 显式 + 全工具 UAT-first |
|
|
202
62
|
|
|
203
|
-
|
|
63
|
+
三层独立 —— 配置任意一层,对应工具可用。
|
|
204
64
|
|
|
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) |
|
|
65
|
+
## 核心能力
|
|
211
66
|
|
|
212
|
-
|
|
67
|
+
- **以你身份发消息**(8):text / image / file / 富文本 post / 卡片 / 批量;差异化锚点 —— 飞书官方 API 没有 `send_as_user`
|
|
68
|
+
- **读群与 P2P 私聊**(17):群消息 / 私聊 / `merge_forward` 自动展开 / URL + 飞书文档链接自动提取 / 外部群自动 fallback 到 UAT
|
|
69
|
+
- **文档生态**(27):飞书文档(含 `read_doc_markdown` 省 ~60% token)/ 多维表格(500 条批量)/ 知识库(含 write CRUD)/ 云空间
|
|
70
|
+
- **协作工具**(21):日历(读+写)/ 任务 v2(含成员管理)/ OKR(读+进展记录)/ 联系人
|
|
71
|
+
- **实时事件**(2):机器级 SSOT WS,每条事件全机精确投递一次
|
|
72
|
+
- **诊断与多账号**(4):N 个 profile 自动切换,写路径不切(避免错号建资源)
|
|
213
73
|
|
|
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 轮转 / 不重启重新订阅) |
|
|
74
|
+
完整工具列表 + 跨域 caveat + 用法 patterns 见 [docs/TOOLS.md](docs/TOOLS.md)。
|
|
218
75
|
|
|
219
76
|
## 9 个 MCP prompts(slash commands)
|
|
220
77
|
|
|
@@ -232,48 +89,27 @@ Claude:[read_messages → 总结 → send_to_group] Sent
|
|
|
232
89
|
|
|
233
90
|
## 客户端配置
|
|
234
91
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
**统一 env 块**:
|
|
238
|
-
|
|
239
|
-
```json
|
|
240
|
-
{
|
|
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"
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
**安放位置**:
|
|
92
|
+
环境变量统一,配置文件位置和顶层键不同:
|
|
254
93
|
|
|
255
94
|
| 客户端 | 配置文件 | 顶层键 |
|
|
256
95
|
|---|---|---|
|
|
257
|
-
| Claude Code | `~/.claude.json
|
|
96
|
+
| Claude Code | `~/.claude.json`(推荐全局)/ `.mcp.json` | `mcpServers.feishu-user-plugin` |
|
|
258
97
|
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) | `mcpServers.feishu` |
|
|
259
|
-
| Codex | `~/.codex/config.toml` | `[mcp_servers.feishu-user-plugin]
|
|
98
|
+
| Codex | `~/.codex/config.toml` | `[mcp_servers.feishu-user-plugin]` |
|
|
260
99
|
| Cursor | `.cursor/mcp.json`(项目级) | `mcpServers.feishu` |
|
|
261
|
-
| VS Code (Copilot) | `.vscode/mcp.json` | `servers.feishu
|
|
100
|
+
| VS Code (Copilot) | `.vscode/mcp.json` | `servers.feishu`(注意 `servers`,非 `mcpServers`) |
|
|
262
101
|
| OpenClaw | `~/.openclaw/openclaw.json` | `mcp.servers.feishu-user-plugin` |
|
|
263
102
|
| Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers.feishu` |
|
|
264
103
|
|
|
265
|
-
**自动化设置**:
|
|
266
|
-
|
|
267
104
|
```bash
|
|
268
|
-
npx feishu-user-plugin setup #
|
|
269
|
-
npx feishu-user-plugin setup --client codex # Codex
|
|
270
|
-
npx feishu-user-plugin setup --client both #
|
|
271
|
-
npx feishu-user-plugin setup --activate # 激活当前 profile
|
|
105
|
+
npx feishu-user-plugin setup # Claude Code
|
|
106
|
+
npx feishu-user-plugin setup --client codex # Codex
|
|
107
|
+
npx feishu-user-plugin setup --client both # 都写
|
|
272
108
|
```
|
|
273
109
|
|
|
274
|
-
各客户端完整 JSON 模板见 [README.en.md `MCP Client Configuration`
|
|
110
|
+
各客户端完整 JSON 模板见 [README.en.md `MCP Client Configuration`](README.en.md#mcp-client-configuration);详细安装与凭证流程见 [docs/AUTH-SETUP.md](docs/AUTH-SETUP.md)。
|
|
275
111
|
|
|
276
|
-
##
|
|
112
|
+
## 多账号
|
|
277
113
|
|
|
278
114
|
`~/.feishu-user-plugin/credentials.json` 支持多 profile(默认 + 任意附加),单台机器一处配置覆盖多个飞书账号 / 多个企业。
|
|
279
115
|
|
|
@@ -283,73 +119,47 @@ npx feishu-user-plugin switch-profile <name>
|
|
|
283
119
|
npx feishu-user-plugin keepalive --all # 跨 profile keepalive
|
|
284
120
|
```
|
|
285
121
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
单调用覆盖:传 `via_profile: "<name>"` 钉到指定 profile,传 `via_profile: "auto"` 给写路径开自动切换。
|
|
122
|
+
读路径工具失败码 `91403` / `1254301` / `1254000` / `99991672` / `HTTP 403` 时自动跨 profile retry。写路径不自动切(避免错号创建资源)。单调用覆盖:传 `via_profile: "<name>"` 钉到指定 profile。
|
|
289
123
|
|
|
290
|
-
详见 [
|
|
124
|
+
详见 [docs/TOOLS.md "多 profile auto-switch"](docs/TOOLS.md#多-profile-auto-switchv138)。
|
|
291
125
|
|
|
292
|
-
##
|
|
126
|
+
## 实时事件
|
|
293
127
|
|
|
294
|
-
机器上单进程持有 WS owner
|
|
128
|
+
机器上单进程持有 WS owner 锁,所有 MCP 进程共享 `events.jsonl`,每条事件全机恰好一次。
|
|
295
129
|
|
|
296
130
|
```bash
|
|
297
|
-
mcp call manage_ws_status --action info
|
|
298
|
-
mcp call manage_ws_status --action claim --force true
|
|
131
|
+
mcp call manage_ws_status --action info
|
|
132
|
+
mcp call manage_ws_status --action claim --force true
|
|
299
133
|
```
|
|
300
134
|
|
|
301
|
-
默认订阅 `["im.message.receive_v1"]
|
|
135
|
+
默认订阅 `["im.message.receive_v1"]`。要订阅审批 / 日历 / vc 等其他事件,编辑 `credentials.json::profiles[<active>].events`,然后 `manage_ws_status(action=reconfig)` 不重启重新订阅。
|
|
302
136
|
|
|
303
137
|
仅支持 feishu.cn —— Lark 国际版(lark.com)的 WSClient 当前不支持。
|
|
304
138
|
|
|
305
|
-
## 工程细节
|
|
306
|
-
|
|
307
|
-
### Token 生命周期
|
|
308
|
-
|
|
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 防过期 |
|
|
315
|
-
|
|
316
|
-
```bash
|
|
317
|
-
crontab -e
|
|
318
|
-
# 0 */4 * * * npx feishu-user-plugin keepalive >> /tmp/feishu-keepalive.log 2>&1
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
UAT 刷新失败 `invalid_grant` —— refresh token 过期 / 被撤销,重跑 `npx feishu-user-plugin oauth` 然后重启 Claude Code / Codex。
|
|
322
|
-
|
|
323
|
-
### 凭证存储(v1.3.7+)
|
|
324
|
-
|
|
325
|
-
单一可信源 `~/.feishu-user-plugin/credentials.json`(mode 0600),多 harness 共享。schema 见 [docs/CREDENTIALS-FORMAT.md](docs/CREDENTIALS-FORMAT.md)。
|
|
326
|
-
|
|
327
|
-
```bash
|
|
328
|
-
npx feishu-user-plugin migrate # dry-run
|
|
329
|
-
npx feishu-user-plugin migrate --confirm # 真写
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
### 自动 sync hooks
|
|
333
|
-
|
|
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 |
|
|
341
|
-
|
|
342
|
-
CI(`.github/workflows/validate.yml`)每个 PR 跑同样的 gate。
|
|
343
|
-
|
|
344
139
|
## 已知限制
|
|
345
140
|
|
|
346
141
|
- **Cookie 寿命**:12-24 小时无心跳过期,需重新登录 feishu.cn 拿 cookie
|
|
347
142
|
- **协议变化**:cookie + protobuf 层依赖飞书 web 客户端的协议,飞书更新可能失效(机器人能力不受影响)
|
|
348
143
|
- **卡片**:cookie 通道发卡片服务端不可用,机器人通道可发
|
|
349
144
|
- **Lark 国际版**:实时事件 WS 不支持
|
|
350
|
-
- **未实现**:`search_messages
|
|
145
|
+
- **未实现**:`search_messages`、md → wiki 同步(详见 [ROADMAP.md](ROADMAP.md))
|
|
146
|
+
|
|
147
|
+
## 文档
|
|
148
|
+
|
|
149
|
+
| 文档 | 角色 |
|
|
150
|
+
|------|------|
|
|
151
|
+
| [docs/TOOLS.md](docs/TOOLS.md) | 工具详细 + 跨域 caveat + 用法 patterns |
|
|
152
|
+
| [docs/AUTH-SETUP.md](docs/AUTH-SETUP.md) | 安装 / 三层鉴权 / Cookie 抓取 / OAuth Scopes |
|
|
153
|
+
| [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) | 错误码与诊断 |
|
|
154
|
+
| [docs/RELEASING.md](docs/RELEASING.md) | 发版流程 + team-skills 同步 + 公告规则 |
|
|
155
|
+
| [docs/REFACTOR-NOTES.md](docs/REFACTOR-NOTES.md) | 文件职责矩阵 |
|
|
156
|
+
| [docs/CREDENTIALS-FORMAT.md](docs/CREDENTIALS-FORMAT.md) | 凭证 schema |
|
|
157
|
+
| [docs/TESTING-METHODOLOGY.md](docs/TESTING-METHODOLOGY.md) | 测试方法 |
|
|
158
|
+
| [CONTRIBUTING.md](CONTRIBUTING.md) | 贡献流程(中英双语) |
|
|
159
|
+
| [ROADMAP.md](ROADMAP.md) | 路线图(forward-only) |
|
|
160
|
+
| [CHANGELOG.md](CHANGELOG.md) | 历史变更 |
|
|
351
161
|
|
|
352
|
-
完整
|
|
162
|
+
完整 docs/ 索引:[docs/README.md](docs/README.md)。
|
|
353
163
|
|
|
354
164
|
## 贡献
|
|
355
165
|
|
|
@@ -357,6 +167,26 @@ Issues / PR 欢迎。提交前先看 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
|
|
357
167
|
|
|
358
168
|
飞书改协议导致功能挂掉 —— 开 issue 带错误日志即可。
|
|
359
169
|
|
|
170
|
+
## 隐私 / Privacy
|
|
171
|
+
|
|
172
|
+
本地运行的 MCP 服务器,凭证留在用户本机,不上报遥测,不与插件作者后台通信。完整文本见 [PRIVACY.md](PRIVACY.md)。
|
|
173
|
+
|
|
174
|
+
- **收集**:插件本身不收集任何数据;`LARK_COOKIE` / `LARK_APP_ID` / `LARK_APP_SECRET` / `LARK_USER_ACCESS_TOKEN` / `LARK_USER_REFRESH_TOKEN` 全部由用户主动配置,来源于用户自己的飞书 / Lark 账号
|
|
175
|
+
- **处理**:仅处理用户通过 MCP 工具调用主动请求的消息 / 文档 / 多维表格 / 知识库 / 云空间 / 日历 / 任务 / OKR / 联系人,不留存、不分析
|
|
176
|
+
- **存储**:`~/.feishu-user-plugin/credentials.json`(mode 0600);可选事件日志 `~/.feishu-user-plugin/events.jsonl`(10 MB / 20 MB 自动轮转)
|
|
177
|
+
- **第三方**:仅与用户自己的飞书租户和用户运行的 AI 客户端通信,无 CDN / 分析 / 错误上报
|
|
178
|
+
- **保留**:完全用户控制;`rm -rf ~/.feishu-user-plugin && npm uninstall -g feishu-user-plugin` 即清空
|
|
179
|
+
- **联系**:[GitHub Issues](https://github.com/EthanQC/feishu-user-plugin/issues),安全问题在 issue 标题前加 `[security]`
|
|
180
|
+
|
|
181
|
+
A locally-run MCP server. Credentials stay on the user's machine; no telemetry, no phone-home. Full text at [PRIVACY.md](PRIVACY.md).
|
|
182
|
+
|
|
183
|
+
- **Collected**: nothing by the plugin itself; the five `LARK_*` envs are supplied by the user from their own Feishu / Lark account
|
|
184
|
+
- **Processed**: only the messages / docs / bitable / wiki / drive / calendar / tasks / OKR / contacts the user explicitly requests via MCP tool calls
|
|
185
|
+
- **Stored**: `~/.feishu-user-plugin/credentials.json` (mode 0600); optional event log at `~/.feishu-user-plugin/events.jsonl`
|
|
186
|
+
- **Third-party**: only the user's own Feishu tenant and the AI client the user runs (Claude Code / Codex / Cursor / etc.)
|
|
187
|
+
- **Retention**: entirely user-controlled; `rm -rf ~/.feishu-user-plugin && npm uninstall -g feishu-user-plugin` removes everything
|
|
188
|
+
- **Contact**: [GitHub Issues](https://github.com/EthanQC/feishu-user-plugin/issues); security disclosures with `[security]` prefix in the title
|
|
189
|
+
|
|
360
190
|
## License
|
|
361
191
|
|
|
362
192
|
[MIT](LICENSE)
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "feishu-user-plugin",
|
|
3
3
|
"mcpName": "io.github.EthanQC/feishu-user-plugin",
|
|
4
|
-
"version": "1.3.
|
|
5
|
-
"description": "All-in-one Feishu MCP server for Claude Code
|
|
4
|
+
"version": "1.3.12",
|
|
5
|
+
"description": "All-in-one Feishu MCP server + CLI tool for Claude Code / Codex / Cursor / scripts — 85 tools across 3 auth layers (cookie / app / OAuth). Send as you, read groups, manage docs / bitable / wiki / drive / calendar / tasks / OKR.",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"feishu-user-plugin": "src/cli.js"
|
|
@@ -45,12 +45,15 @@
|
|
|
45
45
|
"proto/",
|
|
46
46
|
"scripts/",
|
|
47
47
|
".claude-plugin/",
|
|
48
|
+
".cursor-plugin/",
|
|
49
|
+
".mcpb/",
|
|
48
50
|
"skills/",
|
|
49
51
|
".mcp.json.example",
|
|
50
52
|
".env.example",
|
|
51
53
|
"CHANGELOG.md",
|
|
52
54
|
"README.md",
|
|
53
55
|
"README.en.md",
|
|
56
|
+
"PRIVACY.md",
|
|
54
57
|
"LICENSE"
|
|
55
58
|
],
|
|
56
59
|
"engines": {
|
|
@@ -64,7 +67,6 @@
|
|
|
64
67
|
"protobufjs": "^7.5.6"
|
|
65
68
|
},
|
|
66
69
|
"devDependencies": {
|
|
67
|
-
"@resvg/resvg-js": "^2.6.2",
|
|
68
70
|
"husky": "^9.1.7"
|
|
69
71
|
},
|
|
70
72
|
"overrides": {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// scripts/build-mcpb.js
|
|
5
|
+
//
|
|
6
|
+
// Packages the runtime files + .mcpb/manifest.json into
|
|
7
|
+
// dist/feishu-user-plugin-<version>.mcpb (a ZIP with manifest.json at the root).
|
|
8
|
+
//
|
|
9
|
+
// The .mcpb format is a plain ZIP archive consumed by Claude Desktop / Anthropic
|
|
10
|
+
// Connectors Directory. Required layout:
|
|
11
|
+
// manifest.json (at root, copied from .mcpb/manifest.json)
|
|
12
|
+
// src/... (server runtime)
|
|
13
|
+
// proto/... (protobuf descriptors)
|
|
14
|
+
// skills/... (MCP prompts source)
|
|
15
|
+
// .claude-plugin/... (plugin metadata)
|
|
16
|
+
// package.json (so `node src/index.js` resolves deps after `npm ci`)
|
|
17
|
+
// package-lock.json
|
|
18
|
+
// PRIVACY.md, README.md, LICENSE
|
|
19
|
+
//
|
|
20
|
+
// node_modules/ is NOT bundled; the connector host runs `npm ci --omit=dev`
|
|
21
|
+
// against the bundled package.json once installed (Anthropic convention).
|
|
22
|
+
//
|
|
23
|
+
// Re-runnable: overwrites dist/feishu-user-plugin-<version>.mcpb on each run.
|
|
24
|
+
//
|
|
25
|
+
// Usage:
|
|
26
|
+
// node scripts/build-mcpb.js
|
|
27
|
+
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
const { execFileSync } = require('child_process');
|
|
31
|
+
|
|
32
|
+
const ROOT = path.join(__dirname, '..');
|
|
33
|
+
const DIST = path.join(ROOT, 'dist');
|
|
34
|
+
const MANIFEST_SRC = path.join(ROOT, '.mcpb', 'manifest.json');
|
|
35
|
+
|
|
36
|
+
function fail(msg) {
|
|
37
|
+
console.error(`build-mcpb: ${msg}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!fs.existsSync(MANIFEST_SRC)) fail('.mcpb/manifest.json not found');
|
|
42
|
+
|
|
43
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
|
44
|
+
const manifest = JSON.parse(fs.readFileSync(MANIFEST_SRC, 'utf8'));
|
|
45
|
+
|
|
46
|
+
if (!manifest.version) fail('.mcpb/manifest.json missing `version`');
|
|
47
|
+
if (manifest.version !== pkg.version) {
|
|
48
|
+
fail(
|
|
49
|
+
`version mismatch — package.json=${pkg.version} but .mcpb/manifest.json=${manifest.version}. ` +
|
|
50
|
+
`Run: node scripts/check-mcpb-version.js`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const VERSION = pkg.version;
|
|
55
|
+
const OUT = path.join(DIST, `feishu-user-plugin-${VERSION}.mcpb`);
|
|
56
|
+
|
|
57
|
+
if (!fs.existsSync(DIST)) fs.mkdirSync(DIST, { recursive: true });
|
|
58
|
+
if (fs.existsSync(OUT)) fs.unlinkSync(OUT);
|
|
59
|
+
|
|
60
|
+
// Files & dirs to bundle. Order matters only for predictable zip output.
|
|
61
|
+
// Mirrors package.json::files plus PRIVACY.md and the bundled manifest.json.
|
|
62
|
+
const ENTRIES = [
|
|
63
|
+
'manifest.json', // synthesized at staging root from .mcpb/manifest.json
|
|
64
|
+
'src',
|
|
65
|
+
'proto',
|
|
66
|
+
'scripts',
|
|
67
|
+
'.claude-plugin',
|
|
68
|
+
'skills',
|
|
69
|
+
'package.json',
|
|
70
|
+
'package-lock.json',
|
|
71
|
+
'PRIVACY.md',
|
|
72
|
+
'README.md',
|
|
73
|
+
'LICENSE',
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// Stage in a tmp dir so the zip has manifest.json at the archive root rather
|
|
77
|
+
// than .mcpb/manifest.json. Using a tmp dir keeps the source tree clean.
|
|
78
|
+
const STAGE = fs.mkdtempSync(path.join(require('os').tmpdir(), 'mcpb-build-'));
|
|
79
|
+
try {
|
|
80
|
+
// Copy manifest.json to staging root
|
|
81
|
+
fs.copyFileSync(MANIFEST_SRC, path.join(STAGE, 'manifest.json'));
|
|
82
|
+
|
|
83
|
+
// Copy each remaining entry from repo root → staging root
|
|
84
|
+
for (const entry of ENTRIES.slice(1)) {
|
|
85
|
+
const src = path.join(ROOT, entry);
|
|
86
|
+
if (!fs.existsSync(src)) {
|
|
87
|
+
console.warn(`build-mcpb: skipping missing entry: ${entry}`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const dest = path.join(STAGE, entry);
|
|
91
|
+
copyRecursive(src, dest);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Create the ZIP via system `zip` (present on macOS + ubuntu-latest CI).
|
|
95
|
+
// -r recursive, -X strip extra OS attrs for reproducibility, -q quiet.
|
|
96
|
+
// We pass `.` so paths inside the zip are relative to the staging root.
|
|
97
|
+
execFileSync('zip', ['-rqX', OUT, '.'], { cwd: STAGE, stdio: 'inherit' });
|
|
98
|
+
} finally {
|
|
99
|
+
fs.rmSync(STAGE, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const stats = fs.statSync(OUT);
|
|
103
|
+
console.log(`OK: built ${path.relative(ROOT, OUT)} (${(stats.size / 1024).toFixed(1)} KB)`);
|
|
104
|
+
|
|
105
|
+
// --- helpers ------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
function copyRecursive(src, dest) {
|
|
108
|
+
const stat = fs.statSync(src);
|
|
109
|
+
if (stat.isDirectory()) {
|
|
110
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
111
|
+
for (const child of fs.readdirSync(src)) {
|
|
112
|
+
// Skip OS noise + already-built artifacts inside copied dirs
|
|
113
|
+
if (child === '.DS_Store' || child === 'node_modules' || child === 'dist') continue;
|
|
114
|
+
copyRecursive(path.join(src, child), path.join(dest, child));
|
|
115
|
+
}
|
|
116
|
+
} else if (stat.isFile()) {
|
|
117
|
+
fs.copyFileSync(src, dest);
|
|
118
|
+
}
|
|
119
|
+
}
|