feishu-user-plugin 1.0.0 → 1.0.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/.env.example CHANGED
@@ -1,6 +1,6 @@
1
1
  # === User Identity (reverse-engineered protocol) ===
2
2
  # Required for: send_as_user, send_to_user, send_to_group, search_contacts, etc.
3
- # How to get: Login to feishu.cn → Playwright context.cookies() or DevTools
3
+ # How to get: Login to feishu.cn → Playwright context.cookies() or DevTools Network tab
4
4
  LARK_COOKIE=
5
5
 
6
6
  # === Official API (Feishu Open Platform) ===
@@ -9,9 +9,9 @@ LARK_COOKIE=
9
9
  LARK_APP_ID=
10
10
  LARK_APP_SECRET=
11
11
 
12
- # === User OAuth UAT (optional, for P2P chat reading) ===
12
+ # === User OAuth UAT (required for P2P chat reading) ===
13
13
  # Required for: read_p2p_messages, list_user_chats
14
- # How to get: Run "node src/oauth.js" after configuring App ID/Secret above
14
+ # How to get: Run "node src/oauth.js" after configuring Cookie + App ID/Secret above
15
15
  # These are auto-populated by the OAuth flow and auto-refreshed at runtime
16
16
  LARK_USER_ACCESS_TOKEN=
17
17
  LARK_USER_REFRESH_TOKEN=
package/.mcp.json.example CHANGED
@@ -6,7 +6,9 @@
6
6
  "env": {
7
7
  "LARK_COOKIE": "YOUR_FEISHU_COOKIE_HERE",
8
8
  "LARK_APP_ID": "YOUR_APP_ID_HERE",
9
- "LARK_APP_SECRET": "YOUR_APP_SECRET_HERE"
9
+ "LARK_APP_SECRET": "YOUR_APP_SECRET_HERE",
10
+ "LARK_USER_ACCESS_TOKEN": "YOUR_UAT_HERE",
11
+ "LARK_USER_REFRESH_TOKEN": "YOUR_REFRESH_TOKEN_HERE"
10
12
  }
11
13
  }
12
14
  }
package/README.md CHANGED
@@ -15,7 +15,7 @@ The only MCP server that lets you send messages as your **personal identity** (n
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
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). Each is independent; configure only what you need.
18
+ - **3 auth layers** -- Cookie-based user identity, app credentials (Official API), and OAuth UAT (P2P reading). All three are needed for full functionality.
19
19
  - **8 slash commands** for Claude Code -- `/send`, `/reply`, `/search`, `/digest`, `/doc`, `/table`, `/wiki`, `/status`
20
20
  - **Auto session management** -- Cookie heartbeat every 4h, UAT auto-refresh with token rotation.
21
21
  - **Chat name resolution** -- Pass a group name instead of `oc_xxx` ID; it resolves automatically.
@@ -110,11 +110,9 @@ Add your bot to the group chats where you want it to read messages. The bot can
110
110
  | `LARK_APP_ID` | Official API tools | App ID from Feishu Open Platform. Needed for `read_messages`, docs, tables, wiki, drive. |
111
111
  | `LARK_APP_SECRET` | Official API tools | App Secret from Feishu Open Platform. Used together with `LARK_APP_ID`. |
112
112
  | `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`. |
113
+ | `LARK_USER_REFRESH_TOKEN` | UAT auto-refresh | Refresh token for automatic UAT renewal. Obtained together with UAT via OAuth flow. |
113
114
 
114
- Each auth layer is independent. You can configure:
115
- - **Cookie only** -- for sending messages as yourself
116
- - **App credentials only** -- for reading docs, tables, wiki, group chats
117
- - **All three** -- for the full feature set
115
+ All five variables are required for full functionality. Configure all of them during setup.
118
116
 
119
117
  ## How to Get Your Cookie
120
118
 
@@ -147,9 +145,9 @@ Claude Code will automatically:
147
145
 
148
146
  > The server automatically refreshes the session via heartbeat every 4 hours. The `sl_session` cookie has a 12-hour max-age.
