feishu-user-plugin 1.3.0 → 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.
package/README.md CHANGED
@@ -3,22 +3,25 @@
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-33-orange.svg)](#tools-33-total)
6
+ [![Tools](https://img.shields.io/badge/Tools-76-orange.svg)](#tools)
7
7
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
8
8
 
9
- **All-in-one Feishu/Lark MCP Server -- 33 tools, 9 skills, 3 auth layers for messaging, docs, tables, wiki, and drive.**
9
+ **All-in-one Feishu/Lark MCP Server -- 76 tools, 9 skills, 3 auth layers for messaging, docs, bitable, calendar, tasks, drive, and more.**
10
10
 
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 for documents, spreadsheets, wikis, and more.
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
12
 
13
13
  ## Highlights
14
14
 
15
15
  - **Send as yourself** -- Messages show your real name, not a bot. Supports text, rich text, images, files, stickers, and audio.
16
16
  - **Read everything** -- Group chats via bot API, P2P (direct messages) via OAuth UAT.
17
- - **Full Feishu suite** -- Docs, Bitable (spreadsheets), Wiki, Drive, Contacts -- all in one plugin.
18
- - **3 auth layers** -- Cookie-based user identity, app credentials (Official API), and OAuth UAT (P2P reading). All three are needed for full functionality.
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.
19
22
  - **9 slash commands** for Claude Code -- `/send`, `/reply`, `/search`, `/digest`, `/doc`, `/table`, `/wiki`, `/drive`, `/status`
20
23
  - **Auto session management** -- Cookie heartbeat every 4h, UAT auto-refresh with token rotation.
21
- - **Chat name resolution** -- Pass a group name instead of `oc_xxx` ID; it resolves automatically.
24
+ - **Multi-platform** -- Claude Code, Cursor, Windsurf, VS Code, OpenClaw.
22
25
 
23
26
  ## Why This Exists
24
27
 
@@ -284,6 +287,34 @@ Add to `.vscode/mcp.json` in your project:
284
287
  }
285
288
  ```
286
289
 
290
+ ### OpenClaw
291
+
292
+ Add to `~/.openclaw/openclaw.json` (note: key path is `mcp.servers`, not `mcpServers`):
293
+
294
+ ```json
295
+ {
296
+ "mcp": {
297
+ "servers": {
298
+ "feishu-user-plugin": {
299
+ "command": "npx",
300
+ "args": ["-y", "feishu-user-plugin"],
301
+ "env": {
302
+ "LARK_COOKIE": "your-cookie-string",
303
+ "LARK_APP_ID": "cli_xxxxxxxxxxxx",
304
+ "LARK_APP_SECRET": "your-app-secret",
305
+ "LARK_USER_ACCESS_TOKEN": "your-uat",
306
+ "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
307
+ }
308
+ }
309
+ }
310
+ }
311
+ }
312
+ ```
313
+
314
+ Or via CLI: `openclaw mcp set feishu-user-plugin '{"command":"npx","args":["-y","feishu-user-plugin"],"env":{...}}'`
315
+
316
+ > OpenClaw's built-in Feishu channel handles receiving messages (bot identity). This plugin adds user identity messaging + docs/bitable/calendar/tasks.
317
+
287
318
  ### Windsurf
288
319
 
289
320
  Add to `~/.codeium/windsurf/mcp_config.json`:
@@ -306,86 +337,119 @@ Add to `~/.codeium/windsurf/mcp_config.json`:
306
337
  }
307
338
  ```
308
339
 
309
- ## Tools (33 total)
340
+ ## Tools (76 total)
310
341
 
311
- ### User Identity -- Messaging (cookie auth, Protobuf)
312
-
313
- Send messages as yourself, not as a bot.
342
+ ### User Identity -- Messaging (8 tools, cookie auth)
314
343
 
315
344
  | Tool | Description |
316
345
  |------|-------------|
317
346
  | `send_to_user` | Search user by name + send text -- one step |
318
347
  | `send_to_group` | Search group by name + send text -- one step |
319
- | `send_as_user` | Send text to any chat by ID, supports reply threading (`root_id` / `parent_id`) |
320
- | `send_image_as_user` | Send image (requires `image_key` from upload) |
321
- | `send_file_as_user` | Send file (requires `file_key` from upload) |
322
- | `send_post_as_user` | Send rich text with title + formatted paragraphs (links, @mentions) |
348
+ | `send_as_user` | Send text to any chat by ID, supports reply threading |
349
+ | `send_image_as_user` | Send image (requires `image_key` from `upload_image`) |
350
+ | `send_file_as_user` | Send file (requires `file_key` from `upload_file`) |
351
+ | `send_post_as_user` | Send rich text with title + formatted paragraphs |
323
352
  | `send_sticker_as_user` | Send sticker/emoji |
324
353
  | `send_audio_as_user` | Send audio message |
325
354
 
326
- ### User Identity -- Contacts & Info (cookie auth)
355
+ ### User Identity -- Contacts & Info (5 tools, cookie auth)
327
356
 
328
357
  | Tool | Description |
329
358
  |------|-------------|
330
359
  | `search_contacts` | Search users, bots, or group chats by name |
331
- | `create_p2p_chat` | Create/get P2P (direct message) chat, returns numeric `chat_id` |
332
- | `get_chat_info` | Group details: name, description, member count, owner |
360
+ | `create_p2p_chat` | Create/get P2P (direct message) chat |
361
+ | `get_chat_info` | Group details (supports both oc_xxx and numeric ID) |
333
362
  | `get_user_info` | User display name lookup by user ID |
334
363
  | `get_login_status` | Check cookie, app credentials, and UAT status |
335
364
 
336
- ### User OAuth UAT -- P2P Chat Reading
365
+ ### User OAuth UAT -- P2P Chat Reading (2 tools)
337
366
 
338
367
  | Tool | Description |
339
368
  |------|-------------|
340
- | `read_p2p_messages` | Read P2P (direct message) history. Works for chats the bot cannot access. |
341
- | `list_user_chats` | List group chats the user is in. Note: only returns groups, not P2P. |
369
+ | `read_p2p_messages` | Read P2P (direct message) history |
370
+ | `list_user_chats` | List group chats the user is in |
342
371
 
343
- ### Official API -- IM (Bot Identity)
372
+ ### Official API -- IM (17 tools)
344
373
 
345
374
  | Tool | Description |
346
375
  |------|-------------|
347
376
  | `list_chats` | List all chats the bot has joined |
348
- | `read_messages` | Read message history (accepts chat name or `oc_xxx` ID) |
349
- | `reply_message` | Reply to a specific message by `message_id` (as bot) |
377
+ | `read_messages` | Read message history (accepts chat name, oc_xxx, or numeric ID) |
378
+ | `send_message_as_bot` | Send message as bot to any chat |
379
+ | `reply_message` | Reply to a specific message (as bot) |
350
380
  | `forward_message` | Forward a message to another chat |
351
-
352
- ### Official API -- Documents
381
+ | `delete_message` | Recall/delete a bot message |
382
+ | `update_message` | Edit a sent bot message |
383
+ | `add_reaction` | Add emoji reaction to a message |
384
+ | `delete_reaction` | Remove emoji reaction |
385
+ | `pin_message` | Pin a message in chat |
386
+ | `unpin_message` | Unpin a message |
387
+ | `create_group` | Create a new group chat |
388
+ | `update_group` | Update group name/description |
389
+ | `list_members` | List group members |
390
+ | `add_members` | Add users to a group |
391
+ | `remove_members` | Remove users from a group |
392
+ | `upload_image` / `upload_file` | Upload image/file, returns key for sending |
393
+
394
+ ### Official API -- Documents (7 tools)
353
395
 
354
396
  | Tool | Description |
355
397
  |------|-------------|
356
398
  | `search_docs` | Search documents by keyword |
357
- | `read_doc` | Read raw text content of a document |
399
+ | `read_doc` | Read raw text content |
400
+ | `get_doc_blocks` | Get structured block tree |
358
401
  | `create_doc` | Create a new document |
402
+ | `create_doc_block` | Insert content blocks into a document |
403
+ | `update_doc_block` | Update a specific block |
404
+ | `delete_doc_blocks` | Delete a range of blocks |
405
+
406
+ ### Official API -- Bitable (17 tools)
407
+
408
+ | Tool | Description |
409
+ |------|-------------|
410
+ | `create_bitable` | Create a new Bitable app |
411
+ | `list_bitable_tables` / `create_bitable_table` / `delete_bitable_table` | Table management |
412
+ | `list_bitable_fields` / `create_bitable_field` / `update_bitable_field` / `delete_bitable_field` | Field management |
413
+ | `list_bitable_views` | List views |
414
+ | `search_bitable_records` / `get_bitable_record` | Query records |
415
+ | `create_bitable_record` / `update_bitable_record` / `delete_bitable_record` | Single record CRUD |
416
+ | `batch_create_bitable_records` / `batch_update_bitable_records` / `batch_delete_bitable_records` | Batch operations (max 500/call) |
359
417
 
360
- ### Official API -- Bitable (Spreadsheets)
418
+ ### Official API -- Calendar (5 tools)
361
419
 
362
420
  | Tool | Description |
363
421
  |------|-------------|
364
- | `list_bitable_tables` | List all tables in a Bitable app |
365
- | `list_bitable_fields` | List all fields (columns) in a table |
366
- | `search_bitable_records` | Query records with filter and sort |
367
- | `create_bitable_record` | Create a new record (row) |
368
- | `update_bitable_record` | Update an existing record |
422
+ | `list_calendars` | List accessible calendars |
423
+ | `create_calendar_event` | Create a calendar event |
424
+ | `list_calendar_events` | List events in a calendar |
425
+ | `delete_calendar_event` | Delete an event |
426
+ | `get_freebusy` | Check user availability |
369
427
 
370
- ### Official API -- Wiki
428
+ ### Official API -- Tasks (5 tools)
371
429
 
372
430
  | Tool | Description |
373
431
  |------|-------------|
374
- | `list_wiki_spaces` | List all accessible wiki spaces |
375
- | `search_wiki` | Search wiki/docs by keyword |
376
- | `list_wiki_nodes` | Browse wiki node tree |
432
+ | `create_task` | Create a task |
433
+ | `get_task` | Get task details |
434
+ | `list_tasks` | List tasks |
435
+ | `update_task` | Update a task |
436
+ | `complete_task` | Mark task as complete |
377
437
 
378
- ### Official API -- Drive
438
+ ### Official API -- Drive (5 tools)
379
439
 
380
440
  | Tool | Description |
381
441
  |------|-------------|
382
442
  | `list_files` | List files in a folder |
383
443
  | `create_folder` | Create a new folder |
444
+ | `copy_file` | Copy a file |
445
+ | `move_file` | Move a file |
446
+ | `delete_file` | Delete a file/folder |
384
447
 
385
- ### Official API -- Contacts
448
+ ### Official API -- Wiki & Contacts (4 tools)
386
449
 
387
450
  | Tool | Description |
388
451
  |------|-------------|
452
+ | `list_wiki_spaces` / `search_wiki` / `list_wiki_nodes` | Wiki spaces, search, browse |
389
453
  | `find_user` | Find user by email or mobile number |
390
454
 
391
455
  ## Claude Code Slash Commands (9 skills)
@@ -444,7 +508,7 @@ feishu-user-plugin/
444
508
  │ ├── SKILL.md # Main skill definition (trigger, tools, auth)
445
509
  │ └── references/ # 8 skill reference docs + CLAUDE.md
446
510
  ├── src/
447
- │ ├── index.js # MCP server entry point (33 tools)
511
+ │ ├── index.js # MCP server entry point (76 tools)
448
512
  │ ├── client.js # User identity client (Protobuf gateway)
449
513
  │ ├── official.js # Official API client (REST, UAT)
450
514
  │ ├── utils.js # ID generators, cookie parser
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "feishu-user-plugin",
3
- "version": "1.3.0",
4
- "description": "All-in-one Feishu plugin for Claude Code — messaging, docs, bitable, calendar, tasks, drive. 76 tools + 9 skills, 3 auth layers.",
3
+ "version": "1.3.1",
4
+ "description": "All-in-one Feishu plugin for Claude Code & Codex — messaging, docs, bitable, wiki, drive. 66 tools + 9 skills, 3 auth layers.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "feishu-user-plugin": "src/cli.js"
@@ -10,7 +10,8 @@
10
10
  "start": "node src/index.js",
11
11
  "test": "node src/test-all.js",
12
12
  "test:quick": "node src/test-send.js",
13
- "oauth": "node src/oauth.js"
13
+ "oauth": "node src/oauth.js",
14
+ "prepublishOnly": "node scripts/confirm-version.js"
14
15
  },
15
16
  "keywords": [
16
17
  "feishu",
@@ -37,6 +38,7 @@
37
38
  "files": [
38
39
  "src/",
39
40
  "proto/",
41
+ "scripts/",
40
42
  ".claude-plugin/",
41
43
  "skills/",
42
44
  ".mcp.json.example",
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Pre-publish version confirmation gate.
4
+ * Runs as prepublishOnly — blocks `npm publish` until version is confirmed.
5
+ * Automatically skipped in CI (GitHub Actions handles tag/version check separately).
6
+ */
7
+
8
+ if (process.env.CI || process.env.GITHUB_ACTIONS) {
9
+ process.exit(0);
10
+ }
11
+
12
+ const readline = require('readline');
13
+ const pkg = require('../package.json');
14
+
15
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
16
+
17
+ console.log(`\n Package: ${pkg.name}`);
18
+ console.log(` Version: ${pkg.version}`);
19
+ console.log(` Tools: ${pkg.description}\n`);
20
+
21
+ rl.question(` Confirm publish v${pkg.version}? (y/N): `, (answer) => {
22
+ rl.close();
23
+ if (answer.trim().toLowerCase() !== 'y') {
24
+ console.error('\n Publish cancelled. Update version in package.json if needed.\n');
25
+ process.exit(1);
26
+ }
27
+ console.log(' Confirmed. Proceeding with publish...\n');
28
+ });
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process');
4
+
5
+ const defaultCommand = ['node', '/Users/abble/feishu-user-plugin/src/index.js'];
6
+ const childCommand = process.argv.slice(2);
7
+ const [command, ...args] = childCommand.length > 0 ? childCommand : defaultCommand;
8
+
9
+ const child = spawn(command, args, {
10
+ stdio: ['pipe', 'pipe', 'pipe'],
11
+ env: process.env,
12
+ });
13
+
14
+ let parentMode = null; // 'content-length' | 'newline'
15
+ let parentInBuffer = Buffer.alloc(0);
16
+ let childOutBuffer = '';
17
+
18
+ function writeToParent(jsonLine) {
19
+ if (parentMode === 'content-length') {
20
+ const body = Buffer.from(jsonLine, 'utf8');
21
+ process.stdout.write(`Content-Length: ${body.length}\r\n\r\n`);
22
+ process.stdout.write(body);
23
+ return;
24
+ }
25
+ process.stdout.write(jsonLine + '\n');
26
+ }
27
+
28
+ function handleParentMessage(message) {
29
+ const line = typeof message === 'string' ? message : JSON.stringify(message);
30
+ child.stdin.write(line + '\n');
31
+ }
32
+
33
+ function tryParseParentBuffer() {
34
+ while (parentInBuffer.length > 0) {
35
+ if (parentMode === 'content-length' || (!parentMode && parentInBuffer.includes(Buffer.from('\r\n\r\n')))) {
36
+ parentMode = 'content-length';
37
+ const headerEnd = parentInBuffer.indexOf(Buffer.from('\r\n\r\n'));
38
+ if (headerEnd === -1) return;
39
+ const headerText = parentInBuffer.subarray(0, headerEnd).toString('utf8');
40
+ const match = headerText.match(/Content-Length:\s*(\d+)/i);
41
+ if (!match) {
42
+ process.stderr.write('[mcp-bridge] Missing Content-Length header\n');
43
+ process.exit(1);
44
+ }
45
+ const bodyLength = Number(match[1]);
46
+ const totalLength = headerEnd + 4 + bodyLength;
47
+ if (parentInBuffer.length < totalLength) return;
48
+ const body = parentInBuffer.subarray(headerEnd + 4, totalLength).toString('utf8');
49
+ parentInBuffer = parentInBuffer.subarray(totalLength);
50
+ handleParentMessage(body);
51
+ continue;
52
+ }
53
+
54
+ parentMode = 'newline';
55
+ const newlineIndex = parentInBuffer.indexOf(0x0a);
56
+ if (newlineIndex === -1) return;
57
+ const line = parentInBuffer.subarray(0, newlineIndex).toString('utf8').replace(/\r$/, '');
58
+ parentInBuffer = parentInBuffer.subarray(newlineIndex + 1);
59
+ if (line.trim()) handleParentMessage(line);
60
+ }
61
+ }
62
+
63
+ process.stdin.on('data', (chunk) => {
64
+ parentInBuffer = Buffer.concat([parentInBuffer, chunk]);
65
+ tryParseParentBuffer();
66
+ });
67
+
68
+ child.stdout.on('data', (chunk) => {
69
+ childOutBuffer += chunk.toString('utf8');
70
+ while (true) {
71
+ const newlineIndex = childOutBuffer.indexOf('\n');
72
+ if (newlineIndex === -1) break;
73
+ const line = childOutBuffer.slice(0, newlineIndex).replace(/\r$/, '');
74
+ childOutBuffer = childOutBuffer.slice(newlineIndex + 1);
75
+ if (line.trim()) writeToParent(line);
76
+ }
77
+ });
78
+
79
+ child.stderr.on('data', (chunk) => {
80
+ process.stderr.write(chunk);
81
+ });
82
+
83
+ child.on('exit', (code, signal) => {
84
+ if (signal) {
85
+ process.stderr.write(`[mcp-bridge] Child exited via signal ${signal}\n`);
86
+ process.exit(1);
87
+ }
88
+ process.exit(code ?? 0);
89
+ });
90
+
91
+ child.on('error', (error) => {
92
+ process.stderr.write(`[mcp-bridge] Failed to spawn child: ${error.message}\n`);
93
+ process.exit(1);
94
+ });
95
+
96
+ process.on('SIGINT', () => child.kill('SIGINT'));
97
+ process.on('SIGTERM', () => child.kill('SIGTERM'));
@@ -6,7 +6,7 @@ All-in-one Feishu plugin for Claude Code with three auth layers:
6
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 (76 tools)
9
+ ## Tool Categories (66 tools)
10
10
 
11
11
  ### User Identity — Messaging (reverse-engineered, cookie-based)
12
12
  - `send_to_user` — Search user + send text (one step, most common). Returns candidates if multiple matches.
@@ -25,9 +25,10 @@ All-in-one Feishu plugin for Claude Code with three auth layers:
25
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)
28
+ ### User OAuth UAT Tools (P2P chat reading + user-identity creation)
29
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
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,26 +36,22 @@ All-in-one Feishu plugin for Claude Code with three auth layers:
35
36
  - `reply_message` / `forward_message` — Message operations (as bot)
36
37
  - `delete_message` / `update_message` — Recall or edit bot's own messages
37
38
  - `add_reaction` / `delete_reaction` — Emoji reactions on messages
38
- - `pin_message` / `unpin_message` Pin/unpin messages in chat
39
+ - `pin_message` Pin or unpin a message (pinned=true/false)
39
40
  - `create_group` / `update_group` — Create and manage group chats
40
- - `list_members` / `add_members` / `remove_members` — Group membership management
41
+ - `list_members` / `manage_members` — Group membership (manage_members: action=add/remove)
41
42
  - `search_docs` / `read_doc` / `get_doc_blocks` / `create_doc` — Document operations
42
43
  - `create_doc_block` / `update_doc_block` / `delete_doc_blocks` — Document content editing (insert/update/delete blocks)
43
- - `create_bitable` Create a new Bitable (multi-dimensional table) app
44
- - `list_bitable_tables` / `create_bitable_table` — Table management
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)
45
46
  - `list_bitable_fields` / `create_bitable_field` / `update_bitable_field` / `delete_bitable_field` — Field (column) management
46
- - `list_bitable_views` — List views in a table
47
- - `search_bitable_records` — Query records with filter/sort
48
- - `create_bitable_record` / `update_bitable_record` / `delete_bitable_record` — Single record CRUD
49
- - `batch_create_bitable_records` / `batch_update_bitable_records` / `batch_delete_bitable_records` — Batch operations (max 500/call)
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)
50
50
  - `list_wiki_spaces` / `search_wiki` / `list_wiki_nodes` — Wiki
