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 +103 -39
- package/package.json +5 -3
- package/scripts/confirm-version.js +28 -0
- package/scripts/mcp_stdio_bridge.js +97 -0
- package/skills/feishu-user-plugin/references/CLAUDE.md +76 -34
- package/src/cli.js +12 -7
- package/src/config.js +202 -27
- package/src/index.js +124 -237
- package/src/oauth.js +2 -1
- package/src/official.js +103 -109
- package/src/setup.js +19 -3
package/README.md
CHANGED
|
@@ -3,22 +3,25 @@
|
|
|
3
3
|
[](LICENSE)
|
|
4
4
|
[](https://nodejs.org)
|
|
5
5
|
[](https://modelcontextprotocol.io)
|
|
6
|
-
[](#tools)
|
|
7
7
|
[](CONTRIBUTING.md)
|
|
8
8
|
|
|
9
|
-
**All-in-one Feishu/Lark MCP Server --
|
|
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
|
|
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
|
|
18
|
-
- **3 auth layers** -- Cookie-based user identity, app credentials (Official API), and OAuth UAT (P2P reading).
|
|
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
|
-
- **
|
|
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 (
|
|
340
|
+
## Tools (76 total)
|
|
310
341
|
|
|
311
|
-
### User Identity -- Messaging (cookie auth
|
|
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
|
|
320
|
-
| `send_image_as_user` | Send image (requires `image_key` from
|
|
321
|
-
| `send_file_as_user` | Send file (requires `file_key` from
|
|
322
|
-
| `send_post_as_user` | Send rich text with title + formatted paragraphs
|
|
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
|
|
332
|
-
| `get_chat_info` | Group details
|
|
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
|
|
341
|
-
| `list_user_chats` | List group chats the user is in
|
|
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 (
|
|
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
|
|
349
|
-
| `
|
|
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
|
-
|
|
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
|
|
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 --
|
|
418
|
+
### Official API -- Calendar (5 tools)
|
|
361
419
|
|
|
362
420
|
| Tool | Description |
|
|
363
421
|
|------|-------------|
|
|
364
|
-
| `
|
|
365
|
-
| `
|
|
366
|
-
| `
|
|
367
|
-
| `
|
|
368
|
-
| `
|
|
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 --
|
|
428
|
+
### Official API -- Tasks (5 tools)
|
|
371
429
|
|
|
372
430
|
| Tool | Description |
|
|
373
431
|
|------|-------------|
|
|
374
|
-
| `
|
|
375
|
-
| `
|
|
376
|
-
| `
|
|
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 (
|
|
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.
|
|
4
|
-
"description": "All-in-one Feishu plugin for Claude Code — messaging, docs, bitable,
|
|
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 (
|
|
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`
|
|
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` / `
|
|
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`
|
|
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` —
|
|
47
|
-
- `search_bitable_records` — Query records
|
|
48
|
-
- `
|
|
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
|
-
-
|
|
78
|
-
-
|
|
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 → `
|
|
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.
|
|
243
|
-
3.
|
|
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)
|
|
286
|
-
- `setup`
|
|
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
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
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 (
|
|
57
|
-
1.
|
|
58
|
-
2.
|
|
59
|
-
3.
|
|
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:
|