@yoreland/lark-cli-mcp 1.0.1 → 1.1.0

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
@@ -56,17 +56,41 @@ npx -y @yoreland/lark-cli-mcp -- <args> # passthrough to bundled lark-cli
56
56
 
57
57
  ---
58
58
 
59
- ## The 7 tools
60
-
61
- | Tool | What it does | lark-cli command |
62
- | ------------------------ | ----------------------- | -------------------------------------- |
63
- | `feishu_send_message` | Send a message | `im +messages-send --as user` |
64
- | `feishu_get_messages` | Read recent messages | `im +chat-messages-list --as user` |
65
- | `feishu_reply_message` | Reply (thread optional) | `im +messages-reply --as user` |
66
- | `feishu_search_messages` | Search messages | `im +messages-search --as user` |
67
- | `feishu_list_chats` | Find group chats | `im +chat-search --as user` |
68
- | `feishu_search_user` | Find a user (→ open_id) | `contact +search-user --as user` |
69
- | `feishu_get_thread` | View a thread | `im +threads-messages-list --as user` |
59
+ ## The tools
60
+
61
+ ### Messaging (IM)
62
+
63
+ | Tool | What it does |
64
+ | ------------------------ | ----------------------- |
65
+ | `feishu_send_message` | Send a message |
66
+ | `feishu_get_messages` | Read recent messages |
67
+ | `feishu_reply_message` | Reply (thread optional) |
68
+ | `feishu_search_messages` | Search messages |
69
+ | `feishu_list_chats` | Find group chats |
70
+ | `feishu_search_user` | Find a user (→ open_id) |
71
+ | `feishu_get_thread` | View a thread |
72
+
73
+ ### Docs / Wiki / Drive
74
+
75
+ | Tool | What it does |
76
+ | ----------------------- | ---------------------------------- |
77
+ | `feishu_search_docs` | Search docs / wiki / sheets |
78
+ | `feishu_doc_fetch` | Read a document |
79
+ | `feishu_doc_create` | Create a document (markdown) |
80
+ | `feishu_doc_update` | Update a document |
81
+ | `feishu_drive_search` | Search Drive files (type filters) |
82
+ | `feishu_wiki_node_list` | List wiki nodes |
83
+ | `feishu_wiki_node_get` | Get a wiki node (accepts URL) |
84
+
85
+ ### Bitable (multi-dimensional tables)
86
+
87
+ | Tool | What it does |
88
+ | --------------------------- | ------------------------------------- |
89
+ | `feishu_base_table_list` | List tables in a base |
90
+ | `feishu_base_field_list` | List fields of a table |
91
+ | `feishu_base_record_list` | List records (filter/sort) |
92
+ | `feishu_base_record_search` | Search records |
93
+ | `feishu_base_record_upsert` | Create/update a record |
70
94
 
71
95
  ### Talk to it naturally
72
96
 
@@ -87,7 +111,7 @@ The workshop host creates **one** Feishu custom app and configures it so attende
87
111
 
88
112
  1. [Feishu Open Platform](https://open.feishu.cn) → create an internal custom app → note **App ID / App Secret**.
89
113
  2. Enable **User token scopes** matching the `im`, `contact`, `search` domains (message read/write, reply, chat read, user search, message search).
90
- 3. Distribute the App ID/Secret to attendees via `lark-cli config` (or a pre-bound config). The login step requests scopes via `--domain im,contact`.
114
+ 3. Distribute the App ID/Secret to attendees via `lark-cli config` (or a pre-bound config). The login step requests scopes via `--domain im,contact,docs,wiki,drive,base`.
91
115
 
92
116
  `auth` uses **OAuth Device Flow**, so no `redirect URL` / `localhost:3000` callback configuration is required.
93
117
 
@@ -97,7 +121,7 @@ The workshop host creates **one** Feishu custom app and configures it so attende
97
121
 
98
122
  **`missing required scope(s)`** — re-login with the needed domain:
99
123
  ```bash
100
- npx -y @yoreland/lark-cli-mcp auth --domain im,contact
124
+ npx -y @yoreland/lark-cli-mcp auth --domain im,contact,docs,wiki,drive,base
101
125
  ```
102
126
 
103
127
  **Client shows "No tools loaded"** — run `npx -y @yoreland/lark-cli-mcp doctor`; confirm Node ≥18 and that `auth status` is OK.
package/bin/cli.mjs CHANGED
@@ -19,10 +19,11 @@ import { fileURLToPath } from "node:url";
19
19
 
20
20
  const __dirname = dirname(fileURLToPath(import.meta.url));
21
21
 
22
- // Default scopes for a Feishu messaging workshop (user identity).
23
- // `im` domain covers send/read/reply/search; `contact` for user search.
22
+ // Default scopes (user identity) covering IM + docs/wiki/drive/bitable.
23
+ // `im` = messaging; `contact` = user lookup; `docs`/`wiki`/`drive` = cloud docs;
24
+ // `base` = Bitable (multi-dimensional tables).
24
25
  // NOTE: there is no `search` domain in lark-cli; message search lives under `im`.
25
- const DEFAULT_DOMAINS = "im,contact";
26
+ const DEFAULT_DOMAINS = "im,contact,docs,wiki,drive,base";
26
27
 
27
28
  function resolveLarkCli() {
28
29
  if (process.env.LARK_CLI_BIN && existsSync(process.env.LARK_CLI_BIN)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoreland/lark-cli-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server that wraps lark-cli to operate Feishu/Lark as your own user identity (send/read/reply/search messages). Designed for one-line npx launch.",
5
5
  "type": "module",
6
6
  "bin": {
package/server.mjs CHANGED
@@ -156,6 +156,192 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
156
156
  required: ["message_id"],
157
157
  },
158
158
  },
159
+ {
160
+ name: "feishu_search_docs",
161
+ description:
162
+ "搜索飞书云文档(文档/Wiki/表格),按关键词。返回标题、token、URL 等",
163
+ inputSchema: {
164
+ type: "object",
165
+ properties: {
166
+ query: { type: "string", description: "搜索关键词" },
167
+ count: { type: "number", description: "结果数量,默认 15,最大 20" },
168
+ },
169
+ required: ["query"],
170
+ },
171
+ },
172
+ {
173
+ name: "feishu_doc_fetch",
174
+ description: "读取一篇飞书文档的内容(传文档 URL 或 token)",
175
+ inputSchema: {
176
+ type: "object",
177
+ properties: {
178
+ doc: { type: "string", description: "文档 URL 或 token" },
179
+ },
180
+ required: ["doc"],
181
+ },
182
+ },
183
+ {
184
+ name: "feishu_doc_create",
185
+ description: "新建一篇飞书文档(Markdown 内容)",
186
+ inputSchema: {
187
+ type: "object",
188
+ properties: {
189
+ title: { type: "string", description: "文档标题" },
190
+ markdown: { type: "string", description: "Markdown 内容" },
191
+ folder_token: {
192
+ type: "string",
193
+ description: "目标文件夹 token(可选)",
194
+ },
195
+ },
196
+ required: ["title"],
197
+ },
198
+ },
199
+ {
200
+ name: "feishu_doc_update",
201
+ description: "更新一篇飞书文档内容",
202
+ inputSchema: {
203
+ type: "object",
204
+ properties: {
205
+ doc: { type: "string", description: "文档 URL 或 token" },
206
+ markdown: { type: "string", description: "新的 Markdown 内容" },
207
+ mode: {
208
+ type: "string",
209
+ description:
210
+ "更新模式:append | overwrite | replace_all | insert_before | insert_after(默认 append)",
211
+ },
212
+ new_title: { type: "string", description: "同时更新标题(可选)" },
213
+ },
214
+ required: ["doc", "markdown"],
215
+ },
216
+ },
217
+ {
218
+ name: "feishu_drive_search",
219
+ description:
220
+ "在云盘中搜索文件(doc/sheet/bitable/file 等),支持类型筛选",
221
+ inputSchema: {
222
+ type: "object",
223
+ properties: {
224
+ query: { type: "string", description: "搜索关键词(可空,纯按筛选浏览)" },
225
+ doc_types: {
226
+ type: "string",
227
+ description:
228
+ "类型逗号分隔:doc,sheet,bitable,mindnote,file,wiki,docx,folder,slides",
229
+ },
230
+ mine: { type: "boolean", description: "仅我拥有的文档" },
231
+ },
232
+ },
233
+ },
234
+ {
235
+ name: "feishu_wiki_node_list",
236
+ description: "列出某个 Wiki 空间或父节点下的节点",
237
+ inputSchema: {
238
+ type: "object",
239
+ properties: {
240
+ space_id: {
241
+ type: "string",
242
+ description: "Wiki 空间 ID(个人库用 my_library)",
243
+ },
244
+ parent_node_token: {
245
+ type: "string",
246
+ description: "父节点 token(可选,省略则列根节点)",
247
+ },
248
+ },
249
+ required: ["space_id"],
250
+ },
251
+ },
252
+ {
253
+ name: "feishu_wiki_node_get",
254
+ description: "读取一个 Wiki 节点详情(支持传 Lark URL / node_token / obj_token)",
255
+ inputSchema: {
256
+ type: "object",
257
+ properties: {
258
+ node_token: {
259
+ type: "string",
260
+ description: "wiki node_token / obj_token / Lark URL",
261
+ },
262
+ },
263
+ required: ["node_token"],
264
+ },
265
+ },
266
+ {
267
+ name: "feishu_base_table_list",
268
+ description: "列出某个多维表格(Bitable/Base)下的所有数据表",
269
+ inputSchema: {
270
+ type: "object",
271
+ properties: {
272
+ base_token: { type: "string", description: "多维表格 app_token / URL" },
273
+ },
274
+ required: ["base_token"],
275
+ },
276
+ },
277
+ {
278
+ name: "feishu_base_field_list",
279
+ description: "列出多维表格某个数据表的字段(列)",
280
+ inputSchema: {
281
+ type: "object",
282
+ properties: {
283
+ base_token: { type: "string", description: "多维表格 app_token / URL" },
284
+ table_id: { type: "string", description: "数据表 ID(tbl_xxx)或表名" },
285
+ },
286
+ required: ["base_token", "table_id"],
287
+ },
288
+ },
289
+ {
290
+ name: "feishu_base_record_list",
291
+ description: "列出多维表格某个数据表的记录(行),支持筛选/排序",
292
+ inputSchema: {
293
+ type: "object",
294
+ properties: {
295
+ base_token: { type: "string", description: "多维表格 app_token / URL" },
296
+ table_id: { type: "string", description: "数据表 ID 或表名" },
297
+ limit: { type: "number", description: "返回条数 1-200,默认 100" },
298
+ filter_json: {
299
+ type: "string",
300
+ description: "筛选条件 JSON(与视图 filter 同结构,可选)",
301
+ },
302
+ sort_json: {
303
+ type: "string",
304
+ description: '排序 JSON,如 [{"field":"Updated","desc":true}](可选)',
305
+ },
306
+ },
307
+ required: ["base_token", "table_id"],
308
+ },
309
+ },
310
+ {
311
+ name: "feishu_base_record_search",
312
+ description: "在多维表格数据表中检索记录",
313
+ inputSchema: {
314
+ type: "object",
315
+ properties: {
316
+ base_token: { type: "string", description: "多维表格 app_token / URL" },
317
+ table_id: { type: "string", description: "数据表 ID 或表名" },
318
+ limit: { type: "number", description: "返回条数 1-200,默认 10" },
319
+ },
320
+ required: ["base_token", "table_id"],
321
+ },
322
+ },
323
+ {
324
+ name: "feishu_base_record_upsert",
325
+ description:
326
+ "在多维表格数据表中创建或更新一条记录(给 record_id 则更新,否则创建)",
327
+ inputSchema: {
328
+ type: "object",
329
+ properties: {
330
+ base_token: { type: "string", description: "多维表格 app_token / URL" },
331
+ table_id: { type: "string", description: "数据表 ID 或表名" },
332
+ fields_json: {
333
+ type: "string",
334
+ description:
335
+ '字段值 JSON,如 {"Name":"Alice","Status":"Todo"}(不要包 fields)',
336
+ },
337
+ record_id: {
338
+ type: "string",
339
+ description: "记录 ID(传则更新该记录,不传则新建)",
340
+ },
341
+ },
342
+ required: ["base_token", "table_id", "fields_json"],
343
+ },
344
+ },
159
345
  ],
160
346
  }));