51
51
  - `list_files` / `create_folder` — Drive
52
52
  - `copy_file` / `move_file` / `delete_file` — Drive file operations (copy, move, delete)
53
53
  - `upload_image` / `upload_file` — Upload image/file, returns key for send_image/send_file
54
54
  - `find_user` — Contact lookup by email/mobile
55
- - `list_calendars` / `create_calendar_event` / `list_calendar_events` / `delete_calendar_event` — Calendar management
56
- - `get_freebusy` — Check user availability
57
- - `create_task` / `get_task` / `list_tasks` / `update_task` / `complete_task` — Task management
58
55
 
59
56
  ## Usage Patterns
60
57
 
@@ -73,14 +70,18 @@ All-in-one Feishu plugin for Claude Code with three auth layers:
73
70
 
74
71
  ### Bitable (Multi-dimensional Tables)
75
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
76
75
  - Query data → `list_bitable_tables` → `list_bitable_fields` → `search_bitable_records`
77
- - Single record CRUD → `create_bitable_record` / `update_bitable_record` / `delete_bitable_record`
78
- - Bulk operations → `batch_create_bitable_records` / `batch_update_bitable_records` / `batch_delete_bitable_records` (max 500/call)
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
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`
80
81
 
81
82
  ### Group Management
82
83
  - Create a group → `create_group` with name and optional member open_ids
83
- - Add/remove members → `add_members` / `remove_members` with chat_id + user open_ids
84
+ - Add/remove members → `manage_members` with chat_id + member_ids + action (add/remove)
84
85
  - List members → `list_members`
85
86
 
86
87
  ### Document Editing
@@ -88,15 +89,6 @@ All-in-one Feishu plugin for Claude Code with three auth layers:
88
89
  - Edit existing block → `get_doc_blocks` to find block_id → `update_doc_block`
89
90
  - Delete blocks → `delete_doc_blocks` with start/end index range
90
91
 
91
- ### Calendar
92
- - View schedule → `list_calendars` → `list_calendar_events`
93
- - Create event → `create_calendar_event` with calendar_id, summary, start/end time
94
- - Check availability → `get_freebusy` with user open_ids and time range
95
-
96
- ### Tasks
97
- - Create task → `create_task` with summary, optional description/due
98
- - Track tasks → `list_tasks` → `update_task` / `complete_task`
99
-
100
92
  ### Diagnostics
101
93
  - Diagnose issues → `get_login_status` first
102
94
 
@@ -237,10 +229,16 @@ Tell user to restart Claude Code. Only ONE restart should be needed.
237
229
 
238
230
  ## Troubleshooting Guide
239
231
 
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
+
240
237
  ### If MCP tools are not available
241
238
  1. Check `~/.claude.json` — config must be in **top-level** `mcpServers`, not inside `projects[*]`
242
- 2. Restart Claude Code after config changes
243
- 3. After restart, tools may take a few seconds to register — if first call fails with "No such tool", wait and retry once
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
244
242
 
245
243
  ### If cookie authentication fails
246
244
  - `document.cookie` in browser console CANNOT access HttpOnly cookies (`session`, `sl_session`)
@@ -282,9 +280,17 @@ Tell user to restart Claude Code. Only ONE restart should be needed.
282
280
  - **team-skills plugin**: Skills + CLAUDE.md only (no .mcp.json). For internal team members.
283
281
 
284
282
  ### Config management
285
- - `src/config.js`: Unified config module. Discovers config in `~/.claude.json` (top-level + project-level) and `.mcp.json`.
286
- - `setup` always writes to `~/.claude.json` top-level `mcpServers` (global).
287
- - `persistToConfig()` finds the correct config entry and writes back (used by heartbeat + UAT refresh).
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
288
294
 
289
295
  ## Development & Publishing
290
296
 
@@ -315,6 +321,32 @@ cp .claude-plugin/plugin.json /path/to/team-skills/plugins/feishu-user-plugin/.c
315
321
 
316
322
  ## Development Workflow
317
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
+
318
350
  ### Keeping ROADMAP.md up to date
319
351
  - When completing a feature or fixing a bug, check the corresponding item in ROADMAP.md as `[x]` done
320
352
  - When discovering new bugs, limitations, or feature ideas during development, add them to the appropriate section in ROADMAP.md
@@ -341,10 +373,20 @@ cp .claude-plugin/plugin.json /path/to/team-skills/plugins/feishu-user-plugin/.c
341
373
  - `chore:` dependencies, CI, config changes
342
374
 
343
375
  ### Publishing
344
- 1. Update `version` in `package.json`
345
- 2. `git add <files> && git commit -m "v1.x.x: description"`
346
- 3. `git tag v1.x.x && git push && git push --tags`
347
- 4. GitHub Actions auto-publishes to npm. Users get the new version on next Claude Code restart.
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
348
390
 
349
391
  ### Syncing to team-skills (after any CLAUDE.md or skills change)
350
392
  1. Copy CLAUDE.md to skill reference: `cp CLAUDE.md skills/feishu-user-plugin/references/CLAUDE.md`
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: