feishu-user-plugin 1.2.1 → 1.3.1

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.
@@ -3,17 +3,17 @@
3
3
  ## What This Is
4
4
  All-in-one Feishu plugin for Claude Code with three auth layers:
5
5
  - **User Identity** (cookie auth): Send messages (text, image, file, post, sticker, audio) as yourself
6
- - **Official API** (app credentials): Read group messages, docs, tables, wiki, drive, contacts
6
+ - **Official API** (app credentials): Read group messages, docs, tables, wiki, drive, contacts, upload files
7
7
  - **User OAuth UAT** (user_access_token): Read P2P chat history, list all user's chats
8
8
 
9
- ## Tool Categories
9
+ ## Tool Categories (66 tools)
10
10
 
11
11
  ### User Identity — Messaging (reverse-engineered, cookie-based)
12
- - `send_to_user` — Search user + send text (one step, most common)
13
- - `send_to_group` — Search group + send text (one step)
12
+ - `send_to_user` — Search user + send text (one step, most common). Returns candidates if multiple matches.
13
+ - `send_to_group` — Search group + send text (one step). Returns candidates if multiple matches.
14
14
  - `send_as_user` — Send text to any chat by ID, supports reply threading (root_id/parent_id)
15
- - `send_image_as_user` — Send image (requires image_key from upload)
16
- - `send_file_as_user` — Send file (requires file_key from upload)
15
+ - `send_image_as_user` — Send image (requires image_key from `upload_image`)
16
+ - `send_file_as_user` — Send file (requires file_key from `upload_file`)
17
17
  - `send_post_as_user` — Send rich text with title + formatted paragraphs
18
18
  - `send_sticker_as_user` — Send sticker/emoji
19
19
  - `send_audio_as_user` — Send audio message
@@ -21,64 +21,168 @@ All-in-one Feishu plugin for Claude Code with three auth layers:
21
21
  ### User Identity — Contacts & Info
22
22
  - `search_contacts` — Search users/groups by name
23
23
  - `create_p2p_chat` — Create/get P2P chat
24
- - `get_chat_info` — Group details (name, members, owner)
25
- - `get_user_info` — User display name lookup (from search cache)
24
+ - `get_chat_info` — Group details (name, members, owner). Supports both oc_xxx and numeric chat_id (Official API + protobuf fallback)
25
+ - `get_user_info` — User display name lookup (official API first, cookie cache fallback)
26
26
  - `get_login_status` — Check cookie, app, and UAT status
27
27
 
28
- ### User OAuth UAT Tools (P2P chat reading)
29
- - `read_p2p_messages` — Read P2P (direct message) chat history. chat_id accepts both numeric IDs (from create_p2p_chat) and oc_xxx format.
28
+ ### User OAuth UAT Tools (P2P chat reading + user-identity creation)
29
+ - `read_p2p_messages` — Read P2P (direct message) chat history. chat_id accepts both numeric IDs (from create_p2p_chat) and oc_xxx format. Returns newest messages first by default.
30
30
  - `list_user_chats` — List group chats the user is in. Note: API only returns groups, not P2P. For P2P, use: `search_contacts` → `create_p2p_chat` → `read_p2p_messages`.
31
+ - `create_doc` / `create_bitable` / `create_folder` — **UAT-first**: creates resources as the user (not the app) when UAT with write scopes is available. Falls back to app token.
31
32
 
32
33
  ### Official API Tools (app credentials)
