feishu-mcp 0.1.7 → 0.1.8

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.
@@ -20,7 +20,7 @@ TableCreateSchema, WhiteboardFillArraySchema } from '../../types/feishuSchema.js
20
20
  */
21
21
  export function registerFeishuBlockTools(server, feishuService) {
22
22
  // 添加更新块文本内容工具
23
- server.tool('update_feishu_block_text', 'Updates the text content and styling of a specific block in a Feishu document. Can be used to modify content in existing text, code, or heading blocks while preserving the block type and other properties. Note: For Feishu wiki links (https://xxx.feishu.cn/wiki/xxx) you must first use convert_feishu_wiki_to_document_id tool to obtain a compatible document ID.', {
23
+ server.tool('update_feishu_block_text', 'Updates the text content and styling of a specific block in a Feishu document. Can be used to modify content in existing text, code, or heading blocks while preserving the block type and other properties. Note: For Feishu wiki links (https://xxx.feishu.cn/wiki/xxx), use get_feishu_document_info to get document information, then use the returned documentId for editing operations.', {
24
24
  documentId: DocumentIdSchema,
25
25
  blockId: BlockIdSchema,
26
26
  textElements: TextElementsArraySchema,
@@ -47,7 +47,7 @@ export function registerFeishuBlockTools(server, feishuService) {
47
47
  }
48
48
  });
49
49
  // 添加通用飞书块创建工具(支持文本、代码、标题)
50
- server.tool('batch_create_feishu_blocks', 'PREFERRED: Efficiently creates multiple blocks (text, code, heading, list, image, mermaid, whiteboard) in a single API call. USE THIS TOOL when creating multiple consecutive blocks at the same position - reduces API calls by up to 90%. KEY FEATURES: (1) Handles any number of blocks by auto-batching large requests (>50 blocks), (2) Creates blocks at consecutive positions in a document, (3) Supports direct heading level format (e.g. "heading1", "heading2") or standard "heading" type with level in options. CORRECT FORMAT: mcp_feishu_batch_create_feishu_blocks({documentId:"doc123",parentBlockId:"para123",startIndex:0,blocks:[{blockType:"text",options:{...}},{blockType:"heading1",options:{heading:{content:"Title"}}}]}). For whiteboard blocks, use blockType:"whiteboard" with options:{whiteboard:{align:1}}. After creating a whiteboard block, you will receive a token in the response (board.token field) which can be used with fill_whiteboard_with_plantuml tool. The fill_whiteboard_with_plantuml tool supports both PlantUML (syntax_type: 1) and Mermaid (syntax_type: 2) formats. For separate positions, use individual block creation tools instead. For wiki links (https://xxx.feishu.cn/wiki/xxx), first convert with convert_feishu_wiki_to_document_id tool.', {
50
+ server.tool('batch_create_feishu_blocks', 'PREFERRED: Efficiently creates multiple blocks (text, code, heading, list, image, mermaid, whiteboard) in a single API call. USE THIS TOOL when creating multiple consecutive blocks at the same position - reduces API calls by up to 90%. KEY FEATURES: (1) Handles any number of blocks by auto-batching large requests (>50 blocks), (2) Creates blocks at consecutive positions in a document, (3) Supports direct heading level format (e.g. "heading1", "heading2") or standard "heading" type with level in options. CORRECT FORMAT: mcp_feishu_batch_create_feishu_blocks({documentId:"doc123",parentBlockId:"para123",startIndex:0,blocks:[{blockType:"text",options:{...}},{blockType:"heading1",options:{heading:{content:"Title"}}}]}). For whiteboard blocks, use blockType:"whiteboard" with options:{whiteboard:{align:1}}. After creating a whiteboard block, you will receive a token in the response (board.token field) which can be used with fill_whiteboard_with_plantuml tool. The fill_whiteboard_with_plantuml tool supports both PlantUML (syntax_type: 1) and Mermaid (syntax_type: 2) formats. For separate positions, use individual block creation tools instead. For wiki links (https://xxx.feishu.cn/wiki/xxx), use get_feishu_document_info to get document information, then use the returned documentId for editing operations.', {
51
51
  documentId: DocumentIdSchema,
52
52
  parentBlockId: ParentBlockIdSchema,
53
53
  index: IndexSchema,
@@ -458,34 +458,41 @@ export function registerFeishuBlockTools(server, feishuService) {
458
458
  // },
459
459
  // );
460
460
  // 添加飞书Wiki文档ID转换工具
461
- server.tool('convert_feishu_wiki_to_document_id', 'Converts a Feishu Wiki document link to a compatible document ID. This conversion is required before using wiki links with any other Feishu document tools.', {
462
- wikiUrl: z.string().describe('Wiki URL or Token (required). Supports complete URL formats like https://xxx.feishu.cn/wiki/xxxxx or direct use of the Token portion'),
463
- }, async ({ wikiUrl }) => {
464
- try {
465
- if (!feishuService) {
466
- return {
467
- content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
468
- };
469
- }
470
- Logger.info(`开始转换Wiki文档链接,输入: ${wikiUrl}`);
471
- const documentId = await feishuService.convertWikiToDocumentId(wikiUrl);
472
- Logger.info(`Wiki文档转换成功,可用的文档ID为: ${documentId}`);
473
- return {
474
- content: [
475
- { type: 'text', text: `Converted Wiki link to Document ID: ${documentId}\n\nUse this Document ID with other Feishu document tools.` }
476
- ],
477
- };
478
- }
479
- catch (error) {
480
- Logger.error(`转换Wiki文档链接失败:`, error);
481
- const errorMessage = formatErrorMessage(error);
482
- return {
483
- content: [{ type: 'text', text: `转换Wiki文档链接失败: ${errorMessage}` }],
484
- };
485
- }
486
- });
461
+ // server.tool(
462
+ // 'convert_feishu_wiki_to_document_id',
463
+ // 'Converts a Feishu Wiki document link to a compatible document ID. This conversion is required before using wiki links with any other Feishu document tools.',
464
+ // {
465
+ // wikiUrl: z.string().describe('Wiki URL or Token (required). Supports complete URL formats like https://xxx.feishu.cn/wiki/xxxxx or direct use of the Token portion'),
466
+ // },
467
+ // async ({ wikiUrl }) => {
468
+ // try {
469
+ // if (!feishuService) {
470
+ // return {
471
+ // content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
472
+ // };
473
+ // }
474
+ //
475
+ // Logger.info(`开始转换Wiki文档链接,输入: ${wikiUrl}`);
476
+ // const documentId = await feishuService.convertWikiToDocumentId(wikiUrl);
477
+ //
478
+ // Logger.info(`Wiki文档转换成功,可用的文档ID为: ${documentId}`);
479
+ //
480
+ // return {
481
+ // content: [
482
+ // { type: 'text', text: `Converted Wiki link to Document ID: ${documentId}\n\nUse this Document ID with other Feishu document tools.` }
483
+ // ],
484
+ // };
485
+ // } catch (error) {
486
+ // Logger.error(`转换Wiki文档链接失败:`, error);
487
+ // const errorMessage = formatErrorMessage(error);
488
+ // return {
489
+ // content: [{ type: 'text', text: `转换Wiki文档链接失败: ${errorMessage}` }],
490
+ // };
491
+ // }
492
+ // },
493
+ // );
487
494
  // 添加删除文档块工具
488
- server.tool('delete_feishu_document_blocks', 'Deletes one or more consecutive blocks from a Feishu document. Use this tool to remove unwanted content, clean up document structure, or clear space before inserting new content. Supports batch deletion for efficiency. Note: For Feishu wiki links (https://xxx.feishu.cn/wiki/xxx) you must first use convert_feishu_wiki_to_document_id tool to obtain a compatible document ID.', {
495
+ server.tool('delete_feishu_document_blocks', 'Deletes one or more consecutive blocks from a Feishu document. Use this tool to remove unwanted content, clean up document structure, or clear space before inserting new content. Supports batch deletion for efficiency. Note: For Feishu wiki links (https://xxx.feishu.cn/wiki/xxx), use get_feishu_document_info to get document information, then use the returned documentId for editing operations.', {
489
496
  documentId: DocumentIdSchema,
490
497
  parentBlockId: ParentBlockIdSchema,
491
498
  startIndex: StartIndexSchema,
@@ -647,7 +654,7 @@ export function registerFeishuBlockTools(server, feishuService) {
647
654
  }
648
655
  });
649
656
  // 添加创建飞书表格工具
650
- server.tool('create_feishu_table', 'Creates a table block in a Feishu document with specified rows and columns. Each cell can contain different types of content blocks (text, lists, code, etc.). This tool creates the complete table structure including table cells and their content. Note: For Feishu wiki links (https://xxx.feishu.cn/wiki/xxx) you must first use convert_feishu_wiki_to_document_id tool to obtain a compatible document ID.', {
657
+ server.tool('create_feishu_table', 'Creates a table block in a Feishu document with specified rows and columns. Each cell can contain different types of content blocks (text, lists, code, etc.). This tool creates the complete table structure including table cells and their content. Note: For Feishu wiki links (https://xxx.feishu.cn/wiki/xxx), use get_feishu_document_info to get document information, then use the returned documentId for editing operations.', {
651
658
  documentId: DocumentIdSchema,
652
659
  parentBlockId: ParentBlockIdSchema,
653
660
  index: IndexSchema,
@@ -1,61 +1,126 @@
1
1
  import { formatErrorMessage } from '../../utils/error.js';
2
2
  import { Logger } from '../../utils/logger.js';
3
- import { FolderTokenSchema, FolderNameSchema, } from '../../types/feishuSchema.js';
4
- import { Config } from '../../utils/config.js';
3
+ import { FolderTokenSchema, FolderTokenOptionalSchema, FolderNameSchema, WikiSpaceNodeContextSchema, } from '../../types/feishuSchema.js';
5
4
  /**
6
5
  * 注册飞书文件夹相关的MCP工具
7
6
  * @param server MCP服务器实例
8
7
  * @param feishuService 飞书API服务实例
9
8
  */
10
9
  export function registerFeishuFolderTools(server, feishuService) {
11
- const config = Config.getInstance();
12
10
  // 添加获取根文件夹信息工具
13
- if (config.feishu.authType === 'user') {
14
- server.tool('get_feishu_root_folder_info', 'Retrieves basic information about the root folder in Feishu Drive. Returns the token, ID and user ID of the root folder, which can be used for subsequent folder operations.', {}, async () => {
15
- try {
16
- if (!feishuService) {
17
- return {
18
- content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
19
- };
20
- }
21
- Logger.info(`开始获取飞书根文件夹信息`);
22
- const folderInfo = await feishuService.getRootFolderInfo();
23
- Logger.info(`飞书根文件夹信息获取成功,token: ${folderInfo.token}`);
11
+ server.tool('get_feishu_root_folder_info', 'Retrieves the root folder in Feishu Drive, wiki spaces list, and "My Library". Use this when you need to browse folders or wiki spaces from the root. If you know the wiki node name, you can also use search_feishu_documents to directly locate specific wiki nodes instead of traversing from root. Returns root folder token, all wiki spaces, and personal library information.', {}, async () => {
12
+ try {
13
+ if (!feishuService) {
24
14
  return {
25
- content: [{ type: 'text', text: JSON.stringify(folderInfo, null, 2) }],
15
+ content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
26
16
  };
27
17
  }
18
+ Logger.info(`开始获取飞书根文件夹信息、知识空间列表和我的知识库`);
19
+ const result = {
20
+ root_folder: null,
21
+ wiki_spaces: null,
22
+ my_library: null,
23
+ };
24
+ // 获取根文件夹信息
25
+ try {
26
+ const folderInfo = await feishuService.getRootFolderInfo();
27
+ result.root_folder = folderInfo?.data || folderInfo;
28
+ Logger.info(`飞书根文件夹信息获取成功,token: ${result.root_folder?.token}`);
29
+ }
28
30
  catch (error) {
29
31
  Logger.error(`获取飞书根文件夹信息失败:`, error);
30
- const errorMessage = formatErrorMessage(error, '获取飞书根文件夹信息失败');
31
- return {
32
- content: [{ type: 'text', text: errorMessage }],
33
- };
32
+ result.root_folder = { error: formatErrorMessage(error, '获取根文件夹信息失败') };
33
+ }
34
+ // 获取知识空间列表(遍历所有分页)
35
+ try {
36
+ const wikiSpaces = await feishuService.getAllWikiSpacesList(20);
37
+ result.wiki_spaces = wikiSpaces || [];
38
+ Logger.info(`知识空间列表获取成功,共 ${Array.isArray(result.wiki_spaces) ? result.wiki_spaces.length : 0} 个空间`);
39
+ }
40
+ catch (error) {
41
+ Logger.error(`获取知识空间列表失败:`, error);
42
+ result.wiki_spaces = [];
34
43
  }
35
- });
36
- }
44
+ // 获取"我的知识库"(通过传入 my_library 作为 space_id)
45
+ try {
46
+ const myLibrary = await feishuService.getWikiSpaceInfo('my_library', 'en');
47
+ // 提取 space 对象的内容,去掉 space 这一层
48
+ const libraryData = myLibrary?.data || myLibrary;
49
+ result.my_library = libraryData?.space || libraryData;
50
+ Logger.info(`我的知识库获取成功,space_id: ${result.my_library?.space_id}`);
51
+ }
52
+ catch (error) {
53
+ Logger.error(`获取我的知识库失败:`, error);
54
+ result.my_library = { error: formatErrorMessage(error, '获取我的知识库失败') };
55
+ }
56
+ return {
57
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
58
+ };
59
+ }
60
+ catch (error) {
61
+ Logger.error(`获取飞书信息失败:`, error);
62
+ const errorMessage = formatErrorMessage(error, '获取飞书信息失败');
63
+ return {
64
+ content: [{ type: 'text', text: errorMessage }],
65
+ };
66
+ }
67
+ });
37
68
  // 添加获取文件夹中的文件清单工具
38
- server.tool('get_feishu_folder_files', 'Retrieves a list of files and subfolders in a specified folder. Use this to explore folder contents, view file metadata, and get URLs and tokens for further operations.', {
39
- folderToken: FolderTokenSchema,
40
- }, async ({ folderToken, }) => {
69
+ server.tool('get_feishu_folder_files', 'Retrieves a list of files and subfolders in a specified folder or wiki space node. Supports two modes: (1) Feishu Drive folder mode: use folderToken to get files in a Feishu Drive folder. (2) Wiki space node mode: use wikiContext with spaceId (and optional parentNodeToken) to get documents under a wiki space node. If parentNodeToken is not provided, retrieves nodes from the root of the wiki space. Only one mode can be used at a time - provide either folderToken OR wikiContext.', {
70
+ folderToken: FolderTokenOptionalSchema,
71
+ wikiContext: WikiSpaceNodeContextSchema,
72
+ }, async ({ folderToken, wikiContext }) => {
41
73
  try {
42
74
  if (!feishuService) {
43
75
  return {
44
76
  content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
45
77
  };
46
78
  }
47
- Logger.info(`开始获取飞书文件夹中的文件清单,文件夹Token: ${folderToken}`);
48
- const fileList = await feishuService.getFolderFileList(folderToken);
49
- Logger.info(`飞书文件夹中的文件清单获取成功,共 ${fileList.files?.length || 0} 个文件`);
79
+ // 验证参数:必须提供 folderToken 或 wikiContext 之一,但不能同时提供
80
+ if (folderToken && wikiContext) {
81
+ return {
82
+ content: [{ type: 'text', text: '错误:不能同时提供 folderToken 和 wikiContext 参数,请选择其中一种模式。' }],
83
+ };
84
+ }
85
+ if (!folderToken && !wikiContext) {
86
+ return {
87
+ content: [{ type: 'text', text: '错误:必须提供 folderToken(飞书文档目录模式)或 wikiContext(知识库节点模式)参数之一。' }],
88
+ };
89
+ }
90
+ // 模式一:飞书文档目录模式
91
+ if (folderToken) {
92
+ Logger.info(`开始获取飞书文件夹中的文件清单,文件夹Token: ${folderToken}`);
93
+ const fileList = await feishuService.getFolderFileList(folderToken);
94
+ Logger.info(`飞书文件夹中的文件清单获取成功,共 ${fileList.files?.length || 0} 个文件`);
95
+ return {
96
+ content: [{ type: 'text', text: JSON.stringify(fileList, null, 2) }],
97
+ };
98
+ }
99
+ // 模式二:知识库节点模式
100
+ if (wikiContext) {
101
+ const { spaceId, parentNodeToken } = wikiContext;
102
+ if (!spaceId) {
103
+ return {
104
+ content: [{ type: 'text', text: '错误:使用 wikiContext 模式时,必须提供 spaceId。' }],
105
+ };
106
+ }
107
+ Logger.info(`开始获取知识空间子节点列表,知识空间ID: ${spaceId}, 父节点Token: ${parentNodeToken || 'null(根节点)'}`);
108
+ const nodeList = await feishuService.getAllWikiSpaceNodes(spaceId, parentNodeToken);
109
+ Logger.info(`知识空间子节点列表获取成功,共 ${Array.isArray(nodeList) ? nodeList.length : 0} 个节点`);
110
+ return {
111
+ content: [{ type: 'text', text: JSON.stringify({ nodes: nodeList || [] }, null, 2) }],
112
+ };
113
+ }
114
+ // 理论上不会到达这里
50
115
  return {
51
- content: [{ type: 'text', text: JSON.stringify(fileList, null, 2) }],
116
+ content: [{ type: 'text', text: '错误:未知错误' }],
52
117
  };
53
118
  }
54
119
  catch (error) {
55
- Logger.error(`获取飞书文件夹中的文件清单失败:`, error);
120
+ Logger.error(`获取文件列表失败:`, error);
56
121
  const errorMessage = formatErrorMessage(error);
57
122
  return {
58
- content: [{ type: 'text', text: `获取飞书文件夹中的文件清单失败: ${errorMessage}` }],
123
+ content: [{ type: 'text', text: `获取文件列表失败: ${errorMessage}` }],
59
124
  };
60
125
  }
61
126
  });
@@ -1,9 +1,8 @@
1
- // import { z } from 'zod';
2
1
  import { formatErrorMessage } from '../../utils/error.js';
3
2
  import { Logger } from '../../utils/logger.js';
4
- import { DocumentIdSchema,
3
+ import { DocumentIdSchema, DocumentIdOrWikiIdSchema, DocumentTypeSchema,
5
4
  // BlockIdSchema,
6
- SearchKeySchema, WhiteboardIdSchema, DocumentTitleSchema, FolderTokenSchema, } from '../../types/feishuSchema.js';
5
+ SearchKeySchema, SearchTypeSchema, PageTokenSchema, OffsetSchema, WhiteboardIdSchema, DocumentTitleSchema, FolderTokenOptionalSchema, WikiSpaceNodeContextSchema, } from '../../types/feishuSchema.js';
7
6
  /**
8
7
  * 注册飞书相关的MCP工具
9
8
  * @param server MCP服务器实例
@@ -11,42 +10,94 @@ SearchKeySchema, WhiteboardIdSchema, DocumentTitleSchema, FolderTokenSchema, } f
11
10
  */
12
11
  export function registerFeishuTools(server, feishuService) {
13
12
  // 添加创建飞书文档工具
14
- server.tool('create_feishu_document', 'Creates a new Feishu document and returns its information. Use this tool when you need to create a document from scratch with a specific title and folder location.', {
13
+ server.tool('create_feishu_document', 'Creates a new Feishu document and returns its information. Supports two modes: (1) Feishu Drive folder mode: use folderToken to create a document in a folder. (2) Wiki space node mode: use wikiContext with spaceId (and optional parentNodeToken) to create a node (document) in a wiki space. IMPORTANT: In wiki spaces, documents are nodes themselves - they can act as parent nodes containing child documents, and can also be edited as regular documents. The created node returns both node_token (node ID, can be used as parentNodeToken for creating child nodes) and obj_token (document ID, can be used for document editing operations like get_feishu_document_blocks, batch_create_feishu_blocks, etc.). Only one mode can be used at a time - provide either folderToken OR wikiContext, not both.', {
15
14
  title: DocumentTitleSchema,
16
- folderToken: FolderTokenSchema,
17
- }, async ({ title, folderToken }) => {
15
+ folderToken: FolderTokenOptionalSchema,
16
+ wikiContext: WikiSpaceNodeContextSchema,
17
+ }, async ({ title, folderToken, wikiContext }) => {
18
18
  try {
19
- Logger.info(`开始创建飞书文档,标题: ${title}${folderToken ? `,文件夹Token: ${folderToken}` : ',使用默认文件夹'}`);
20
- const newDoc = await feishuService?.createDocument(title, folderToken);
21
- if (!newDoc) {
22
- throw new Error('创建文档失败,未返回文档信息');
19
+ if (!feishuService) {
20
+ return {
21
+ content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
22
+ };
23
+ }
24
+ // 参数验证:必须提供 folderToken 或 wikiContext 之一,但不能同时提供
25
+ if (folderToken && wikiContext) {
26
+ return {
27
+ content: [{ type: 'text', text: '错误:不能同时提供 folderToken 和 wikiContext 参数,请选择其中一种模式。\n- 使用 folderToken 在飞书文档目录中创建文档\n- 使用 wikiContext 在知识库中创建节点(文档)' }],
28
+ };
23
29
  }
24
- Logger.info(`飞书文档创建成功,文档ID: ${newDoc.objToken || newDoc.document_id}`);
30
+ if (!folderToken && !wikiContext) {
31
+ return {
32
+ content: [{ type: 'text', text: '错误:必须提供 folderToken(飞书文档目录模式)或 wikiContext(知识库节点模式)参数之一。' }],
33
+ };
34
+ }
35
+ // 模式一:飞书文档目录模式
36
+ if (folderToken) {
37
+ Logger.info(`开始创建飞书文档(文件夹模式),标题: ${title},文件夹Token: ${folderToken}`);
38
+ const newDoc = await feishuService.createDocument(title, folderToken);
39
+ if (!newDoc) {
40
+ throw new Error('创建文档失败,未返回文档信息');
41
+ }
42
+ Logger.info(`飞书文档创建成功,文档ID: ${newDoc.objToken || newDoc.document_id}`);
43
+ return {
44
+ content: [{ type: 'text', text: JSON.stringify(newDoc, null, 2) }],
45
+ };
46
+ }
47
+ // 模式二:知识库节点模式
48
+ if (wikiContext) {
49
+ const { spaceId, parentNodeToken } = wikiContext;
50
+ if (!spaceId) {
51
+ return {
52
+ content: [{ type: 'text', text: '错误:使用 wikiContext 模式时,必须提供 spaceId。' }],
53
+ };
54
+ }
55
+ Logger.info(`开始创建知识库节点,标题: ${title},知识空间ID: ${spaceId},父节点Token: ${parentNodeToken || 'null(根节点)'}`);
56
+ const node = await feishuService.createWikiSpaceNode(spaceId, title, parentNodeToken);
57
+ if (!node) {
58
+ throw new Error('创建知识库节点失败,未返回节点信息');
59
+ }
60
+ // 构建返回信息,说明知识库节点的特殊性质
61
+ const result = {
62
+ ...node,
63
+ _note: '知识库节点既是节点又是文档:node_token 可作为父节点使用,obj_token 可用于文档编辑操作'
64
+ };
65
+ Logger.info(`知识库节点创建成功,node_token: ${node.node_token}, obj_token: ${node.obj_token}`);
66
+ return {
67
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
68
+ };
69
+ }
70
+ // 理论上不会到达这里
25
71
  return {
26
- content: [{ type: 'text', text: JSON.stringify(newDoc, null, 2) }],
72
+ content: [{ type: 'text', text: '错误:未知错误' }],
27
73
  };
28
74
  }
29
75
  catch (error) {
30
- Logger.error(`创建飞书文档失败:`, error);
76
+ Logger.error(`创建文档失败:`, error);
31
77
  const errorMessage = formatErrorMessage(error);
32
78
  return {
33
- content: [{ type: 'text', text: `创建飞书文档失败: ${errorMessage}` }],
79
+ content: [{ type: 'text', text: `创建文档失败: ${errorMessage}` }],
34
80
  };
35
81
  }
36
82
  });
37
- // 添加获取飞书文档信息工具
38
- server.tool('get_feishu_document_info', 'Retrieves basic information about a Feishu document. Use this to verify a document exists, check access permissions, or get metadata like title, type, and creation information.', {
39
- documentId: DocumentIdSchema,
40
- }, async ({ documentId }) => {
83
+ // 添加获取飞书文档信息工具(支持普通文档和Wiki文档)
84
+ server.tool('get_feishu_document_info', 'Retrieves basic information about a Feishu document or Wiki node. Supports both regular documents (via document ID/URL) and Wiki documents (via Wiki URL/token). Use this to verify a document exists, check access permissions, or get metadata like title, type, and creation information. For Wiki documents, returns complete node information including documentId (obj_token) for document editing operations, and space_id and node_token for creating child nodes. ', {
85
+ documentId: DocumentIdOrWikiIdSchema,
86
+ documentType: DocumentTypeSchema,
87
+ }, async ({ documentId, documentType }) => {
41
88
  try {
42
89
  if (!feishuService) {
43
90
  return {
44
91
  content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
45
92
  };
46
93
  }
47
- Logger.info(`开始获取飞书文档信息,文档ID: ${documentId}`);
48
- const docInfo = await feishuService.getDocumentInfo(documentId);
49
- Logger.info(`飞书文档信息获取成功,标题: ${docInfo.title}`);
94
+ Logger.info(`开始获取飞书文档信息,文档ID: ${documentId}, 类型: ${documentType || 'auto'}`);
95
+ const docInfo = await feishuService.getDocumentInfo(documentId, documentType);
96
+ if (!docInfo) {
97
+ throw new Error('获取文档信息失败,未返回数据');
98
+ }
99
+ const title = docInfo.title || docInfo.document?.title || '未知标题';
100
+ Logger.info(`飞书文档信息获取成功,标题: ${title}, 类型: ${docInfo._type || 'document'}`);
50
101
  return {
51
102
  content: [{ type: 'text', text: JSON.stringify(docInfo, null, 2) }],
52
103
  };
@@ -92,7 +143,7 @@ export function registerFeishuTools(server, feishuService) {
92
143
  // },
93
144
  // );
94
145
  // 添加获取飞书文档块工具
95
- server.tool('get_feishu_document_blocks', 'Retrieves the block structure information of a Feishu document. Essential to use before inserting content to understand document structure and determine correct insertion positions. Returns a detailed hierarchy of blocks with their IDs, types, and content. Note: For Feishu wiki links (https://xxx.feishu.cn/wiki/xxx) you must first use convert_feishu_wiki_to_document_id tool to obtain a compatible document ID.', {
146
+ server.tool('get_feishu_document_blocks', 'Retrieves the block structure information of a Feishu document. Essential to use before inserting content to understand document structure and determine correct insertion positions. Returns a detailed hierarchy of blocks with their IDs, types, and content. Note: For Feishu wiki links (https://xxx.feishu.cn/wiki/xxx), use get_feishu_document_info to get document information, then use the returned documentId for editing operations.', {
96
147
  documentId: DocumentIdSchema,
97
148
  }, async ({ documentId }) => {
98
149
  try {
@@ -183,19 +234,22 @@ export function registerFeishuTools(server, feishuService) {
183
234
  // }
184
235
  // },
185
236
  // );
186
- // 添加搜索文档工具
187
- server.tool('search_feishu_documents', 'Searches for documents in Feishu. Supports keyword-based search and returns document information including title, type, and owner. Use this tool to find specific content or related documents in your document library.', {
237
+ // 添加搜索文档工具(支持文档和知识库搜索)
238
+ server.tool('search_feishu_documents', 'Searches for documents and/or Wiki knowledge base nodes in Feishu. Supports keyword-based search with type filtering (document, wiki, or both). Returns document and wiki information including title, type, and owner. Supports pagination: use offset for document search pagination and pageToken for wiki search pagination. Each type (document or wiki) can return up to 100 results maximum per search. Default page size is 20 items.', {
188
239
  searchKey: SearchKeySchema,
189
- }, async ({ searchKey }) => {
240
+ searchType: SearchTypeSchema,
241
+ offset: OffsetSchema,
242
+ pageToken: PageTokenSchema,
243
+ }, async ({ searchKey, searchType, offset, pageToken }) => {
190
244
  try {
191
245
  if (!feishuService) {
192
246
  return {
193
247
  content: [{ type: 'text', text: 'Feishu service is not initialized. Please check the configuration.' }],
194
248
  };
195
249
  }
196
- Logger.info(`开始搜索飞书文档,关键字: ${searchKey},`);
197
- const searchResult = await feishuService.searchDocuments(searchKey);
198
- Logger.info(`文档搜索完成,找到 ${searchResult.size} 个结果`);
250
+ Logger.info(`开始搜索,关键字: ${searchKey}, 类型: ${searchType || 'both'}, offset: ${offset || 0}, pageToken: ${pageToken || '无'}`);
251
+ const searchResult = await feishuService.search(searchKey, searchType || 'both', offset, pageToken);
252
+ Logger.info(`搜索完成,文档: ${searchResult.documents?.length || 0} 条,知识库: ${searchResult.wikis?.length || 0} 条`);
199
253
  return {
200
254
  content: [
201
255
  { type: 'text', text: JSON.stringify(searchResult, null, 2) },
@@ -203,11 +257,11 @@ export function registerFeishuTools(server, feishuService) {
203
257
  };
204
258
  }
205
259
  catch (error) {
206
- Logger.error(`搜索飞书文档失败:`, error);
260
+ Logger.error(`搜索失败:`, error);
207
261
  const errorMessage = formatErrorMessage(error);
208
262
  return {
209
263
  content: [
210
- { type: 'text', text: `搜索飞书文档失败: ${errorMessage}` },
264
+ { type: 'text', text: `搜索失败: ${errorMessage}` },
211
265
  ],
212
266
  };
213
267
  }