149
147
 
150
- ## How to Set Up P2P Chat Reading (OAuth)
148
+ ## Set Up OAuth (Required for P2P Chat Reading)
151
149
 
152
- To read direct message history with `read_p2p_messages` and `list_user_chats`:
150
+ To enable `read_p2p_messages` and `list_user_chats`:
153
151
 
154
152
  1. Your Feishu app must be a **Custom App** (自建应用), NOT marketplace/third-party
155
153
  2. Add scopes: `im:message`, `im:message:readonly`, `im:chat:readonly`
@@ -166,7 +164,7 @@ cd $(npm root -g)/feishu-user-plugin && node src/oauth.js
166
164
  # Or clone the repo just for the OAuth step, then use npx for daily use
167
165
  ```
168
166
 
169
- A browser window will open for OAuth consent. The token is saved to `.env` automatically and auto-refreshes at runtime. Add the resulting `LARK_USER_ACCESS_TOKEN` to your `.mcp.json` env.
167
+ 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.
170
168
 
171
169
  ## MCP Client Configuration
172
170
 
@@ -185,7 +183,9 @@ Add to your project's `.mcp.json` (or `~/.claude/.mcp.json` for global):
185
183
  "env": {
186
184
  "LARK_COOKIE": "your-cookie-string",
187
185
  "LARK_APP_ID": "cli_xxxxxxxxxxxx",
188
- "LARK_APP_SECRET": "your-app-secret"
186
+ "LARK_APP_SECRET": "your-app-secret",
187
+ "LARK_USER_ACCESS_TOKEN": "your-uat",
188
+ "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
189
189
  }
190
190
  }
191
191
  }
@@ -203,7 +203,9 @@ Add to your project's `.mcp.json` (or `~/.claude/.mcp.json` for global):
203
203
  "env": {
204
204
  "LARK_COOKIE": "your-cookie-string",
205
205
  "LARK_APP_ID": "cli_xxxxxxxxxxxx",
206
- "LARK_APP_SECRET": "your-app-secret"
206
+ "LARK_APP_SECRET": "your-app-secret",
207
+ "LARK_USER_ACCESS_TOKEN": "your-uat",
208
+ "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
207
209
  }
208
210
  }
209
211
  }
@@ -228,7 +230,9 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
228
230
  "env": {
229
231
  "LARK_COOKIE": "your-cookie-string",
230
232
  "LARK_APP_ID": "cli_xxxxxxxxxxxx",
231
- "LARK_APP_SECRET": "your-app-secret"
233
+ "LARK_APP_SECRET": "your-app-secret",
234
+ "LARK_USER_ACCESS_TOKEN": "your-uat",
235
+ "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
232
236
  }
233
237
  }
234
238
  }
@@ -248,7 +252,9 @@ Add to `.cursor/mcp.json` in your project:
248
252
  "env": {
249
253
  "LARK_COOKIE": "your-cookie-string",
250
254
  "LARK_APP_ID": "cli_xxxxxxxxxxxx",
251
- "LARK_APP_SECRET": "your-app-secret"
255
+ "LARK_APP_SECRET": "your-app-secret",
256
+ "LARK_USER_ACCESS_TOKEN": "your-uat",
257
+ "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
252
258
  }
253
259
  }
254
260
  }
@@ -269,7 +275,9 @@ Add to `.vscode/mcp.json` in your project:
269
275
  "env": {
270
276
  "LARK_COOKIE": "your-cookie-string",
271
277
  "LARK_APP_ID": "cli_xxxxxxxxxxxx",
272
- "LARK_APP_SECRET": "your-app-secret"
278
+ "LARK_APP_SECRET": "your-app-secret",
279
+ "LARK_USER_ACCESS_TOKEN": "your-uat",
280
+ "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
273
281
  }
274
282
  }
275
283
  }
@@ -289,7 +297,9 @@ Add to `~/.codeium/windsurf/mcp_config.json`:
289
297
  "env": {
290
298
  "LARK_COOKIE": "your-cookie-string",
291
299
  "LARK_APP_ID": "cli_xxxxxxxxxxxx",
292
- "LARK_APP_SECRET": "your-app-secret"
300
+ "LARK_APP_SECRET": "your-app-secret",
301
+ "LARK_USER_ACCESS_TOKEN": "your-uat",
302
+ "LARK_USER_REFRESH_TOKEN": "your-refresh-token"
293
303
  }
294
304
  }
295
305
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feishu-user-plugin",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "All-in-one Feishu plugin for Claude Code — send messages as yourself, read chats, manage docs/tables/wiki. 33 tools + 8 skills, 3 auth layers.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -61,7 +61,8 @@ Check cookie / app credentials / UAT — all three auth layers at once.
61
61
  | LARK_COOKIE | **You** | Send messages as yourself | Yes (for messaging) |
62
62
  | LARK_APP_ID | **You** (create a Feishu app) | Official API access | Yes |
63
63
  | LARK_APP_SECRET | **You** (from your Feishu app) | Official API access | Yes |
64
- | LARK_USER_ACCESS_TOKEN | **You** (OAuth flow, optional) | Read P2P chat history | Optional |
64
+ | LARK_USER_ACCESS_TOKEN | **You** (OAuth flow) | Read P2P chat history | Yes (for P2P reading) |
65
+ | LARK_USER_REFRESH_TOKEN | **You** (OAuth flow) | UAT auto-refresh | Yes (for P2P reading) |
65
66
 
66
67
  ### Getting Your Cookie (Automated via Playwright)
67
68
 
@@ -4,7 +4,7 @@
4
4
  All-in-one Feishu plugin for Claude Code with three auth layers:
5
5
  - **User Identity** (cookie auth): Send messages (text, image, file, post, sticker, audio) as yourself
6
6
  - **Official API** (app credentials): Read group messages, docs, tables, wiki, drive, contacts
7
- - **User OAuth UAT** (user_access_token): Read P2P chat history
7
+ - **User OAuth UAT** (user_access_token): Read P2P chat history, list all user's chats
8
8
 
9
9
  ## Tool Categories
10
10
 
@@ -26,7 +26,7 @@ All-in-one Feishu plugin for Claude Code with three auth layers:
26
26
  - `get_login_status` — Check cookie, app, and UAT status
27
27
 
28
28
  ### User OAuth UAT Tools (P2P chat reading)
29
- - `read_p2p_messages` — Read P2P (direct message) chat history. Requires OAuth setup.
29
+ - `read_p2p_messages` — Read P2P (direct message) chat history. chat_id accepts both numeric IDs (from create_p2p_chat) and oc_xxx format.
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
31
 
32
32
  ### Official API Tools (app credentials)
@@ -43,7 +43,7 @@ All-in-one Feishu plugin for Claude Code with three auth layers:
43
43
  - Send text as yourself → `send_to_user` or `send_to_group`
44
44
  - Send rich content → `send_post_as_user` (formatted text), `send_image_as_user` (images)
45
45
  - Read group chat history → `read_messages` with chat name or oc_ ID
46
- - Read P2P chat history → `search_contacts` → `create_p2p_chat` → `read_p2p_messages` (requires OAuth UAT)
46
+ - Read P2P chat history → `search_contacts` → `create_p2p_chat` → `read_p2p_messages`
47
47
  - Reply as user in thread → `send_as_user` with root_id
48
48
  - Reply as bot → `reply_message` (official API, text messages only)
49
49
  - Diagnose issues → `get_login_status` first
@@ -51,44 +51,82 @@ All-in-one Feishu plugin for Claude Code with three auth layers:
51
51
  ## Auth & Session
52
52
  - **LARK_COOKIE**: Required for user identity tools. Session auto-refreshed every 4h via heartbeat.
53
53
  - **LARK_APP_ID + LARK_APP_SECRET**: Required for official API tools.
54
- - **LARK_USER_ACCESS_TOKEN**: Required for P2P reading. Obtained via OAuth flow. Auto-refreshed via refresh_token.
54
+ - **LARK_USER_ACCESS_TOKEN + LARK_USER_REFRESH_TOKEN**: Required for P2P reading. Auto-refreshed via refresh_token.
55
55
 
56
56
  ## Automated Cookie Setup via Playwright
57
57
 
58
- When LARK_COOKIE is missing or expired, automatically obtain it using Playwright MCP.
59
- Do NOT ask the user to manually copy cookies from DevTools.
58
+ When LARK_COOKIE is missing or expired, obtain it using Playwright MCP.
60
59
 
61
60
  ### Prerequisites
62
- Playwright MCP must be available. If not, tell the user:
61
+ Playwright MCP must be available. If not:
63
62
  > Run: `npx @anthropic-ai/claude-code mcp add playwright -- npx @anthropic-ai/mcp-server-playwright`
64
63
  > Then restart Claude Code.
65
64
 
66
- ### Automated Flow
67
- 1. `browser_navigate` → `https://www.feishu.cn/messenger/`
68
- 2. `browser_take_screenshot` show QR code to user, ask them to scan with Feishu app
69
- 3. Poll `browser_snapshot` every 3-5s until messenger page loads
70
- 4. `browser_run_code`:
71
- ```js
72
- const cookies = await context.cookies('https://www.feishu.cn');
73
- const feishuCookies = cookies.filter(c => c.domain.endsWith('feishu.cn'));
74
- const cookieStr = feishuCookies.map(c => c.name + '=' + c.value).join('; ');
75
- ```
76
- 5. Read user's `.mcp.json`, update `LARK_COOKIE` value
77
- 6. Tell user to restart Claude Code
78
-
79
- ### Cookie Refresh
80
- When cookie auth fails, offer to re-run the Playwright flow. Heartbeat auto-refreshes every 4h.
65
+ ### Automated Flow — FOLLOW EXACTLY
66
+
67
+ **Step 1: Clear cookies first** (Playwright uses persistent browser profile that may have a DIFFERENT account cached):
68
+ ```
69
+ browser_run_code: await context.clearCookies();
70
+ browser_navigate: https://www.feishu.cn/messenger/
71
+ ```
72
+
73
+ **Step 2: Show QR code and wait for login**:
74
+ ```
75
+ browser_take_screenshot
76
+ ```
77
+ Poll `browser_snapshot` every 5s until URL changes from `/accounts/`.
78
+
79
+ **Step 3: Extract cookie TWO-STEP approach** (NEVER use browser_run_code output directly as cookie):
80
+
81
+ Step 3a via `browser_run_code`:
82
+ ```js
83
+ const cookies = await page.context().cookies('https://www.feishu.cn');
84
+ const str = cookies.map(c => c.name + '=' + c.value).join('; ');
85
+ await page.evaluate(s => { window.__COOKIE__ = s; }, str);
86
+ return 'Stored ' + cookies.length + ' cookies, length=' + str.length;
87
+ ```
88
+
89
+ Step 3b via `browser_evaluate`:
90
+ ```js
91
+ window.__COOKIE__
92
+ ```
93
+
94
+ **Step 4: Validate** — Must be pure ASCII, contain `session=` and `sl_session=`, length 500-5000. If >10000, it's contaminated.
95
+
96
+ **Step 5: Write config** using exact format (NO `"type": "stdio"`):
97
+ ```json
98
+ {
99
+ "feishu-user-plugin": {
100
+ "command": "npx",
101
+ "args": ["-y", "feishu-user-plugin"],
102
+ "env": {
103
+ "LARK_COOKIE": "<cookie>",
104
+ "LARK_APP_ID": "<id>",
105
+ "LARK_APP_SECRET": "<secret>",
106
+ "LARK_USER_ACCESS_TOKEN": "<uat>",
107
+ "LARK_USER_REFRESH_TOKEN": "<refresh>"
108
+ }
109
+ }
110
+ }
111
+ ```
81
112
 