33
- - `list_chats` / `read_messages` — Chat history (accepts chat name, oc_ ID, or numeric ID; auto-falls back to UAT for external groups)
34
- - `reply_message` / `forward_message` Message operations (as bot). reply_message only works for text messages.
35
- - `search_docs` / `read_doc` / `create_doc` Document operations
36
- - `list_bitable_tables` / `list_bitable_fields` / `search_bitable_records` Table queries
37
- - `create_bitable_record` / `update_bitable_record` — Table writes
34
+ - `list_chats` / `read_messages` — Chat history (read_messages accepts chat name, oc_ ID, or numeric ID; auto-resolves via bot's group list → im.chat.search → search_contacts). **Auto-falls back to UAT for external groups the bot cannot access.** Returns newest messages first by default. Messages include sender names.
35
+ - `send_message_as_bot` — Bot sends message to any chat (text, post, interactive, etc.)
36
+ - `reply_message` / `forward_message` Message operations (as bot)
37
+ - `delete_message` / `update_message` Recall or edit bot's own messages
38
+ - `add_reaction` / `delete_reaction` — Emoji reactions on messages
39
+ - `pin_message` — Pin or unpin a message (pinned=true/false)
40
+ - `create_group` / `update_group` — Create and manage group chats
41
+ - `list_members` / `manage_members` — Group membership (manage_members: action=add/remove)
42
+ - `search_docs` / `read_doc` / `get_doc_blocks` / `create_doc` — Document operations
43
+ - `create_doc_block` / `update_doc_block` / `delete_doc_blocks` — Document content editing (insert/update/delete blocks)
44
+ - `create_bitable` / `get_bitable_meta` / `copy_bitable` — Bitable app management (create, get info, copy)
45
+ - `list_bitable_tables` / `create_bitable_table` / `update_bitable_table` / `delete_bitable_table` — Table management (CRUD + rename)
46
+ - `list_bitable_fields` / `create_bitable_field` / `update_bitable_field` / `delete_bitable_field` — Field (column) management
47
+ - `list_bitable_views` / `create_bitable_view` / `delete_bitable_view` — View management (grid, kanban, gallery, form, gantt, calendar)
48
+ - `search_bitable_records` / `get_bitable_record` — Query records
49
+ - `batch_create_bitable_records` / `batch_update_bitable_records` / `batch_delete_bitable_records` — Record CRUD (single or batch, max 500/call)
38
50
  - `list_wiki_spaces` / `search_wiki` / `list_wiki_nodes` — Wiki
39
51
  - `list_files` / `create_folder` — Drive
52
+ - `copy_file` / `move_file` / `delete_file` — Drive file operations (copy, move, delete)
53
+ - `upload_image` / `upload_file` — Upload image/file, returns key for send_image/send_file
40
54
  - `find_user` — Contact lookup by email/mobile
41
55
 
42
56
  ## Usage Patterns
57
+
58
+ ### Messaging
43
59
  - Send text as yourself → `send_to_user` or `send_to_group`
44
- - Send rich content → `send_post_as_user` (formatted text), `send_image_as_user` (images)
60
+ - Send image → `upload_image` `send_image_as_user`
61
+ - Send file → `upload_file` → `send_file_as_user`
62
+ - Send rich content → `send_post_as_user` (formatted text with links, @mentions)
63
+ - Reply as user in thread → `send_as_user` with root_id
64
+ - Reply as bot → `reply_message` (official API)
65
+
66
+ ### Reading
45
67
  - Read any group chat history → `read_messages` with chat name or ID (auto-handles external groups via UAT fallback)
46
68
  - Read P2P chat history → `search_contacts` → `create_p2p_chat` → `read_p2p_messages`
47
- - Reply as user in thread → `send_as_user` with root_id
48
- - Reply as bot → `reply_message` (official API, text messages only)
69
+ - Get chat details → `get_chat_info` (supports both oc_xxx and numeric ID)
70
+
71
+ ### Bitable (Multi-dimensional Tables)
72
+ - Create a bitable from scratch → `create_bitable` → `create_bitable_table` → `create_bitable_field`
73
+ - Get bitable info → `get_bitable_meta`
74
+ - Copy a bitable → `copy_bitable` with name and optional folder
75
+ - Query data → `list_bitable_tables` → `list_bitable_fields` → `search_bitable_records`
76
+ - Rename table → `update_bitable_table` with new name
77
+ - Read single record → `get_bitable_record`
78
+ - Create/update/delete records → `batch_create_bitable_records` / `batch_update_bitable_records` / `batch_delete_bitable_records` (works for single or up to 500)
79
+ - Manage fields → `create_bitable_field` / `update_bitable_field` (requires type param) / `delete_bitable_field`
80
+ - Manage views → `create_bitable_view` (type: grid/kanban/gallery/form/gantt/calendar) / `delete_bitable_view`
81
+
82
+ ### Group Management
83
+ - Create a group → `create_group` with name and optional member open_ids
84
+ - Add/remove members → `manage_members` with chat_id + member_ids + action (add/remove)
85
+ - List members → `list_members`
86
+
87
+ ### Document Editing
88
+ - Create doc with content → `create_doc` → `create_doc_block` (use document_id as parent_block_id for root)
89
+ - Edit existing block → `get_doc_blocks` to find block_id → `update_doc_block`
90
+ - Delete blocks → `delete_doc_blocks` with start/end index range
91
+
92
+ ### Diagnostics
49
93
  - Diagnose issues → `get_login_status` first
50
94
 
51
95
  ## Auth & Session
52
- - **LARK_COOKIE**: Required for user identity tools. Session auto-refreshed every 4h via heartbeat.
96
+ - **LARK_COOKIE**: Required for user identity tools. Session auto-refreshed every 4h via heartbeat and persisted to config.
53
97
  - **LARK_APP_ID + LARK_APP_SECRET**: Required for official API tools.
54
- - **LARK_USER_ACCESS_TOKEN + LARK_USER_REFRESH_TOKEN**: Required for P2P reading. Auto-refreshed via refresh_token.
98
+ - **LARK_USER_ACCESS_TOKEN + LARK_USER_REFRESH_TOKEN**: Required for P2P reading. Auto-refreshed on expiry (error codes 99991668/99991663/99991677). Token auto-persisted to MCP config on refresh.
99
+ - Cookie expiry: sl_session has 12h max-age, auto-refreshed by heartbeat every 4h.
100
+ - UAT expiry: 2h, auto-refreshed via refresh_token.
101
+ - Refresh token expiry: 7 days. Use `keepalive` cron to prevent expiration.
55
102
 
56
- ## Automated Cookie Setup via Playwright
103
+ ## Required Environment Variables (ALL are required for full functionality)
104
+
105
+ | Variable | Purpose |
106
+ |----------|---------|
107
+ | LARK_COOKIE | User identity messaging |
108
+ | LARK_APP_ID | Official API access |
109
+ | LARK_APP_SECRET | Official API access |
110
+ | LARK_USER_ACCESS_TOKEN | P2P chat reading |
111
+ | LARK_USER_REFRESH_TOKEN | UAT auto-refresh |
112
+
113
+ All 5 must be configured. Without UAT, `read_p2p_messages` and `list_user_chats` will not work.
114
+
115
+ ## Installation
116
+
117
+ ### Config location
118
+
119
+ Credentials are stored in `~/.claude.json` top-level `mcpServers` (global — works in all directories).
120
+ **Do NOT put credentials in project-level config** (`projects[*].mcpServers` or `.mcp.json`) — this causes scope issues.
121
+
122
+ ### Non-interactive setup (for Claude Code agents)
123
+
124
+ ```bash
125
+ npx feishu-user-plugin setup --app-id <APP_ID> --app-secret <APP_SECRET>
126
+ ```
127
+
128
+ Writes config to `~/.claude.json` top-level `mcpServers` without any interactive prompts. Supports `--cookie` flag too.
129
+
130
+ ### Interactive setup
131
+
132
+ ```bash
133
+ npx feishu-user-plugin setup # Interactive setup wizard
134
+ npx feishu-user-plugin oauth # Get OAuth UAT tokens
135
+ npx feishu-user-plugin status # Check auth status
136
+ npx feishu-user-plugin keepalive # Refresh cookie + UAT (for cron jobs)
137
+ ```
138
+
139
+ ### Token auto-renewal via cron (optional)
140
+
141
+ To keep tokens alive even when Claude Code is closed:
142
+
143
+ ```bash
144
+ crontab -e
145
+ # Add: 0 */4 * * * npx feishu-user-plugin keepalive >> /tmp/feishu-keepalive.log 2>&1
146
+ ```
57
147
 
58
- When LARK_COOKIE is missing or expired, obtain it using Playwright MCP.
148
+ ## Automated Cookie Setup via Playwright
59
149
 
60
150
  ### Prerequisites
61
- Playwright MCP must be available. If not:
62
- > Run: `npx @anthropic-ai/claude-code mcp add playwright -- npx @anthropic-ai/mcp-server-playwright`
63
- > Then restart Claude Code.
151
+ Playwright MCP must be available. If not installed:
152
+ > Run: `npx @anthropic-ai/claude-code mcp add playwright -- npx @anthropic-ai/mcp-server-playwright` then restart Claude Code.
153
+
154
+ ### Automated Flow — FOLLOW EXACTLY, DO NOT IMPROVISE
155
+
156
+ **Step 1: Clear existing browser session (MANDATORY)**
64
157
 
65
- ### Automated Flow FOLLOW EXACTLY
158
+ Playwright MCP uses Edge's persistent profile. It may have a cached login from a DIFFERENT Feishu account. You MUST clear cookies first:
159
+
160
+ ```
161
+ browser_run_code:
162
+ await context.clearCookies();
163
+ ```
66
164
 
67
- **Step 1: Clear cookies first** (Playwright uses persistent browser profile that may have a DIFFERENT account cached):
165
+ Then navigate:
68
166
  ```
69
- browser_run_code: await context.clearCookies();
70
167
  browser_navigate: https://www.feishu.cn/messenger/
71
168
  ```
72
169
 
73
- **Step 2: Show QR code and wait for login**:
170
+ **Step 2: Wait for user to scan QR code**
171
+
172
+ Take a screenshot to show the QR code:
74
173
  ```
75
174
  browser_take_screenshot
76
175
  ```
77
- Poll `browser_snapshot` every 5s until URL changes from `/accounts/`.
78
176
 
79
- **Step 3: Extract cookie TWO-STEP approach** (NEVER use browser_run_code output directly as cookie):
177
+ Tell the user: "Please scan the QR code with Feishu mobile app to log in. Make sure you use the correct account."
178
+
179
+ Poll with `browser_snapshot` every 5 seconds until the URL changes away from `/accounts/` (indicating login complete).
180
+
181
+ **Step 3: Extract cookie — TWO-STEP approach (MANDATORY)**
182
+
183
+ NEVER use `browser_run_code` output directly as the cookie string. Its output includes `### Result\n` markdown prefix, page snapshots, and console logs that contaminate the cookie.
80
184
 
81
- Step 3a via `browser_run_code`:
185
+ Step 3a — Store cookie in page context via `browser_run_code`:
82
186
  ```js
83
187
  const cookies = await page.context().cookies('https://www.feishu.cn');
84
188
  const str = cookies.map(c => c.name + '=' + c.value).join('; ');
@@ -86,47 +190,220 @@ await page.evaluate(s => { window.__COOKIE__ = s; }, str);
86
190
  return 'Stored ' + cookies.length + ' cookies, length=' + str.length;
87
191
  ```
88
192
 
89
- Step 3b via `browser_evaluate`:
193
+ Step 3b — Read the clean cookie string via `browser_evaluate`:
90
194
  ```js
91
195
  window.__COOKIE__
92
196
  ```
93
197
 
94
- **Step 4: Validate** Must be pure ASCII, contain `session=` and `sl_session=`, length 500-5000. If >10000, it's contaminated.
198
+ This two-step approach ensures the cookie string is clean, with no markdown prefix or page content mixed in.
199
+
200
+ **Step 4: Validate BEFORE writing (MANDATORY)**
201
+
202
+ Check the cookie string:
203
+ 1. Must be pure ASCII — no Chinese characters, no markdown (`###`), no HTML
204
+ 2. Must contain `session=` and `sl_session=`
205
+ 3. Length should be 500-5000 characters. If >10000, it is contaminated — DO NOT write it.
206
+ 4. Must NOT start with `###` or contain `\n` followed by non-cookie content
207
+
208
+ If validation fails: STOP. Debug the extraction. Do NOT write a bad cookie to config.
209
+
210
+ **Step 5: Write cookie to config**
211
+
212
+ Use `persistToConfig` or directly update the `LARK_COOKIE` field in `~/.claude.json` → `mcpServers` → `feishu-user-plugin` → `env`.
213
+
214
+ **Step 6: Run OAuth for UAT (if not already configured)**
215
+
216
+ ```bash
217
+ npx feishu-user-plugin oauth
218
+ ```
219
+
220
+ This opens a browser for OAuth consent. After completion, tokens are auto-saved to `~/.claude.json`.
221
+
222
+ **Step 7: Close browser and prompt restart**
95
223
 
96
- **Step 5: Write config** using exact format:
97
- ```json
98
- {
99
- "feishu-user-plugin": {
100
- "command": "npx",
101
- "args": ["-y", "feishu-user-plugin"],
102
- "env": {
103
- "LARK_COOKIE": "<cookie>",
104
- "LARK_APP_ID": "<id>",
105
- "LARK_APP_SECRET": "<secret>",
106
- "LARK_USER_ACCESS_TOKEN": "<uat>",
107
- "LARK_USER_REFRESH_TOKEN": "<refresh>"
108
- }
109
- }
110
- }
111
224
  ```
225
+ browser_close
226
+ ```
227
+
228
+ Tell user to restart Claude Code. Only ONE restart should be needed.
229
+
230
+ ## Troubleshooting Guide
112
231
 
113
- ## Troubleshooting
232
+ ### If MCP disconnects mid-session
233
+ - **Root cause** (fixed in v1.3.1): `@larksuiteoapi/node-sdk`'s `defaultLogger.error` uses `console.log` (stdout). MCP protocol uses stdout for JSON-RPC, so SDK error logs corrupt the transport and cause immediate disconnect.
234
+ - **Fix**: Custom logger redirects all SDK output to stderr. Already applied in `src/official.js`.
235
+ - If still happening: check for any `console.log` calls in server code (only `console.error` is safe in MCP servers).
236
+
237
+ ### If MCP tools are not available
238
+ 1. Check `~/.claude.json` — config must be in **top-level** `mcpServers`, not inside `projects[*]`
239
+ 2. For Codex: check `~/.codex/config.toml` has `[mcp_servers.feishu-user-plugin]` section
240
+ 3. Restart Claude Code / Codex after config changes
241
+ 4. After restart, tools may take a few seconds to register — if first call fails with "No such tool", wait and retry once
114
242
 
115
243
  ### If cookie authentication fails
244
+ - `document.cookie` in browser console CANNOT access HttpOnly cookies (`session`, `sl_session`)
245
+ - **Correct method**: Network tab → first request → Request Headers → Cookie → Copy value
116
246
  - **Best method**: Playwright two-step extraction (see above)
117
- - **Manual fallback**: Network tab → first request → Request Headers → Cookie → Copy value
118
- - Do NOT use `document.cookie` or Application → Cookies (misses HttpOnly cookies)
119
247
 
120
- ### If Playwright logs into the wrong account
121
- - Always `context.clearCookies()` BEFORE navigating to feishu.cn
122
- - Verify account by checking URL domain after login
248
+ ### If Playwright logs into the wrong Feishu account
249
+ - Playwright uses Edge's persistent profile with cached sessions
250
+ - **ALWAYS clear cookies first** with `context.clearCookies()` before navigating to feishu.cn
251
+
252
+ ### If read_messages returns an error
253
+ - Error messages include the actual Feishu error code and description
254
+ - `read_messages` auto-falls back to UAT when bot API fails (e.g. external groups)
255
+ - Chat name resolution: bot's group list → `im.chat.search` → `search_contacts` (cookie)
256
+ - If all three strategies fail, provide the oc_xxx or numeric chat ID directly
123
257
 
124
- ### If UAT refresh fails with "invalid_grant" (error 28003/20003/20005)
125
- - Refresh token expired re-run `node src/oauth.js` (needs LARK_APP_ID + LARK_APP_SECRET in `.env`)
126
- - Copy new UAT + refresh token from `.env` to MCP config, then restart Claude Code
258
+ ### If UAT refresh fails with "invalid_grant"
259
+ - The refresh token has expired or been revoked auto-refresh cannot recover this
260
+ - **Fix**: Re-run OAuth: `npx feishu-user-plugin oauth`
261
+ - Then restart Claude Code
262
+
263
+ ### If OAuth fails with "Missing LARK_APP_ID"
264
+ - `oauth.js` reads credentials from `~/.claude.json` MCP config (not .env)
265
+ - Run `npx feishu-user-plugin setup` first, then re-run OAuth
266
+
267
+ ### If two MCP servers are running (duplicate tools)
268
+ - This happens when both `~/.claude.json` mcpServers AND a team-skills plugin have feishu-user-plugin
269
+ - team-skills plugin should NOT have `.mcp.json` — it only provides skills and CLAUDE.md
270
+ - Delete `.mcp.json` from the team-skills plugin directory if it exists
127
271
 
128
272
  ### If list_user_chats doesn't return P2P chats
129
- - Expected — API only returns groups. Use: `search_contacts` → `create_p2p_chat` → `read_p2p_messages`.
273
+ - This is expected the API only returns group chats
274
+ - **Correct P2P flow**: `search_contacts` → `create_p2p_chat` → `read_p2p_messages`
275
+
276
+ ## Architecture
277
+
278
+ ### Two distribution channels
279
+ - **npm package** (`npx feishu-user-plugin`): MCP server code + skills + CLAUDE.md. For external users.
280
+ - **team-skills plugin**: Skills + CLAUDE.md only (no .mcp.json). For internal team members.
281
+
282
+ ### Config management
283
+ - `src/config.js`: Unified config module. Discovers config in `~/.claude.json` (top-level + project-level), `.mcp.json`, and `~/.codex/config.toml`.
284
+ - `setup` writes to `~/.claude.json` (default) or `~/.codex/config.toml` (with `--client codex`), or both (`--client both`).
285
+ - `persistToConfig()` finds the correct config entry and writes back atomically (used by heartbeat + UAT refresh).
286
+ - All config writes use atomic write (tmp file + rename) to prevent race conditions with Claude Code.
287
+
288
+ ### Multi-client support
289
+ - **Claude Code**: JSON config in `~/.claude.json` mcpServers
290
+ - **Codex**: TOML config in `~/.codex/config.toml` mcp_servers
291
+ - Setup: `npx feishu-user-plugin setup --client codex` or `--client both`
292
+ - MCP server code is identical for both clients — only config format differs
293
+ - Codex does not support Claude Code slash commands (skills) — only MCP tools are available
294
+
295
+ ## Development & Publishing
296
+
297
+ ### Publishing to npm
298
+
299
+ ```bash
300
+ # 1. Update version in package.json
301
+ # 2. Commit and tag
302
+ git add -A && git commit -m "v1.2.1: description"
303
+ git tag v1.2.1
304
+ git push && git push --tags
305
+ # 3. GitHub Actions auto-publishes to npm on tag push
306
+ ```
307
+
308
+ GitHub Actions workflow (`.github/workflows/publish.yml`) auto-publishes on `v*` tags.
309
+ NPM_TOKEN is stored as a GitHub repo secret.
310
+
311
+ ### Syncing to team-skills
312
+
313
+ After publishing, sync plugin assets to team-skills:
314
+
315
+ ```bash
316
+ # From the feishu-user-plugin repo:
317
+ cp -r skills/ /path/to/team-skills/plugins/feishu-user-plugin/skills/
318
+ cp .claude-plugin/plugin.json /path/to/team-skills/plugins/feishu-user-plugin/.claude-plugin/
319
+ # Do NOT copy .mcp.json — team-skills plugin should not have one
320
+ ```
321
+
322
+ ## Development Workflow
323
+
324
+ ### Keeping all docs in sync
325
+ When making ANY code change (new tools, bug fixes, features), update ALL of these:
326
+
327
+ **本仓库内:**
328
+ - `CLAUDE.md` — tool count, tool list, usage patterns, known limitations
329
+ - `README.md` — tool count (badge + heading + tool table), feature highlights, OpenClaw/Claude Code config examples
330
+ - `ROADMAP.md` — check off completed items, add new findings
331
+ - `package.json` — version, description (tool count)
332
+ - `skills/feishu-user-plugin/references/CLAUDE.md` — always copy from root: `cp CLAUDE.md skills/feishu-user-plugin/references/CLAUDE.md`
333
+ - `prompts/openclaw-setup.md` — if OpenClaw 相关配置变了要更新
334
+
335
+ **team-skills 仓库 (`/Users/abble/team-skills/plugins/feishu-user-plugin/`):**
336
+ - `skills/` — 同步技能文件: `cp -r skills/ /Users/abble/team-skills/plugins/feishu-user-plugin/skills/`
337
+ - `README.md` — team-skills 有自己的 README(含团队 APP_ID/SECRET),需要同步更新:工具数量、功能列表、更新日志、安装 prompt
338
+ - 两个 README 都必须包含 Claude Code 安装 prompt 和 OpenClaw 安装 prompt
339
+ - team-skills README 的安装 prompt 包含团队共享的 APP_ID/SECRET(hardcoded),本仓库 README 用占位符
340
+
341
+ **同步命令(每次发版后执行):**
342
+ ```bash
343
+ # 1. 同步 skills
344
+ cp CLAUDE.md skills/feishu-user-plugin/references/CLAUDE.md
345
+ cp -r skills/ /Users/abble/team-skills/plugins/feishu-user-plugin/skills/
346
+ # 2. 手动更新 team-skills README(工具数、功能列表、更新日志)
347
+ # 3. 提交并推送两个仓库
348
+ ```
349
+
350
+ ### Keeping ROADMAP.md up to date
351
+ - When completing a feature or fixing a bug, check the corresponding item in ROADMAP.md as `[x]` done
352
+ - When discovering new bugs, limitations, or feature ideas during development, add them to the appropriate section in ROADMAP.md
353
+ - When a version is released (tag pushed), move completed items under the "已完成" section with the version number
354
+ - When researching a direction and deciding not to implement, add it to "已调研但暂不实施" with the reasoning
355
+
356
+ ### When adding new tools
357
+ 1. Add method to `src/official.js`(Official API)or `src/client.js`(Cookie 身份)
358
+ 2. Add tool definition to `TOOLS` array in `src/index.js`
359
+ 3. Add handler case in `handleTool()` switch in `src/index.js`
360
+ 4. Run `node -c src/official.js && node -c src/index.js` to verify syntax
361
+ 5. Update this file (CLAUDE.md) — tool count, tool list, usage patterns
362
+ 6. Update ROADMAP.md if relevant
363
+
364
+ ### When fixing bugs
365
+ 1. Write a standalone test script (`node -e "..."`) to reproduce the bug before fixing
366
+ 2. After fixing, verify with the same script
367
+ 3. If the bug affects MCP tool behavior, test via MCP tool call after server restart
368
+
369
+ ### Commit conventions
370
+ - `feat:` new tools or capabilities
371
+ - `fix:` bug fixes
372
+ - `docs:` CLAUDE.md, ROADMAP.md, README updates
373
+ - `chore:` dependencies, CI, config changes
374
+
375
+ ### Publishing
376
+ **IMPORTANT: Version number must ALWAYS be confirmed with the user before publishing.**
377
+ Any operation involving `npm version`, modifying `package.json` version, `git tag v*`, or `git push --tags` requires explicit user confirmation of the target version number. Do not auto-decide version numbers.
378
+
379
+ Three-layer version safety:
380
+ 1. **Claude rule** (this section): Ask user to confirm version before any publish-related operation
381
+ 2. **Local gate** (`prepublishOnly`): Interactive confirmation when running `npm publish` locally (skipped in CI)
382
+ 3. **CI gate** (`.github/workflows/publish.yml`): Tag must match `package.json` version or publish fails
383
+
384
+ Steps:
385
+ 1. Confirm target version with user
386
+ 2. Update `version` in `package.json`
387
+ 3. `git add <files> && git commit -m "v1.x.x: description"`
388
+ 4. `git tag v1.x.x && git push && git push --tags`
389
+ 5. GitHub Actions verifies tag matches package.json, then auto-publishes to npm
390
+
391
+ ### Syncing to team-skills (after any CLAUDE.md or skills change)
392
+ 1. Copy CLAUDE.md to skill reference: `cp CLAUDE.md skills/feishu-user-plugin/references/CLAUDE.md`
393
+ 2. Sync to team-skills repo: `cp -r skills/ /Users/abble/team-skills/plugins/feishu-user-plugin/skills/`
394
+ 3. Also sync plugin.json: `cp .claude-plugin/plugin.json /Users/abble/team-skills/plugins/feishu-user-plugin/.claude-plugin/`
395
+ 4. Commit and push both repos
396
+
397
+ ### Testing a tool
398
+ - For Official API tools: can test directly via MCP tool call or standalone script using `readCredentials()` from `src/config.js`
399
+ - For Cookie tools: need active session, test via MCP tool call
400
+ - Always verify `_safeSDKCall` handles the response format (multipart uploads return data at top level, not nested under `.data`)
130
401
 
131
- ### If reply_message fails with error 230054
132
- - Only text messages can be replied to via this API.
402
+ ## Known Limitations
403
+ - CARD message type (type=14) not yet implemented complex JSON schema
404
+ - External tenant users may not be resolvable via `get_user_info` (contact API scope limitation)
405
+ - Cookie auth requires human interaction (QR scan) — cannot be fully automated
406
+ - Refresh token expires after 7 days without use — set up `keepalive` cron to prevent this
407
+ - `update_bitable_field` requires `type` parameter even when only changing field name (Feishu API requirement)
408
+ - `list_wiki_spaces` may return empty if bot lacks `wiki:wiki:readonly` permission
409
+ - `search_wiki` uses same API as `search_docs` — `docs_types` filter may not work as expected
package/src/cli.js CHANGED
@@ -41,23 +41,28 @@ function printHelp() {
41
41
  feishu-user-plugin — All-in-one Feishu MCP Server
42
42
 
43
43
  Commands:
44
- (default) Start MCP server (used by Claude Code)
44
+ (default) Start MCP server (used by Claude Code / Codex)
45
45
  setup Interactive setup wizard — writes MCP config
46
46
  oauth Run OAuth flow to obtain user_access_token
47
47
  status Check authentication status
48
48
  keepalive Refresh cookie + UAT to prevent expiration (for cron jobs)
49
49
  help Show this help
50
50
 
51
- Quick Start (team members):
51
+ Setup options:
52
+ --app-id <id> App ID (non-interactive mode)
53
+ --app-secret <s> App Secret (non-interactive mode)
54
+ --cookie <c> Cookie string (optional)
55
+ --client <target> Config target: claude (default), codex, or both
56
+
57
+ Quick Start (Claude Code):
52
58
  1. npx feishu-user-plugin setup
53
59
  2. Follow the prompts to configure credentials
54
60
  3. Restart Claude Code
55
61
 
56
- Quick Start (external users):
57
- 1. Create a Feishu app at https://open.feishu.cn/app
58
- 2. npx feishu-user-plugin setup
59
- 3. npx feishu-user-plugin oauth
60
- 4. Restart Claude Code
62
+ Quick Start (Codex):
63
+ 1. npx feishu-user-plugin setup --client codex
64
+ 2. Follow the prompts to configure credentials
65
+ 3. Restart Codex
61
66
 
62
67
  Auto-renewal (optional):
63
68
  Add to crontab to keep tokens alive even when Claude Code is closed: