feishu-mcp 0.0.10 → 0.0.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/dist/manager/sseConnectionManager.js +104 -0
- package/dist/mcp/feishuMcp.js +67 -0
- package/dist/mcp/tools/feishuBlockTools.js +427 -0
- package/dist/mcp/tools/feishuFolderTools.js +86 -0
- package/dist/mcp/tools/feishuTools.js +137 -0
- package/dist/server.js +39 -637
- package/dist/services/blockFactory.js +29 -11
- package/dist/services/feishuApiService.js +90 -3
- package/dist/types/feishuSchema.js +8 -7
- package/dist/utils/logger.js +27 -11
- package/package.json +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { formatErrorMessage } from '../../utils/error.js';
|
|
2
|
+
import { Logger } from '../../utils/logger.js';
|
|
3
|
+
import { FolderTokenSchema, FolderNameSchema, OrderBySchema, DirectionSchema } from '../../types/feishuSchema.js';
|
|
4
|
+
/**
|
|
5
|
+
* 注册飞书文件夹相关的MCP工具
|
|
6
|
+
* @param server MCP服务器实例
|
|
7
|
+
* @param feishuService 飞书API服务实例
|
|
8
|
+
*/
|
|
9
|
+
export function registerFeishuFolderTools(server, feishuService) {
|
|
10
|
+
// 添加获取根文件夹信息工具
|
|
11
|
+
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 () => {
|
|
12
|
+
try {
|
|
13
|
+
if (!feishuService) {
|
|
14
|
+
return {
|
|
15
|
+
content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
Logger.info(`开始获取飞书根文件夹信息`);
|
|
19
|
+
const folderInfo = await feishuService.getRootFolderInfo();
|
|
20
|
+
Logger.info(`飞书根文件夹信息获取成功,token: ${folderInfo.token}`);
|
|
21
|
+
return {
|
|
22
|
+
content: [{ type: 'text', text: JSON.stringify(folderInfo, null, 2) }],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
Logger.error(`获取飞书根文件夹信息失败:`, error);
|
|
27
|
+
const errorMessage = formatErrorMessage(error, '获取飞书根文件夹信息失败');
|
|
28
|
+
return {
|
|
29
|
+
content: [{ type: 'text', text: errorMessage }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
// 添加获取文件夹中的文件清单工具
|
|
34
|
+
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.', {
|
|
35
|
+
folderToken: FolderTokenSchema,
|
|
36
|
+
orderBy: OrderBySchema,
|
|
37
|
+
direction: DirectionSchema
|
|
38
|
+
}, async ({ folderToken, orderBy = 'EditedTime', direction = 'DESC' }) => {
|
|
39
|
+
try {
|
|
40
|
+
if (!feishuService) {
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
Logger.info(`开始获取飞书文件夹中的文件清单,文件夹Token: ${folderToken},排序方式: ${orderBy},排序方向: ${direction}`);
|
|
46
|
+
const fileList = await feishuService.getFolderFileList(folderToken, orderBy, direction);
|
|
47
|
+
Logger.info(`飞书文件夹中的文件清单获取成功,共 ${fileList.files?.length || 0} 个文件`);
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: 'text', text: JSON.stringify(fileList, null, 2) }],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
Logger.error(`获取飞书文件夹中的文件清单失败:`, error);
|
|
54
|
+
const errorMessage = formatErrorMessage(error);
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: 'text', text: `获取飞书文件夹中的文件清单失败: ${errorMessage}` }],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// 添加创建文件夹工具
|
|
61
|
+
server.tool('create_feishu_folder', 'Creates a new folder in a specified parent folder. Use this to organize documents and files within your Feishu Drive structure. Returns the token and URL of the newly created folder.', {
|
|
62
|
+
folderToken: FolderTokenSchema,
|
|
63
|
+
folderName: FolderNameSchema,
|
|
64
|
+
}, async ({ folderToken, folderName }) => {
|
|
65
|
+
try {
|
|
66
|
+
if (!feishuService) {
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
Logger.info(`开始创建飞书文件夹,父文件夹Token: ${folderToken},文件夹名称: ${folderName}`);
|
|
72
|
+
const result = await feishuService.createFolder(folderToken, folderName);
|
|
73
|
+
Logger.info(`飞书文件夹创建成功,token: ${result.token},URL: ${result.url}`);
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
Logger.error(`创建飞书文件夹失败:`, error);
|
|
80
|
+
const errorMessage = formatErrorMessage(error);
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: 'text', text: `创建飞书文件夹失败: ${errorMessage}` }],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatErrorMessage } from '../../utils/error.js';
|
|
3
|
+
import { Logger } from '../../utils/logger.js';
|
|
4
|
+
import { DocumentIdSchema, BlockIdSchema, } from '../../types/feishuSchema.js';
|
|
5
|
+
/**
|
|
6
|
+
* 注册飞书相关的MCP工具
|
|
7
|
+
* @param server MCP服务器实例
|
|
8
|
+
* @param feishuService 飞书API服务实例
|
|
9
|
+
*/
|
|
10
|
+
export function registerFeishuTools(server, feishuService) {
|
|
11
|
+
// 添加创建飞书文档工具
|
|
12
|
+
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
|
+
title: z.string().describe('Document title (required). This will be displayed in the Feishu document list and document header.'),
|
|
14
|
+
folderToken: z.string().describe('Folder token (required). Specifies where to create the document. Format is an alphanumeric string like "doxcnOu1ZKYH4RtX1Y5XwL5WGRh".'),
|
|
15
|
+
}, async ({ title, folderToken }) => {
|
|
16
|
+
try {
|
|
17
|
+
Logger.info(`开始创建飞书文档,标题: ${title}${folderToken ? `,文件夹Token: ${folderToken}` : ',使用默认文件夹'}`);
|
|
18
|
+
const newDoc = await feishuService?.createDocument(title, folderToken);
|
|
19
|
+
if (!newDoc) {
|
|
20
|
+
throw new Error('创建文档失败,未返回文档信息');
|
|
21
|
+
}
|
|
22
|
+
Logger.info(`飞书文档创建成功,文档ID: ${newDoc.objToken || newDoc.document_id}`);
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: 'text', text: JSON.stringify(newDoc, null, 2) }],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
Logger.error(`创建飞书文档失败:`, error);
|
|
29
|
+
const errorMessage = formatErrorMessage(error);
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: 'text', text: `创建飞书文档失败: ${errorMessage}` }],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
// 添加获取飞书文档信息工具
|
|
36
|
+
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.', {
|
|
37
|
+
documentId: DocumentIdSchema,
|
|
38
|
+
}, async ({ documentId }) => {
|
|
39
|
+
try {
|
|
40
|
+
if (!feishuService) {
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
Logger.info(`开始获取飞书文档信息,文档ID: ${documentId}`);
|
|
46
|
+
const docInfo = await feishuService.getDocumentInfo(documentId);
|
|
47
|
+
Logger.info(`飞书文档信息获取成功,标题: ${docInfo.title}`);
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: 'text', text: JSON.stringify(docInfo, null, 2) }],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
Logger.error(`获取飞书文档信息失败:`, error);
|
|
54
|
+
const errorMessage = formatErrorMessage(error, '获取飞书文档信息失败');
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: 'text', text: errorMessage }],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// 添加获取飞书文档内容工具
|
|
61
|
+
server.tool('get_feishu_document_content', 'Retrieves the plain text content of a Feishu document. Ideal for content analysis, processing, or when you need to extract text without formatting. The content maintains the document structure but without styling. 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.', {
|
|
62
|
+
documentId: DocumentIdSchema,
|
|
63
|
+
lang: z.number().optional().default(0).describe('Language code (optional). Default is 0 (Chinese). Use 1 for English if available.'),
|
|
64
|
+
}, async ({ documentId, lang }) => {
|
|
65
|
+
try {
|
|
66
|
+
if (!feishuService) {
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: 'text', text: 'Feishu service is not initialized. Please check the configuration' }],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
Logger.info(`开始获取飞书文档内容,文档ID: ${documentId},语言: ${lang}`);
|
|
72
|
+
const content = await feishuService.getDocumentContent(documentId, lang);
|
|
73
|
+
Logger.info(`飞书文档内容获取成功,内容长度: ${content.length}字符`);
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: 'text', text: content }],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
Logger.error(`获取飞书文档内容失败:`, error);
|
|
80
|
+
const errorMessage = formatErrorMessage(error);
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: 'text', text: `获取飞书文档内容失败: ${errorMessage}` }],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// 添加获取飞书文档块工具
|
|
87
|
+
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.', {
|
|
88
|
+
documentId: DocumentIdSchema,
|
|
89
|
+
}, async ({ documentId }) => {
|
|
90
|
+
try {
|
|
91
|
+
if (!feishuService) {
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: 'text', text: 'Feishu service is not initialized. Please check the configuration' }],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
Logger.info(`开始获取飞书文档块,文档ID: ${documentId}`);
|
|
97
|
+
const blocks = await feishuService.getDocumentBlocks(documentId);
|
|
98
|
+
Logger.info(`飞书文档块获取成功,共 ${blocks.length} 个块`);
|
|
99
|
+
return {
|
|
100
|
+
content: [{ type: 'text', text: JSON.stringify(blocks, null, 2) }],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
Logger.error(`获取飞书文档块失败:`, error);
|
|
105
|
+
const errorMessage = formatErrorMessage(error);
|
|
106
|
+
return {
|
|
107
|
+
content: [{ type: 'text', text: `获取飞书文档块失败: ${errorMessage}` }],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// 添加获取块内容工具
|
|
112
|
+
server.tool('get_feishu_block_content', 'Retrieves the detailed content and structure of a specific block in a Feishu document. Useful for inspecting block properties, formatting, and content, especially before making updates or for debugging purposes. 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.', {
|
|
113
|
+
documentId: DocumentIdSchema,
|
|
114
|
+
blockId: BlockIdSchema,
|
|
115
|
+
}, async ({ documentId, blockId }) => {
|
|
116
|
+
try {
|
|
117
|
+
if (!feishuService) {
|
|
118
|
+
return {
|
|
119
|
+
content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
Logger.info(`开始获取飞书块内容,文档ID: ${documentId},块ID: ${blockId}`);
|
|
123
|
+
const blockContent = await feishuService.getBlockContent(documentId, blockId);
|
|
124
|
+
Logger.info(`飞书块内容获取成功,块类型: ${blockContent.block_type}`);
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: 'text', text: JSON.stringify(blockContent, null, 2) }],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
Logger.error(`获取飞书块内容失败:`, error);
|
|
131
|
+
const errorMessage = formatErrorMessage(error);
|
|
132
|
+
return {
|
|
133
|
+
content: [{ type: 'text', text: `获取飞书块内容失败: ${errorMessage}` }],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|