82
113
  ## Troubleshooting
83
114
 
84
115
  ### If cookie authentication fails
85
- - **Best method**: Playwright `context.cookies()` (includes HttpOnly)
86
- - **Manual fallback**: Network tab → Disable cache → reload → first request → Request Headers → Cookie → Copy value
116
+ - **Best method**: Playwright two-step extraction (see above)
117
+ - **Manual fallback**: Network tab → first request → Request Headers → Cookie → Copy value
87
118
  - Do NOT use `document.cookie` or Application → Cookies (misses HttpOnly cookies)
88
119
 
120
+ ### If Playwright logs into the wrong account
121
+ - Always `context.clearCookies()` BEFORE navigating to feishu.cn
122
+ - Verify account by checking URL domain after login
123
+
124
+ ### If UAT refresh fails with "invalid_grant" (error 28003/20003/20005)
125
+ - Refresh token expired — re-run `node src/oauth.js` (needs LARK_APP_ID + LARK_APP_SECRET in `.env`)
126
+ - Copy new UAT + refresh token from `.env` to MCP config, then restart Claude Code
127
+
89
128
  ### If list_user_chats doesn't return P2P chats
90
- - This is expected the Feishu API only returns group chats at this endpoint.
91
- - Use the P2P flow: `search_contacts` → `create_p2p_chat` → `read_p2p_messages`.
129
+ - Expected — API only returns groups. Use: `search_contacts` `create_p2p_chat` → `read_p2p_messages`.
92
130
 
