feishu-user-plugin 1.3.9 → 1.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +27 -0
- package/.mcpb/manifest.json +91 -0
- package/CHANGELOG.md +68 -0
- package/PRIVACY.md +105 -0
- package/README.en.md +610 -0
- package/README.md +309 -529
- package/package.json +7 -2
- package/scripts/build-mcpb.js +119 -0
- package/scripts/check-mcp-registry-version.js +43 -0
- package/scripts/check-mcpb-version.js +33 -0
- package/scripts/check-version.js +5 -0
- package/scripts/sync-team-skills.sh +72 -57
- package/skills/feishu-user-plugin/SKILL.md +1 -1
- package/skills/feishu-user-plugin/references/CLAUDE.md +1 -0
- package/src/auth/credentials.js +49 -0
- package/src/auth/lark-desktop.js +135 -0
- package/src/server.js +42 -0
- package/src/setup.js +44 -0
- package/src/test-lark-desktop.js +300 -0
package/README.en.md
ADDED
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
# feishu-user-plugin
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://nodejs.org)
|
|
5
|
+
[](https://modelcontextprotocol.io)
|
|
6
|
+
[](#tools)
|
|
7
|
+
[](CONTRIBUTING.md)
|
|
8
|
+
|
|
9
|
+
**All-in-one Feishu/Lark MCP Server -- 84 tools, 9 skills, 3 auth layers for messaging, docs, bitable, calendar, tasks, drive, OKR, and more.**
|
|
10
|
+
|
|
11
|
+
> [中文 README](README.md) is the primary version. This English README mirrors it.
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
## Highlights
|
|
16
|
+
|
|
17
|
+
- **Send as yourself** -- Messages show your real name, not a bot. Supports text, rich text, images, files, stickers, and audio.
|
|
18
|
+
- **Read everything** -- Group chats via bot API, P2P (direct messages) via OAuth UAT.
|
|
19
|
+
- **Full Feishu suite** -- Docs, Bitable, Wiki, Drive, Calendar, Tasks, Contacts -- all in one plugin.
|
|
20
|
+
- **3 auth layers** -- Cookie-based user identity, app credentials (Official API), and OAuth UAT (P2P reading).
|
|
21
|
+
- **Group management** -- Create groups, add/remove members, pin messages, emoji reactions.
|
|
22
|
+
- **Document editing** -- Not just read/create, but insert/update/delete content blocks.
|
|
23
|
+
- **Calendar & Tasks** -- Create events, check free/busy, manage tasks.
|
|
24
|
+
- **9 slash commands** for Claude Code -- `/send`, `/reply`, `/search`, `/digest`, `/doc`, `/table`, `/wiki`, `/drive`, `/status`
|
|
25
|
+
- **Auto session management** -- Cookie heartbeat every 4h, UAT auto-refresh with token rotation.
|
|
26
|
+
- **Real-time events** (v1.3.9) -- Machine-level WS: one owner process writes to `~/.feishu-user-plugin/events.jsonl`, all harnesses drain from a shared cursor — every event delivered exactly once across all MCP processes. `manage_ws_status` to diagnose/control. WS auto-starts on boot.
|
|
27
|
+
- **Multi-platform** -- Claude Code, Cursor, Windsurf, VS Code, OpenClaw.
|
|
28
|
+
|
|
29
|
+
## Why This Exists
|
|
30
|
+
|
|
31
|
+
Feishu's official API has a hard limitation: **there is no `send_as_user` scope**. Even with `user_access_token` (OAuth), messages still show `sender_type: "app"`.
|
|
32
|
+
|
|
33
|
+
This project combines three auth layers into one plugin:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
User Identity (cookie): You -> Protobuf -> Feishu (messages appear as YOU)
|
|
37
|
+
Official API (app token): You -> REST API -> Feishu (docs, tables, wiki, drive)
|
|
38
|
+
User OAuth (UAT): You -> REST API -> Feishu (read P2P chats, list all chats)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**One plugin. Everything Feishu. No other MCP needed.**
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
### Option 1: npx (recommended)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx feishu-user-plugin
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
No installation needed. The package runs directly via npx.
|
|
52
|
+
|
|
53
|
+
### Option 2: Clone and run locally
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/EthanQC/feishu-user-plugin.git
|
|
57
|
+
cd feishu-user-plugin
|
|
58
|
+
npm install
|
|
59
|
+
npm start
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Create Your Feishu App
|
|
63
|
+
|
|
64
|
+
To use the Official API tools (docs, tables, wiki, drive, bot messaging), you need to create a Feishu app:
|
|
65
|
+
|
|
66
|
+
### Step 1: Create the App
|
|
67
|
+
|
|
68
|
+
1. Go to [Feishu Open Platform](https://open.feishu.cn/app) and log in
|
|
69
|
+
2. Click **Create Custom App** (创建自建应用) -- you must choose **Custom App** (自建应用), NOT marketplace/third-party types
|
|
70
|
+
3. Fill in the app name and description, then create it
|
|
71
|
+
|
|
72
|
+
### Step 2: Enable Bot Capability
|
|
73
|
+
|
|
74
|
+
1. In your app settings, go to **Add Capabilities** (添加应用能力)
|
|
75
|
+
2. Enable **Bot** (机器人)
|
|
76
|
+
|
|
77
|
+
### Step 3: Add Permissions (Scopes)
|
|
78
|
+
|
|
79
|
+
Go to **Permissions & Scopes** (权限管理) and add the following scopes:
|
|
80
|
+
|
|
81
|
+
| Scope | Purpose |
|
|
82
|
+
|-------|---------|
|
|
83
|
+
| `im:message` | Send messages as bot |
|
|
84
|
+
| `im:message:readonly` | Read message history |
|
|
85
|
+
| `im:chat:readonly` | List and read chats |
|
|
86
|
+
| `docx:document` | Read and create documents |
|
|
87
|
+
| `docx:document:readonly` | Read documents |
|
|
88
|
+
| `bitable:record` | Read and write Bitable records |
|
|
89
|
+
| `wiki:wiki:readonly` | Read wiki spaces and nodes |
|
|
90
|
+
| `drive:drive:readonly` | List Drive files and folders |
|
|
91
|
+
| `contact:user.base:readonly` | Look up users by email/mobile |
|
|
92
|
+
|
|
93
|
+
> Add more scopes as needed depending on which tools you use.
|
|
94
|
+
|
|
95
|
+
### Step 4: Get App Credentials
|
|
96
|
+
|
|
97
|
+
1. Go to **Credentials & Basic Info** (凭证与基础信息)
|
|
98
|
+
2. Copy the **App ID** (`cli_xxxxxxxxxxxx`) and **App Secret**
|
|
99
|
+
3. Set them as `LARK_APP_ID` and `LARK_APP_SECRET` in your environment
|
|
100
|
+
|
|
101
|
+
### Step 5: Publish and Approve
|
|
102
|
+
|
|
103
|
+
1. **Create a version** and submit it for review (创建版本)
|
|
104
|
+
2. Have your organization admin approve the app (管理员审核)
|
|
105
|
+
3. After approval, the app is live
|
|
106
|
+
|
|
107
|
+
### Step 6: Add Bot to Group Chats
|
|
108
|
+
|
|
109
|
+
Add your bot to the group chats where you want it to read messages. The bot can only access chats it has been added to.
|
|
110
|
+
|
|
111
|
+
## Environment Variables
|
|
112
|
+
|
|
113
|
+
| Variable | Required For | Description |
|
|
114
|
+
|----------|-------------|-------------|
|
|
115
|
+
| `LARK_COOKIE` | User identity tools | Feishu web session cookie string. Needed for `send_to_user`, `send_to_group`, `search_contacts`, etc. |
|
|
116
|
+
| `LARK_APP_ID` | Official API tools | App ID from Feishu Open Platform. Needed for `read_messages`, docs, tables, wiki, drive. |
|
|
117
|
+
| `LARK_APP_SECRET` | Official API tools | App Secret from Feishu Open Platform. Used together with `LARK_APP_ID`. |
|
|
118
|
+
| `LARK_USER_ACCESS_TOKEN` | P2P chat reading | OAuth user token. Needed for `read_p2p_messages` and `list_user_chats`. Obtained via `node src/oauth.js`. |
|
|
119
|
+
| `LARK_USER_REFRESH_TOKEN` | UAT auto-refresh | Refresh token for automatic UAT renewal. Obtained together with UAT via OAuth flow. |
|
|
120
|
+
|
|
121
|
+
All five variables are required for full functionality. Configure all of them during setup.
|
|
122
|
+
|
|
123
|
+
## How to Get Your Cookie
|
|
124
|
+
|
|
125
|
+
**Option A: Automated via Playwright MCP (recommended, zero manual copying)**
|
|
126
|
+
|
|
127
|
+
First, install Playwright MCP if you don't have it:
|
|
128
|
+
```bash
|
|
129
|
+
npx @anthropic-ai/claude-code mcp add playwright -- npx @anthropic-ai/mcp-server-playwright
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Then just tell Claude Code: **"Help me set up my Feishu cookie"**
|
|
133
|
+
|
|
134
|
+
Claude Code will automatically:
|
|
135
|
+
1. Open feishu.cn in a browser via Playwright
|
|
136
|
+
2. Show you the QR code — scan it with Feishu mobile app
|
|
137
|
+
3. Extract the full cookie (including HttpOnly) via `context.cookies()`
|
|
138
|
+
4. Write it to your `.mcp.json` LARK_COOKIE field
|
|
139
|
+
5. Prompt you to restart Claude Code
|
|
140
|
+
|
|
141
|
+
**Option B: Manual (via Network tab)**
|
|
142
|
+
|
|
143
|
+
1. Open [feishu.cn/messenger](https://www.feishu.cn/messenger/) in your browser and log in
|
|
144
|
+
2. Open DevTools (`F12` or `Cmd+Option+I`)
|
|
145
|
+
3. Go to the **Network** tab → check **Disable cache** → press `Cmd+R` to reload
|
|
146
|
+
4. Click the first request in the list (usually the page itself)
|
|
147
|
+
5. In the right panel, find **Request Headers** → **Cookie:** → right-click → **Copy value**
|
|
148
|
+
6. Set it as `LARK_COOKIE` in your environment
|
|
149
|
+
|
|
150
|
+
> Do NOT use `document.cookie` in the Console or copy from Application → Cookies tab — they miss HttpOnly cookies (`session`, `sl_session`) required for auth.
|
|
151
|
+
|
|
152
|
+
> The server automatically refreshes the session via heartbeat every 4 hours. The `sl_session` cookie has a 12-hour max-age.
|
|
153
|
+
|
|
154
|
+
## Set Up OAuth (Required for P2P Chat Reading)
|
|
155
|
+
|
|
156
|
+
To enable `read_p2p_messages` and `list_user_chats`:
|
|
157
|
+
|
|
158
|
+
1. Your Feishu app must be a **Custom App** (自建应用), NOT marketplace/third-party
|
|
159
|
+
2. Add scopes: `im:message`, `im:message:readonly`, `im:chat:readonly`
|
|
160
|
+
3. In your app's **Security Settings** (安全设置), add the OAuth redirect URI: `http://127.0.0.1:9997/callback`
|
|
161
|
+
4. **Important**: Make sure "对外共享" (external sharing) is **disabled** in your app version settings — enabling it marks the app as b2c/b2b type, which blocks P2P chat access
|
|
162
|
+
5. Run the authorization flow:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# If you cloned the repo:
|
|
166
|
+
node src/oauth.js
|
|
167
|
+
|
|
168
|
+
# If you installed via npx:
|
|
169
|
+
cd $(npm root -g)/feishu-user-plugin && node src/oauth.js
|
|
170
|
+
# Or clone the repo just for the OAuth step, then use npx for daily use
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
A browser window will open for OAuth consent. The token is saved to `.env` automatically and auto-refreshes at runtime. Add both `LARK_USER_ACCESS_TOKEN` and `LARK_USER_REFRESH_TOKEN` from `.env` to your MCP config's `env` section.
|
|
174
|
+
|
|
175
|
+
## MCP Client Configuration
|
|
176
|
+
|
|
177
|
+
### Claude Code
|
|
178
|
+
|
|
179
|
+
Add to your project's `.mcp.json` (or `~/.claude/.mcp.json` for global):
|
|
180
|
+
|
|
181
|
+
**Using npx:**
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"mcpServers": {
|
|
186
|
+
"feishu": {
|
|
187
|
+
"command": "npx",
|
|
188
|
+
"args": ["-y", "feishu-user-plugin"],
|
|
189
|
+
"env": {
|
|
190
|
+
"LARK_COOKIE": "your-cookie-string",
|
|
191
|
+
"LARK_APP_ID": "cli_xxxxxxxxxxxx",
|
|
192
|
+
"LARK_APP_SECRET": "your-app-secret",
|
|
193
|
+
"LARK_USER_ACCESS_TOKEN": "your-uat",
|
|
194
|
+
"LARK_USER_REFRESH_TOKEN": "your-refresh-token"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Using a local clone:**
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"mcpServers": {
|
|
206
|
+
"feishu": {
|
|
207
|
+
"command": "node",
|
|
208
|
+
"args": ["/absolute/path/to/feishu-user-plugin/src/index.js"],
|
|
209
|
+
"env": {
|
|
210
|
+
"LARK_COOKIE": "your-cookie-string",
|
|
211
|
+
"LARK_APP_ID": "cli_xxxxxxxxxxxx",
|
|
212
|
+
"LARK_APP_SECRET": "your-app-secret",
|
|
213
|
+
"LARK_USER_ACCESS_TOKEN": "your-uat",
|
|
214
|
+
"LARK_USER_REFRESH_TOKEN": "your-refresh-token"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Then just say things like:
|
|
222
|
+
- "Send a message to Alice saying the meeting is at 3pm"
|
|
223
|
+
- "What did the engineering group chat about today?"
|
|
224
|
+
- "Search for docs about MCP"
|
|
225
|
+
|
|
226
|
+
### Claude Desktop
|
|
227
|
+
|
|
228
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"mcpServers": {
|
|
233
|
+
"feishu": {
|
|
234
|
+
"command": "npx",
|
|
235
|
+
"args": ["-y", "feishu-user-plugin"],
|
|
236
|
+
"env": {
|
|
237
|
+
"LARK_COOKIE": "your-cookie-string",
|
|
238
|
+
"LARK_APP_ID": "cli_xxxxxxxxxxxx",
|
|
239
|
+
"LARK_APP_SECRET": "your-app-secret",
|
|
240
|
+
"LARK_USER_ACCESS_TOKEN": "your-uat",
|
|
241
|
+
"LARK_USER_REFRESH_TOKEN": "your-refresh-token"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Cursor
|
|
249
|
+
|
|
250
|
+
Add to `.cursor/mcp.json` in your project:
|
|
251
|
+
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"mcpServers": {
|
|
255
|
+
"feishu": {
|
|
256
|
+
"command": "npx",
|
|
257
|
+
"args": ["-y", "feishu-user-plugin"],
|
|
258
|
+
"env": {
|
|
259
|
+
"LARK_COOKIE": "your-cookie-string",
|
|
260
|
+
"LARK_APP_ID": "cli_xxxxxxxxxxxx",
|
|
261
|
+
"LARK_APP_SECRET": "your-app-secret",
|
|
262
|
+
"LARK_USER_ACCESS_TOKEN": "your-uat",
|
|
263
|
+
"LARK_USER_REFRESH_TOKEN": "your-refresh-token"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### VS Code (Copilot)
|
|
271
|
+
|
|
272
|
+
Add to `.vscode/mcp.json` in your project:
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"servers": {
|
|
277
|
+
"feishu": {
|
|
278
|
+
"type": "stdio",
|
|
279
|
+
"command": "npx",
|
|
280
|
+
"args": ["-y", "feishu-user-plugin"],
|
|
281
|
+
"env": {
|
|
282
|
+
"LARK_COOKIE": "your-cookie-string",
|
|
283
|
+
"LARK_APP_ID": "cli_xxxxxxxxxxxx",
|
|
284
|
+
"LARK_APP_SECRET": "your-app-secret",
|
|
285
|
+
"LARK_USER_ACCESS_TOKEN": "your-uat",
|
|
286
|
+
"LARK_USER_REFRESH_TOKEN": "your-refresh-token"
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### OpenClaw
|
|
294
|
+
|
|
295
|
+
Add to `~/.openclaw/openclaw.json` (note: key path is `mcp.servers`, not `mcpServers`):
|
|
296
|
+
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"mcp": {
|
|
300
|
+
"servers": {
|
|
301
|
+
"feishu-user-plugin": {
|
|
302
|
+
"command": "npx",
|
|
303
|
+
"args": ["-y", "feishu-user-plugin"],
|
|
304
|
+
"env": {
|
|
305
|
+
"LARK_COOKIE": "your-cookie-string",
|
|
306
|
+
"LARK_APP_ID": "cli_xxxxxxxxxxxx",
|
|
307
|
+
"LARK_APP_SECRET": "your-app-secret",
|
|
308
|
+
"LARK_USER_ACCESS_TOKEN": "your-uat",
|
|
309
|
+
"LARK_USER_REFRESH_TOKEN": "your-refresh-token"
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Or via CLI: `openclaw mcp set feishu-user-plugin '{"command":"npx","args":["-y","feishu-user-plugin"],"env":{...}}'`
|
|
318
|
+
|
|
319
|
+
> OpenClaw's built-in Feishu channel handles receiving messages (bot identity). This plugin adds user identity messaging + docs/bitable/calendar/tasks.
|
|
320
|
+
|
|
321
|
+
### Windsurf
|
|
322
|
+
|
|
323
|
+
Add to `~/.codeium/windsurf/mcp_config.json`:
|
|
324
|
+
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"mcpServers": {
|
|
328
|
+
"feishu": {
|
|
329
|
+
"command": "npx",
|
|
330
|
+
"args": ["-y", "feishu-user-plugin"],
|
|
331
|
+
"env": {
|
|
332
|
+
"LARK_COOKIE": "your-cookie-string",
|
|
333
|
+
"LARK_APP_ID": "cli_xxxxxxxxxxxx",
|
|
334
|
+
"LARK_APP_SECRET": "your-app-secret",
|
|
335
|
+
"LARK_USER_ACCESS_TOKEN": "your-uat",
|
|
336
|
+
"LARK_USER_REFRESH_TOKEN": "your-refresh-token"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Tools (80 total)
|
|
344
|
+
|
|
345
|
+
### User Identity -- Messaging (10 tools, cookie auth)
|
|
346
|
+
|
|
347
|
+
| Tool | Description |
|
|
348
|
+
|------|-------------|
|
|
349
|
+
| `send_to_user` | Search user by name + send text -- one step |
|
|
350
|
+
| `send_to_group` | Search group by name + send text -- one step |
|
|
351
|
+
| `send_as_user` | Send text to any chat by ID, supports reply threading |
|
|
352
|
+
| `send_image_as_user` | Send image (requires `image_key` from `upload_image`) |
|
|
353
|
+
| `send_file_as_user` | Send file (requires `file_key` from `upload_file`) |
|
|
354
|
+
| `send_post_as_user` | Send rich text with title + formatted paragraphs |
|
|
355
|
+
| `batch_send` | Fan-out send to multiple targets in one call (text / image / file / post). v1.3.6 |
|
|
356
|
+
| `send_card_as_user` | Send a Feishu interactive card via bot identity (Official API). User-identity card sending is server-side disabled at the Feishu cookie auth tier — confirmed in v1.3.9. |
|
|
357
|
+
|
|
358
|
+
### User Identity -- Contacts & Info (5 tools, cookie auth)
|
|
359
|
+
|
|
360
|
+
| Tool | Description |
|
|
361
|
+
|------|-------------|
|
|
362
|
+
| `search_contacts` | Search users, bots, or group chats by name |
|
|
363
|
+
| `create_p2p_chat` | Create/get P2P (direct message) chat |
|
|
364
|
+
| `get_chat_info` | Group details (supports both oc_xxx and numeric ID) |
|
|
365
|
+
| `get_user_info` | User display name lookup by user ID |
|
|
366
|
+
| `get_login_status` | Check cookie, app credentials, and UAT status |
|
|
367
|
+
|
|
368
|
+
### User OAuth UAT -- P2P Chat Reading (2 tools)
|
|
369
|
+
|
|
370
|
+
| Tool | Description |
|
|
371
|
+
|------|-------------|
|
|
372
|
+
| `read_p2p_messages` | Read P2P (direct message) history |
|
|
373
|
+
| `list_user_chats` | List group chats the user is in |
|
|
374
|
+
|
|
375
|
+
### Official API -- IM (17 tools)
|
|
376
|
+
|
|
377
|
+
| Tool | Description |
|
|
378
|
+
|------|-------------|
|
|
379
|
+
| `list_chats` | List all chats the bot has joined |
|
|
380
|
+
| `read_messages` | Read message history (accepts chat name, oc_xxx, or numeric ID) |
|
|
381
|
+
| `send_message_as_bot` | Send message as bot to any chat |
|
|
382
|
+
| `reply_message` | Reply to a specific message (as bot) |
|
|
383
|
+
| `forward_message` | Forward a message to another chat |
|
|
384
|
+
| `delete_message` | Recall/delete a bot message |
|
|
385
|
+
| `update_message` | Edit a sent bot message |
|
|
386
|
+
| `add_reaction` | Add emoji reaction to a message |
|
|
387
|
+
| `delete_reaction` | Remove emoji reaction |
|
|
388
|
+
| `pin_message` | Pin a message in chat |
|
|
389
|
+
| `unpin_message` | Unpin a message |
|
|
390
|
+
| `create_group` | Create a new group chat |
|
|
391
|
+
| `update_group` | Update group name/description |
|
|
392
|
+
| `list_members` | List group members |
|
|
393
|
+
| `add_members` | Add users to a group |
|
|
394
|
+
| `remove_members` | Remove users from a group |
|
|
395
|
+
| `upload_image` / `upload_file` | Upload image/file, returns key for sending |
|
|
396
|
+
| `download_message_resource` | v1.3.7 (C2.4): download a message-attached image or file. Args: `message_id`, `key`, `kind=image|file`, `save_path?`. **Required save_path when bytes > 2 MiB** (Anthropic 5 MB inline cap). Replaces v1.3.6 download_image (message mode) + download_file. |
|
|
397
|
+
| `download_doc_image` | v1.3.7 (C2.4): download an image embedded in a docx (image_token + optional doc_token). Same 2 MiB cap. Replaces v1.3.6 download_image (docx mode). |
|
|
398
|
+
|
|
399
|
+
### Wiki, OKR, and Calendar (v1.3.4)
|
|
400
|
+
|
|
401
|
+
| Tool | Description |
|
|
402
|
+
|------|-------------|
|
|
403
|
+
| `get_wiki_node` | Resolve a Wiki node token to its underlying obj_type + obj_token + space_id |
|
|
404
|
+
| `list_user_okrs` | List a user's OKRs (requires open_id; filter by period_ids) |
|
|
405
|
+
| `get_okrs` | Batch-fetch full OKR details (objectives, key results, progress, alignments) |
|
|
406
|
+
| `list_okr_periods` | List OKR periods (quarters / years) |
|
|
407
|
+
| `list_calendars` | List the current user's calendars (primary + shared + subscribed) |
|
|
408
|
+
| `list_calendar_events` | List events in a calendar within a time range |
|
|
409
|
+
| `get_calendar_event` | Full event details (attendees, location, meeting link, attachments) |
|
|
410
|
+
|
|
411
|
+
All docx / bitable tools' `document_id` / `app_token` parameter also accepts a Wiki node token or a full Feishu URL — the plugin resolves it transparently.
|
|
412
|
+
|
|
413
|
+
### Official API -- Documents (5 tools)
|
|
414
|
+
|
|
415
|
+
| Tool | Description |
|
|
416
|
+
|------|-------------|
|
|
417
|
+
| `search_docs` | Search documents by keyword |
|
|
418
|
+
| `read_doc` | Read raw text content |
|
|
419
|
+
| `get_doc_blocks` | Get structured block tree |
|
|
420
|
+
| `create_doc` | Create a new document |
|
|
421
|
+
| `manage_doc_block` | Insert / update / delete blocks (`action=create|update|delete`). Supports generic `children`, image (`image_path`/`image_token`), and file (`file_path`/`file_token`) shortcuts. v1.3.7 consolidates the v1.3.6 trio create_doc_block / update_doc_block / delete_doc_blocks. |
|
|
422
|
+
|
|
423
|
+
### Official API -- Bitable (6 tools, v1.3.7 consolidation)
|
|
424
|
+
|
|
425
|
+
| Tool | Actions | Description |
|
|
426
|
+
|------|---------|-------------|
|
|
427
|
+
| `manage_bitable_app` | create / copy / get_meta | App-level operations (v1.3.7 consolidates create_bitable / copy_bitable / get_bitable_meta) |
|
|
428
|
+
| `manage_bitable_table` | list / create / update / delete | Table CRUD (rename via update) |
|
|
429
|
+
| `manage_bitable_field` | list / create / update / delete | Field (column) management. `type` required for both create AND update. |
|
|
430
|
+
| `manage_bitable_view` | list / create / delete | Views (grid, kanban, gallery, form, gantt, calendar) |
|
|
431
|
+
| `manage_bitable_record` | search / get / create / update / delete | Record CRUD. create/update/delete accept arrays — single record or up to 500/call. |
|
|
432
|
+
| `upload_bitable_attachment` | — | Upload a file into a Bitable Attachment-type field. Returns `file_token` to write into the field as `[{file_token}]`. v1.3.6 |
|
|
433
|
+
|
|
434
|
+
### Official API -- Calendar (8 tools, write tools v1.3.7)
|
|
435
|
+
|
|
436
|
+
| Tool | Description |
|
|
437
|
+
|------|-------------|
|
|
438
|
+
| `list_calendars` | List accessible calendars |
|
|
439
|
+
| `list_calendar_events` | List events in a calendar |
|
|
440
|
+
| `get_calendar_event` | Full event details |
|
|
441
|
+
| `create_calendar_event` | Create an event (v1.3.7). Requires `calendar:calendar.event:write`. |
|
|
442
|
+
| `update_calendar_event` | Patch event fields (v1.3.7) |
|
|
443
|
+
| `delete_calendar_event` | Delete an event, optionally dissolve its meeting chat (v1.3.7) |
|
|
444
|
+
| `respond_calendar_event` | RSVP as accept / decline / tentative (v1.3.7) |
|
|
445
|
+
| `get_freebusy` | Freebusy lookup for `user_ids` in a time range (v1.3.7) |
|
|
446
|
+
|
|
447
|
+
### Official API -- Tasks v2 (7 tools, v1.3.7 new domain)
|
|
448
|
+
|
|
449
|
+
Identifier is `task_guid` (not v1's numeric `task_id`). Requires `task:task` scope.
|
|
450
|
+
|
|
451
|
+
| Tool | Description |
|
|
452
|
+
|------|-------------|
|
|
453
|
+
| `list_tasks` | List the current user's tasks (filter by completed / type) |
|
|
454
|
+
| `get_task` | Full task detail |
|
|
455
|
+
| `create_task` | Create a task (summary required; due/members optional) |
|
|
456
|
+
| `update_task` | Patch fields. **`update_fields` is required** — Feishu only updates the listed keys. |
|
|
457
|
+
| `complete_task` | Mark complete (or uncomplete with `completed=false`) |
|
|
458
|
+
| `delete_task` | Permanent delete |
|
|
459
|
+
| `manage_task_members` | `action=add|remove`, members `[{id, role:"assignee"|"follower"}]` |
|
|
460
|
+
|
|
461
|
+
### Official API -- Drive (4 tools)
|
|
462
|
+
|
|
463
|
+
| Tool | Description |
|
|
464
|
+
|------|-------------|
|
|
465
|
+
| `list_files` | List files in a folder |
|
|
466
|
+
| `create_folder` | Create a new folder |
|
|
467
|
+
| `manage_drive_file` | Copy / move / delete a Drive file (`action=copy|move|delete`, `type` required). v1.3.7 consolidates v1.3.6 copy_file / move_file / delete_file. |
|
|
468
|
+
| `upload_drive_file` | Upload a local file into a Drive folder (`drive/v1/files/upload_all`). Optional `wiki_space_id` attaches the upload as a Wiki node atomically. v1.3.6 |
|
|
469
|
+
|
|
470
|
+
### Official API -- Wiki (8 tools)
|
|
471
|
+
|
|
472
|
+
| Tool | Description |
|
|
473
|
+
|------|-------------|
|
|
474
|
+
| `list_wiki_spaces` / `search_wiki` / `list_wiki_nodes` / `get_wiki_node` | Wiki spaces, search, browse + resolve a wiki node to underlying obj_token |
|
|
475
|
+
| `create_wiki_node` | Create a new wiki node (doc/sheet/bitable/mindnote/file/docx/slides) inside a space |
|
|
476
|
+
| `update_wiki_node` | Rename a wiki node (title only — content edits via docx/bitable tools) |
|
|
477
|
+
| `move_wiki_node` | Move a wiki node to a different parent or different space |
|
|
478
|
+
| `copy_wiki_node` | Deep-copy a wiki node to a different location (optionally to a different space) |
|
|
479
|
+
|
|
480
|
+
### Plugin -- Profiles (3 tools, v1.3.6 + v1.3.8)
|
|
481
|
+
|
|
482
|
+
| Tool | Description |
|
|
483
|
+
|------|-------------|
|
|
484
|
+
| `list_profiles` | List available identity profiles (default + extras from `LARK_PROFILES_JSON`) and the active one |
|
|
485
|
+
| `switch_profile` | Hot-swap active profile; cached client instances rebuild against new credentials |
|
|
486
|
+
| `manage_profile_hints` | Inspect/set/clear the resourceKey → profile cache used by the v1.3.8 auto-switch middleware |
|
|
487
|
+
|
|
488
|
+
### Multi-profile auto-switch (v1.3.8)
|
|
489
|
+
|
|
490
|
+
When `~/.feishu-user-plugin/credentials.json` has more than one profile, the plugin auto-switches between them on **read** paths when the active profile gets a permission-denied error. The winning profile is cached per resource so subsequent calls go straight to the right account.
|
|
491
|
+
|
|
492
|
+
**Whitelist** -- only `read_*`, `list_*`, `get_*`, `search_*`, `download_*` (and the read-action variants of `manage_bitable_*`) get auto-retry. Writes never auto-switch -- they fail loud so you don't accidentally create resources under the wrong account.
|
|
493
|
+
|
|
494
|
+
**Triggers** -- error codes 91403, 1254301, 1254000, 99991672, HTTP 403, plus message patterns `access_denied / permission_denied / docx_no_permission / no permission / forbidden`.
|
|
495
|
+
|
|
496
|
+
**Per-call override** -- pass `via_profile: "<name>"` in any tool call to pin to that profile (no auto-switch). Pass `via_profile: "auto"` to opt **into** auto-switch on a write call (escape hatch -- be careful).
|
|
497
|
+
|
|
498
|
+
**Cache management** -- `manage_profile_hints(action="list" | "set" | "clear", resource_key?, profile?)` lets you inspect or edit the cache.
|
|
499
|
+
|
|
500
|
+
Single-profile users (the vast majority): zero behaviour change -- the router short-circuits and `manage_profile_hints` is a no-op.
|
|
501
|
+
|
|
502
|
+
## Claude Code Slash Commands (9 skills)
|
|
503
|
+
|
|
504
|
+
This plugin includes 9 built-in skills in `skills/feishu-user-plugin/`:
|
|
505
|
+
|
|
506
|
+
| Skill | Usage | Description |
|
|
507
|
+
|-------|-------|-------------|
|
|
508
|
+
| `/send` | `/send Alice: meeting at 3pm` | Send message as yourself |
|
|
509
|
+
| `/reply` | `/reply engineering-chat` | Read recent messages and reply |
|
|
510
|
+
| `/digest` | `/digest engineering-chat 7` | Summarize recent chat messages |
|
|
511
|
+
| `/search` | `/search engineering` | Search contacts and groups |
|
|
512
|
+
| `/doc` | `/doc search MCP` | Search, read, or create documents |
|
|
513
|
+
| `/table` | `/table query appXxx` | Query or create Bitable records |
|
|
514
|
+
| `/wiki` | `/wiki search protocol` | Search and browse wiki |
|
|
515
|
+
| `/drive` | `/drive list folderToken` | List files or create folders in Drive |
|
|
516
|
+
| `/status` | `/status` | Check login and auth status |
|
|
517
|
+
|
|
518
|
+
Skills are automatically available when the plugin is installed.
|
|
519
|
+
|
|
520
|
+
## Architecture
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
Cookie + Proto ┌──────────────────────────────────────┐
|
|
524
|
+
────────────────── >│ internal-api-lark-api.feishu.cn │
|
|
525
|
+
┌──────────────┐ │ /im/gateway/ (Protobuf over HTTP) │
|
|
526
|
+
│ MCP Client │ └──────────────────────────────────────┘
|
|
527
|
+
│ (Claude, │ App Token (REST) ┌──────────────────────────────────────┐
|
|
528
|
+
│ Cursor, │ ────────────────->│ open.feishu.cn/open-apis/ │
|
|
529
|
+
│ VS Code) │ │ (Official REST API) │
|
|
530
|
+
│ │ └──────────────────────────────────────┘
|
|
531
|
+
│ │ User OAuth (REST)┌──────────────────────────────────────┐
|
|
532
|
+
│ │ ────────────────->│ open.feishu.cn/open-apis/ │
|
|
533
|
+
└──────────────┘ │ (UAT -- P2P chat reading) │
|
|
534
|
+
└──────────────────────────────────────┘
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## Session & Token Lifecycle
|
|
538
|
+
|
|
539
|
+
| Auth Layer | Token | Lifetime | Refresh |
|
|
540
|
+
|------------|-------|----------|---------|
|
|
541
|
+
| Cookie | `sl_session` | 12h max-age | Auto-refreshed every 4h via heartbeat |
|
|
542
|
+
| App Token | `tenant_access_token` | 2h | Auto-managed by SDK |
|
|
543
|
+
| User OAuth | `user_access_token` | ~2h | Auto-refreshed via `refresh_token`, saved to MCP config |
|
|
544
|
+
|
|
545
|
+
When the cookie expires (after ~12-24h without heartbeat), re-login at feishu.cn and update `LARK_COOKIE`. Use `get_login_status` to check health proactively.
|
|
546
|
+
|
|
547
|
+
If UAT refresh fails with `invalid_grant`, re-run `npx feishu-user-plugin oauth` and restart Claude Code / Codex. v1.3.5+ also re-reads the persisted MCP config before refreshing, so duplicate MCP processes can adopt a token already rotated by another process instead of retrying a stale refresh token.
|
|
548
|
+
|
|
549
|
+
## Project Structure
|
|
550
|
+
|
|
551
|
+
```
|
|
552
|
+
feishu-user-plugin/
|
|
553
|
+
├── .claude-plugin/
|
|
554
|
+
│ └── plugin.json # Plugin metadata
|
|
555
|
+
├── skills/
|
|
556
|
+
│ └── feishu-user-plugin/
|
|
557
|
+
│ ├── SKILL.md # Main skill definition (trigger, tools, auth)
|
|
558
|
+
│ └── references/ # 8 skill reference docs + CLAUDE.md
|
|
559
|
+
├── src/
|
|
560
|
+
│ ├── index.js # MCP server entry point (78 tools)
|
|
561
|
+
│ ├── client.js # User identity client (Protobuf gateway)
|
|
562
|
+
│ ├── official.js # Official API client (REST, UAT)
|
|
563
|
+
│ ├── utils.js # ID generators, cookie parser
|
|
564
|
+
│ ├── oauth.js # OAuth flow for user_access_token
|
|
565
|
+
│ ├── test-send.js # Quick CLI test
|
|
566
|
+
│ └── test-all.js # Full test suite
|
|
567
|
+
├── proto/
|
|
568
|
+
│ └── lark.proto # Protobuf message definitions
|
|
569
|
+
├── .mcp.json.example # MCP server config template
|
|
570
|
+
├── server.json # MCP Registry manifest
|
|
571
|
+
├── .env.example # Configuration template
|
|
572
|
+
└── package.json
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## Limitations
|
|
576
|
+
|
|
577
|
+
- Cookie-based auth requires periodic refresh (auto-heartbeat extends to ~12h; manual re-login needed after that)
|
|
578
|
+
- Depends on Feishu's internal Protobuf protocol -- may break if Feishu updates their web client
|
|
579
|
+
- Image/file/audio sending requires pre-uploaded keys (upload via Official API or external bridge)
|
|
580
|
+
- No real-time message receiving (WebSocket push not yet implemented)
|
|
581
|
+
- May violate Feishu's Terms of Service -- use at your own risk
|
|
582
|
+
|
|
583
|
+
## Contributing
|
|
584
|
+
|
|
585
|
+
Issues and PRs welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, code style, and submission guidelines.
|
|
586
|
+
|
|
587
|
+
If Feishu updates their protocol and something breaks, please [open an issue](https://github.com/EthanQC/feishu-user-plugin/issues/new?template=bug_report.md) with the error details.
|
|
588
|
+
|
|
589
|
+
### Automated sync hooks
|
|
590
|
+
|
|
591
|
+
This repo uses husky to enforce several invariants on every commit:
|
|
592
|
+
|
|
593
|
+
- **CLAUDE.md sync** — staging `CLAUDE.md` automatically regenerates `AGENTS.md` (identical body, different first line) and `skills/feishu-user-plugin/references/CLAUDE.md` (verbatim copy). Both are re-staged in the same commit.
|
|
594
|
+
- **Version triangle** — if `package.json`, `.claude-plugin/plugin.json`, or `skills/feishu-user-plugin/SKILL.md` are staged, all three `version` fields must agree or the commit is rejected.
|
|
595
|
+
- **Tool-count badge** — if `src/server.js` or any file under `src/tools/` is staged, the `N tools` badge in `README.md` must match the actual `TOOLS.length` exported by `src/server.js`.
|
|
596
|
+
- **Smoke test** — any change under `src/` triggers `npm run smoke` to catch schema regressions before commit.
|
|
597
|
+
|
|
598
|
+
CI (`.github/workflows/validate.yml`) runs the same checks on every PR to `main`, so bypassing the local hook still gets caught.
|
|
599
|
+
|
|
600
|
+
On the maintainer's machine, a post-merge hook (`scripts/sync-team-skills.sh`) auto-opens a sync PR in the `~/team-skills` repo after every merge to main. The hook silently skips if `~/team-skills` is absent.
|
|
601
|
+
|
|
602
|
+
## License
|
|
603
|
+
|
|
604
|
+
[MIT](LICENSE)
|
|
605
|
+
|
|
606
|
+
## Acknowledgments
|
|
607
|
+
|
|
608
|
+
- [cv-cat/LarkAgentX](https://github.com/cv-cat/LarkAgentX) -- Early Feishu web protocol research (Python)
|
|
609
|
+
- [cv-cat/OpenFeiShuApis](https://github.com/cv-cat/OpenFeiShuApis) -- Underlying API research
|
|
610
|
+
- [Model Context Protocol](https://modelcontextprotocol.io) -- The MCP standard
|