161
347
 
@@ -181,8 +367,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
181
367
  if (chat_id) cmd.push("--chat-id", chat_id);
182
368
  else cmd.push("--user-id", user_id);
183
369
  if (count) cmd.push("--page-size", String(count));
184
- if (start_time) cmd.push("--start-time", start_time);
185
- if (end_time) cmd.push("--end-time", end_time);
370
+ if (start_time) cmd.push("--start", start_time);
371
+ if (end_time) cmd.push("--end", end_time);
186
372
  return await run(cmd);
187
373
  }
188
374
  case "feishu_reply_message": {
@@ -211,8 +397,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
211
397
  keyword,
212
398
  ];
213
399
  if (chat_id) cmd.push("--chat-id", chat_id);
214
- if (start_time) cmd.push("--start-time", start_time);
215
- if (end_time) cmd.push("--end-time", end_time);
400
+ if (start_time) cmd.push("--start", start_time);
401
+ if (end_time) cmd.push("--end", end_time);
216
402
  return await run(cmd);
217
403
  }
218
404
  case "feishu_list_chats": {
@@ -245,6 +431,153 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
245
431
  if (count) cmd.push("--page-size", String(count));
246
432
  return await run(cmd);
247
433
  }
434
+
435
+ // ---- Docs / Wiki / Drive ----
436
+ case "feishu_search_docs": {
437
+ const { query, count } = args;
438
+ const cmd = ["docs", "+search", "--as", "user", "--query", query];
439
+ if (count) cmd.push("--page-size", String(count));
440
+ return await run(cmd);
441
+ }
442
+ case "feishu_doc_fetch": {
443
+ const { doc } = args;
444
+ return await run(["docs", "+fetch", "--as", "user", "--doc", doc]);
445
+ }
446
+ case "feishu_doc_create": {
447
+ const { title, markdown, folder_token } = args;
448
+ const cmd = ["docs", "+create", "--as", "user", "--title", title];
449
+ if (markdown) cmd.push("--markdown", markdown);
450
+ if (folder_token) cmd.push("--folder-token", folder_token);
451
+ return await run(cmd);
452
+ }
453
+ case "feishu_doc_update": {
454
+ const { doc, markdown, mode, new_title } = args;
455
+ const cmd = [
456
+ "docs",
457
+ "+update",
458
+ "--as",
459
+ "user",
460
+ "--doc",
461
+ doc,
462
+ "--markdown",
463
+ markdown,
464
+ "--mode",
465
+ mode || "append",
466
+ ];
467
+ if (new_title) cmd.push("--new-title", new_title);
468
+ return await run(cmd);
469
+ }
470
+ case "feishu_drive_search": {
471
+ const { query, doc_types, mine } = args;
472
+ const cmd = ["drive", "+search", "--as", "user"];
473
+ if (query) cmd.push("--query", query);
474
+ if (doc_types) cmd.push("--doc-types", doc_types);
475
+ if (mine === true) cmd.push("--mine");
476
+ return await run(cmd);
477
+ }
478
+ case "feishu_wiki_node_list": {
479
+ const { space_id, parent_node_token } = args;
480
+ const cmd = [
481
+ "wiki",
482
+ "+node-list",
483
+ "--as",
484
+ "user",
485
+ "--space-id",
486
+ space_id,
487
+ ];
488
+ if (parent_node_token)
489
+ cmd.push("--parent-node-token", parent_node_token);
490
+ return await run(cmd);
491
+ }
492
+ case "feishu_wiki_node_get": {
493
+ const { node_token } = args;
494
+ return await run([
495
+ "wiki",
496
+ "+node-get",
497
+ "--as",
498
+ "user",
499
+ "--node-token",
500
+ node_token,
501
+ ]);
502
+ }
503
+
504
+ // ---- Bitable / Base ----
505
+ case "feishu_base_table_list": {
506
+ const { base_token } = args;
507
+ return await run([
508
+ "base",
509
+ "+table-list",
510
+ "--as",
511
+ "user",
512
+ "--base-token",
513
+ base_token,
514
+ ]);
515
+ }
516
+ case "feishu_base_field_list": {
517
+ const { base_token, table_id } = args;
518
+ return await run([
519
+ "base",
520
+ "+field-list",
521
+ "--as",
522
+ "user",
523
+ "--base-token",
524
+ base_token,
525
+ "--table-id",
526
+ table_id,
527
+ ]);
528
+ }
529
+ case "feishu_base_record_list": {
530
+ const { base_token, table_id, limit, filter_json, sort_json } = args;
531
+ const cmd = [
532
+ "base",
533
+ "+record-list",
534
+ "--as",
535
+ "user",
536
+ "--base-token",
537
+ base_token,
538
+ "--table-id",
539
+ table_id,
540
+ ];
541
+ if (limit) cmd.push("--limit", String(limit));
542
+ if (filter_json) cmd.push("--filter-json", filter_json);
543
+ if (sort_json) cmd.push("--sort-json", sort_json);
544
+ return await run(cmd);
545
+ }
546
+ case "feishu_base_record_search": {
547
+ const { base_token, table_id, limit } = args;
548
+ const cmd = [
549
+ "base",
550
+ "+record-search",
551
+ "--as",
552
+ "user",
553
+ "--base-token",
554
+ base_token,
555
+ "--table-id",
556
+ table_id,
557
+ ];
558
+ if (limit) cmd.push("--limit", String(limit));
559
+ return await run(cmd);
560
+ }
561
+ case "feishu_base_record_upsert": {
562
+ const { base_token, table_id, fields_json, record_id } = args;
563
+ const cmd = [
564
+ "base",
565
+ "+record-upsert",
566
+ "--as",
567
+ "user",
568
+ "--base-token",
569
+ base_token,
570
+ "--table-id",
571
+ table_id,
572
+ "--json",
573
+ fields_json,
574
+ "--format",
575
+ "json",
576
+ ];
577
+ if (record_id) cmd.push("--record-id", record_id);
578
+ return await run(cmd);
579
+ }
580
+
248
581
  default:
249
582
  return error(`未知工具: ${name}`);
250
583
  }