93
131
  ### If reply_message fails with error 230054
94
- - "This operation is not supported for this message type" — only text messages can be replied to.
132
+ - Only text messages can be replied to via this API.
package/src/index.js CHANGED
@@ -255,7 +255,7 @@ const TOOLS = [
255
255
  inputSchema: {
256
256
  type: 'object',
257
257
  properties: {
258
- chat_id: { type: 'string', description: 'Chat ID (oc_xxx). Use list_user_chats to find P2P chat IDs.' },
258
+ chat_id: { type: 'string', description: 'Chat ID (numeric from create_p2p_chat, or oc_xxx from list_user_chats). Both formats work.' },
259
259
  page_size: { type: 'number', description: 'Messages to fetch (default 20, max 50)' },
260
260
  start_time: { type: 'string', description: 'Start timestamp in seconds (optional)' },
261
261
  end_time: { type: 'string', description: 'End timestamp in seconds (optional)' },
@@ -691,7 +691,16 @@ async function handleTool(name, args) {
691
691
  async function main() {
692
692
  const transport = new StdioServerTransport();
693
693
  await server.connect(transport);
694
- console.error('[feishu-user-plugin] MCP Server v1.0.0 — %d tools available', TOOLS.length);
694
+
695
+ // Startup diagnostics
696
+ const hasCookie = !!process.env.LARK_COOKIE;
697
+ const hasApp = !!(process.env.LARK_APP_ID && process.env.LARK_APP_SECRET);
698
+ const hasUAT = !!process.env.LARK_USER_ACCESS_TOKEN;
699
+ console.error(`[feishu-user-plugin] MCP Server v1.0.1 — ${TOOLS.length} tools`);
700
+ console.error(`[feishu-user-plugin] Auth: Cookie=${hasCookie ? 'YES' : 'NO'} App=${hasApp ? 'YES' : 'NO'} UAT=${hasUAT ? 'YES' : 'NO'}`);
701
+ if (!hasCookie) console.error('[feishu-user-plugin] WARNING: LARK_COOKIE not set — user identity tools (send_to_user, etc.) will fail');
702
+ if (!hasApp) console.error('[feishu-user-plugin] WARNING: LARK_APP_ID/SECRET not set — official API tools (read_messages, docs, etc.) will fail');
703
+ if (!hasUAT) console.error('[feishu-user-plugin] WARNING: LARK_USER_ACCESS_TOKEN not set — P2P chat reading (read_p2p_messages) will fail');
695
704
  }
696
705
 
697
706
  main().catch(console.error);