feishu-mcp 0.0.13 → 0.0.15
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 +251 -261
- package/dist/mcp/tools/feishuBlockTools.js +252 -202
- package/dist/mcp/tools/feishuFolderTools.js +28 -22
- package/dist/mcp/tools/feishuTools.js +112 -55
- package/dist/services/blockFactory.js +20 -5
- package/dist/services/feishuApiService.js +90 -22
- package/dist/types/feishuSchema.js +33 -16
- package/package.json +75 -75
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
1
|
+
// import { z } from 'zod';
|
|
2
2
|
import { formatErrorMessage } from '../../utils/error.js';
|
|
3
3
|
import { Logger } from '../../utils/logger.js';
|
|
4
|
-
import { DocumentIdSchema,
|
|
4
|
+
import { DocumentIdSchema,
|
|
5
|
+
// BlockIdSchema,
|
|
6
|
+
SearchKeySchema, WhiteboardIdSchema, DocumentTitleSchema, FolderTokenSchema, } from '../../types/feishuSchema.js';
|
|
5
7
|
/**
|
|
6
8
|
* 注册飞书相关的MCP工具
|
|
7
9
|
* @param server MCP服务器实例
|
|
@@ -10,8 +12,8 @@ import { DocumentIdSchema, BlockIdSchema, SearchKeySchema, } from '../../types/f
|
|
|
10
12
|
export function registerFeishuTools(server, feishuService) {
|
|
11
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
|
-
title:
|
|
14
|
-
folderToken:
|
|
15
|
+
title: DocumentTitleSchema,
|
|
16
|
+
folderToken: FolderTokenSchema,
|
|
15
17
|
}, async ({ title, folderToken }) => {
|
|
16
18
|
try {
|
|
17
19
|
Logger.info(`开始创建飞书文档,标题: ${title}${folderToken ? `,文件夹Token: ${folderToken}` : ',使用默认文件夹'}`);
|
|
@@ -58,31 +60,37 @@ export function registerFeishuTools(server, feishuService) {
|
|
|
58
60
|
}
|
|
59
61
|
});
|
|
60
62
|
// 添加获取飞书文档内容工具
|
|
61
|
-
server.tool(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
63
|
+
// server.tool(
|
|
64
|
+
// 'get_feishu_document_content',
|
|
65
|
+
// '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.',
|
|
66
|
+
// {
|
|
67
|
+
// documentId: DocumentIdSchema,
|
|
68
|
+
// lang: z.number().optional().default(0).describe('Language code (optional). Default is 0 (Chinese). Use 1 for English if available.'),
|
|
69
|
+
// },
|
|
70
|
+
// async ({ documentId, lang }) => {
|
|
71
|
+
// try {
|
|
72
|
+
// if (!feishuService) {
|
|
73
|
+
// return {
|
|
74
|
+
// content: [{ type: 'text', text: 'Feishu service is not initialized. Please check the configuration' }],
|
|
75
|
+
// };
|
|
76
|
+
// }
|
|
77
|
+
//
|
|
78
|
+
// Logger.info(`开始获取飞书文档内容,文档ID: ${documentId},语言: ${lang}`);
|
|
79
|
+
// const content = await feishuService.getDocumentContent(documentId, lang);
|
|
80
|
+
// Logger.info(`飞书文档内容获取成功,内容长度: ${content.length}字符`);
|
|
81
|
+
//
|
|
82
|
+
// return {
|
|
83
|
+
// content: [{ type: 'text', text: content }],
|
|
84
|
+
// };
|
|
85
|
+
// } catch (error) {
|
|
86
|
+
// Logger.error(`获取飞书文档内容失败:`, error);
|
|
87
|
+
// const errorMessage = formatErrorMessage(error);
|
|
88
|
+
// return {
|
|
89
|
+
// content: [{ type: 'text', text: `获取飞书文档内容失败: ${errorMessage}` }],
|
|
90
|
+
// };
|
|
91
|
+
// }
|
|
92
|
+
// },
|
|
93
|
+
// );
|
|
86
94
|
// 添加获取飞书文档块工具
|
|
87
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.', {
|
|
88
96
|
documentId: DocumentIdSchema,
|
|
@@ -96,8 +104,26 @@ export function registerFeishuTools(server, feishuService) {
|
|
|
96
104
|
Logger.info(`开始获取飞书文档块,文档ID: ${documentId}`);
|
|
97
105
|
const blocks = await feishuService.getDocumentBlocks(documentId);
|
|
98
106
|
Logger.info(`飞书文档块获取成功,共 ${blocks.length} 个块`);
|
|
107
|
+
// 检查是否有 block_type 为 43 的块(画板块)
|
|
108
|
+
const whiteboardBlocks = blocks.filter((block) => block.block_type === 43);
|
|
109
|
+
const hasWhiteboardBlocks = whiteboardBlocks.length > 0;
|
|
110
|
+
let responseText = JSON.stringify(blocks, null, 2);
|
|
111
|
+
if (hasWhiteboardBlocks) {
|
|
112
|
+
responseText += '\n\n⚠️ 检测到画板块 (block_type: 43)!\n';
|
|
113
|
+
responseText += `发现 ${whiteboardBlocks.length} 个画板块。画板块包含丰富的图形内容,如形状、文本、思维导图等。\n`;
|
|
114
|
+
responseText += '建议使用 get_feishu_whiteboard_content 工具来获取画板的具体内容和结构。\n';
|
|
115
|
+
responseText += '画板信息:\n';
|
|
116
|
+
whiteboardBlocks.forEach((block, index) => {
|
|
117
|
+
responseText += ` ${index + 1}. 块ID: ${block.block_id}`;
|
|
118
|
+
if (block.board && block.board.token) {
|
|
119
|
+
responseText += `, 画板ID: ${block.board.token}`;
|
|
120
|
+
}
|
|
121
|
+
responseText += '\n';
|
|
122
|
+
});
|
|
123
|
+
responseText += '请使用上述画板ID调用 get_feishu_whiteboard_content 工具。';
|
|
124
|
+
}
|
|
99
125
|
return {
|
|
100
|
-
content: [{ type: 'text', text:
|
|
126
|
+
content: [{ type: 'text', text: responseText }],
|
|
101
127
|
};
|
|
102
128
|
}
|
|
103
129
|
catch (error) {
|
|
@@ -109,31 +135,37 @@ export function registerFeishuTools(server, feishuService) {
|
|
|
109
135
|
}
|
|
110
136
|
});
|
|
111
137
|
// 添加获取块内容工具
|
|
112
|
-
server.tool(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
// server.tool(
|
|
139
|
+
// 'get_feishu_block_content',
|
|
140
|
+
// '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.',
|
|
141
|
+
// {
|
|
142
|
+
// documentId: DocumentIdSchema,
|
|
143
|
+
// blockId: BlockIdSchema,
|
|
144
|
+
// },
|
|
145
|
+
// async ({ documentId, blockId }) => {
|
|
146
|
+
// try {
|
|
147
|
+
// if (!feishuService) {
|
|
148
|
+
// return {
|
|
149
|
+
// content: [{ type: 'text', text: '飞书服务未初始化,请检查配置' }],
|
|
150
|
+
// };
|
|
151
|
+
// }
|
|
152
|
+
//
|
|
153
|
+
// Logger.info(`开始获取飞书块内容,文档ID: ${documentId},块ID: ${blockId}`);
|
|
154
|
+
// const blockContent = await feishuService.getBlockContent(documentId, blockId);
|
|
155
|
+
// Logger.info(`飞书块内容获取成功,块类型: ${blockContent.block_type}`);
|
|
156
|
+
//
|
|
157
|
+
// return {
|
|
158
|
+
// content: [{ type: 'text', text: JSON.stringify(blockContent, null, 2) }],
|
|
159
|
+
// };
|
|
160
|
+
// } catch (error) {
|
|
161
|
+
// Logger.error(`获取飞书块内容失败:`, error);
|
|
162
|
+
// const errorMessage = formatErrorMessage(error);
|
|
163
|
+
// return {
|
|
164
|
+
// content: [{ type: 'text', text: `获取飞书块内容失败: ${errorMessage}` }],
|
|
165
|
+
// };
|
|
166
|
+
// }
|
|
167
|
+
// },
|
|
168
|
+
// );
|
|
137
169
|
// 添加搜索文档工具
|
|
138
170
|
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.', {
|
|
139
171
|
searchKey: SearchKeySchema,
|
|
@@ -163,4 +195,29 @@ export function registerFeishuTools(server, feishuService) {
|
|
|
163
195
|
};
|
|
164
196
|
}
|
|
165
197
|
});
|
|
198
|
+
// 添加获取画板内容工具
|
|
199
|
+
server.tool('get_feishu_whiteboard_content', 'Retrieves the content and structure of a Feishu whiteboard. This tool fetches all nodes (elements) from a whiteboard, including shapes, text, mind maps, and other graphical elements. Use this to analyze whiteboard content, extract information, or understand the structure of collaborative diagrams. The whiteboard ID can be obtained from the board.token field when getting document blocks with block_type: 43.', {
|
|
200
|
+
whiteboardId: WhiteboardIdSchema,
|
|
201
|
+
}, async ({ whiteboardId }) => {
|
|
202
|
+
try {
|
|
203
|
+
if (!feishuService) {
|
|
204
|
+
return {
|
|
205
|
+
content: [{ type: 'text', text: 'Feishu service is not initialized. Please check the configuration' }],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
Logger.info(`开始获取飞书画板内容,画板ID: ${whiteboardId}`);
|
|
209
|
+
const whiteboardContent = await feishuService.getWhiteboardContent(whiteboardId);
|
|
210
|
+
Logger.info(`飞书画板内容获取成功,节点数量: ${whiteboardContent.nodes?.length || 0}`);
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: 'text', text: JSON.stringify(whiteboardContent, null, 2) }],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
Logger.error(`获取飞书画板内容失败:`, error);
|
|
217
|
+
const errorMessage = formatErrorMessage(error);
|
|
218
|
+
return {
|
|
219
|
+
content: [{ type: 'text', text: `获取飞书画板内容失败: ${errorMessage}` }],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
});
|
|
166
223
|
}
|
|
@@ -90,14 +90,29 @@ export class BlockFactory {
|
|
|
90
90
|
return {
|
|
91
91
|
block_type: 2, // 2表示文本块
|
|
92
92
|
text: {
|
|
93
|
-
elements: textContents.map(content =>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
elements: textContents.map(content => {
|
|
94
|
+
// 检查是否是公式元素
|
|
95
|
+
if ('equation' in content) {
|
|
96
|
+
return {
|
|
97
|
+
equation: {
|
|
98
|
+
content: content.equation,
|
|
99
|
+
text_element_style: BlockFactory.applyDefaultTextStyle(content.style)
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// 普通文本元素
|
|
105
|
+
return {
|
|
106
|
+
text_run: {
|
|
107
|
+
content: content.text,
|
|
108
|
+
text_element_style: BlockFactory.applyDefaultTextStyle(content.style)
|
|
109
|
+
}
|
|
110
|
+
};
|
|
97
111
|
}
|
|
98
|
-
})
|
|
112
|
+
}),
|
|
99
113
|
style: {
|
|
100
114
|
align: align, // 1 居左,2 居中,3 居右
|
|
115
|
+
folded: false
|
|
101
116
|
}
|
|
102
117
|
}
|
|
103
118
|
};
|
|
@@ -221,7 +221,7 @@ export class FeishuApiService extends BaseApiService {
|
|
|
221
221
|
* 更新块文本内容
|
|
222
222
|
* @param documentId 文档ID或URL
|
|
223
223
|
* @param blockId 块ID
|
|
224
|
-
* @param textElements
|
|
224
|
+
* @param textElements 文本元素数组,支持普通文本和公式元素
|
|
225
225
|
* @returns 更新结果
|
|
226
226
|
*/
|
|
227
227
|
async updateBlockTextContent(documentId, blockId, textElements) {
|
|
@@ -229,12 +229,24 @@ export class FeishuApiService extends BaseApiService {
|
|
|
229
229
|
const docId = ParamUtils.processDocumentId(documentId);
|
|
230
230
|
const endpoint = `/docx/v1/documents/${docId}/blocks/${blockId}?document_revision_id=-1`;
|
|
231
231
|
Logger.debug(`准备请求API端点: ${endpoint}`);
|
|
232
|
-
const elements = textElements.map(item =>
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
const elements = textElements.map(item => {
|
|
233
|
+
if (item.equation !== undefined) {
|
|
234
|
+
return {
|
|
235
|
+
equation: {
|
|
236
|
+
content: item.equation,
|
|
237
|
+
text_element_style: BlockFactory.applyDefaultTextStyle(item.style)
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
return {
|
|
243
|
+
text_run: {
|
|
244
|
+
content: item.text || '',
|
|
245
|
+
text_element_style: BlockFactory.applyDefaultTextStyle(item.style)
|
|
246
|
+
}
|
|
247
|
+
};
|
|
236
248
|
}
|
|
237
|
-
})
|
|
249
|
+
});
|
|
238
250
|
const data = {
|
|
239
251
|
update_text_elements: {
|
|
240
252
|
elements: elements
|
|
@@ -305,17 +317,27 @@ export class FeishuApiService extends BaseApiService {
|
|
|
305
317
|
* 创建文本块
|
|
306
318
|
* @param documentId 文档ID或URL
|
|
307
319
|
* @param parentBlockId 父块ID
|
|
308
|
-
* @param textContents
|
|
320
|
+
* @param textContents 文本内容数组,支持普通文本和公式元素
|
|
309
321
|
* @param align 对齐方式,1为左对齐,2为居中,3为右对齐
|
|
310
322
|
* @param index 插入位置索引
|
|
311
323
|
* @returns 创建结果
|
|
312
324
|
*/
|
|
313
325
|
async createTextBlock(documentId, parentBlockId, textContents, align = 1, index = 0) {
|
|
314
|
-
//
|
|
315
|
-
const processedTextContents = textContents.map(item =>
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
326
|
+
// 处理文本内容样式,支持普通文本和公式元素
|
|
327
|
+
const processedTextContents = textContents.map(item => {
|
|
328
|
+
if (item.equation !== undefined) {
|
|
329
|
+
return {
|
|
330
|
+
equation: item.equation,
|
|
331
|
+
style: BlockFactory.applyDefaultTextStyle(item.style)
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
return {
|
|
336
|
+
text: item.text || '',
|
|
337
|
+
style: BlockFactory.applyDefaultTextStyle(item.style)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
});
|
|
319
341
|
const blockContent = this.blockFactory.createTextBlock({
|
|
320
342
|
textContents: processedTextContents,
|
|
321
343
|
align
|
|
@@ -501,12 +523,22 @@ export class FeishuApiService extends BaseApiService {
|
|
|
501
523
|
case BlockType.TEXT:
|
|
502
524
|
if ('text' in options && options.text) {
|
|
503
525
|
const textOptions = options.text;
|
|
504
|
-
//
|
|
526
|
+
// 处理文本样式,应用默认样式,支持普通文本和公式元素
|
|
505
527
|
const textStyles = textOptions.textStyles || [];
|
|
506
|
-
const processedTextStyles = textStyles.map((item) =>
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
528
|
+
const processedTextStyles = textStyles.map((item) => {
|
|
529
|
+
if (item.equation !== undefined) {
|
|
530
|
+
return {
|
|
531
|
+
equation: item.equation,
|
|
532
|
+
style: BlockFactory.applyDefaultTextStyle(item.style)
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
return {
|
|
537
|
+
text: item.text || '',
|
|
538
|
+
style: BlockFactory.applyDefaultTextStyle(item.style)
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
});
|
|
510
542
|
blockConfig.options = {
|
|
511
543
|
textContents: processedTextStyles,
|
|
512
544
|
align: textOptions.align || 1
|
|
@@ -566,12 +598,22 @@ export class FeishuApiService extends BaseApiService {
|
|
|
566
598
|
if ('text' in options) {
|
|
567
599
|
blockConfig.type = BlockType.TEXT;
|
|
568
600
|
const textOptions = options.text;
|
|
569
|
-
//
|
|
601
|
+
// 处理文本样式,应用默认样式,支持普通文本和公式元素
|
|
570
602
|
const textStyles = textOptions.textStyles || [];
|
|
571
|
-
const processedTextStyles = textStyles.map((item) =>
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
603
|
+
const processedTextStyles = textStyles.map((item) => {
|
|
604
|
+
if (item.equation !== undefined) {
|
|
605
|
+
return {
|
|
606
|
+
equation: item.equation,
|
|
607
|
+
style: BlockFactory.applyDefaultTextStyle(item.style)
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
return {
|
|
612
|
+
text: item.text || '',
|
|
613
|
+
style: BlockFactory.applyDefaultTextStyle(item.style)
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
});
|
|
575
617
|
blockConfig.options = {
|
|
576
618
|
textContents: processedTextStyles,
|
|
577
619
|
align: textOptions.align || 1
|
|
@@ -922,6 +964,32 @@ export class FeishuApiService extends BaseApiService {
|
|
|
922
964
|
return 'image/png'; // 默认PNG
|
|
923
965
|
}
|
|
924
966
|
}
|
|
967
|
+
/**
|
|
968
|
+
* 获取画板内容
|
|
969
|
+
* @param whiteboardId 画板ID或URL
|
|
970
|
+
* @returns 画板节点数据
|
|
971
|
+
*/
|
|
972
|
+
async getWhiteboardContent(whiteboardId) {
|
|
973
|
+
try {
|
|
974
|
+
// 从URL中提取画板ID
|
|
975
|
+
let normalizedWhiteboardId = whiteboardId;
|
|
976
|
+
if (whiteboardId.includes('feishu.cn/board/')) {
|
|
977
|
+
// 从URL中提取画板ID
|
|
978
|
+
const matches = whiteboardId.match(/board\/([^\/\?]+)/);
|
|
979
|
+
if (matches) {
|
|
980
|
+
normalizedWhiteboardId = matches[1];
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
const endpoint = `/board/v1/whiteboards/${normalizedWhiteboardId}/nodes`;
|
|
984
|
+
Logger.info(`开始获取画板内容,画板ID: ${normalizedWhiteboardId}`);
|
|
985
|
+
const response = await this.get(endpoint);
|
|
986
|
+
Logger.info(`画板内容获取成功,节点数量: ${response.nodes?.length || 0}`);
|
|
987
|
+
return response;
|
|
988
|
+
}
|
|
989
|
+
catch (error) {
|
|
990
|
+
this.handleApiError(error, '获取画板内容失败');
|
|
991
|
+
}
|
|
992
|
+
}
|
|
925
993
|
/**
|
|
926
994
|
* 从路径或URL获取图片的Base64编码
|
|
927
995
|
* @param imagePathOrUrl 图片路径或URL
|
|
@@ -16,6 +16,7 @@ export const IndexSchema = z.number().describe('Insertion position index (requir
|
|
|
16
16
|
'0 means to insert as the first content block after the title.\n' +
|
|
17
17
|
'If children is empty or missing, use 0 to insert the first content block.\n' +
|
|
18
18
|
'For nested blocks, index is relative to the parent block\'s children.\n' +
|
|
19
|
+
'**index must satisfy 0 ≤ index ≤ parentBlock.children.length, otherwise the API will return an error.**\n' +
|
|
19
20
|
'Note: The title block itself is not part of the children array and cannot be operated on with index.' +
|
|
20
21
|
'Specifies where the block should be inserted. Use 0 to insert at the beginning. ' +
|
|
21
22
|
'Use get_feishu_document_blocks tool to understand document structure if unsure. ' +
|
|
@@ -53,11 +54,17 @@ export const TextStylePropertiesSchema = {
|
|
|
53
54
|
};
|
|
54
55
|
// 文本样式对象定义
|
|
55
56
|
export const TextStyleSchema = z.object(TextStylePropertiesSchema).optional().describe('Text style settings. Explicitly set style properties instead of relying on Markdown syntax conversion.');
|
|
56
|
-
// 文本内容单元定义
|
|
57
|
-
export const TextElementSchema = z.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
// 文本内容单元定义 - 支持普通文本和公式元素
|
|
58
|
+
export const TextElementSchema = z.union([
|
|
59
|
+
z.object({
|
|
60
|
+
text: z.string().describe('Text content. Provide plain text without markdown syntax; use style object for formatting.'),
|
|
61
|
+
style: TextStyleSchema
|
|
62
|
+
}).describe('Regular text element with optional styling.'),
|
|
63
|
+
z.object({
|
|
64
|
+
equation: z.string().describe('Mathematical equation content. The formula or expression to display. Format: LaTeX.'),
|
|
65
|
+
style: TextStyleSchema
|
|
66
|
+
}).describe('Mathematical equation element with optional styling.')
|
|
67
|
+
]);
|
|
61
68
|
// 文本内容数组定义
|
|
62
69
|
export const TextElementsArraySchema = z.array(TextElementSchema).describe('Array of text content objects. A block can contain multiple text segments with different styles. Example: [{text:"Hello",style:{bold:true}},{text:" World",style:{italic:true}}]');
|
|
63
70
|
// 代码块语言参数定义
|
|
@@ -74,10 +81,7 @@ export const CodeLanguageSchema = z.number().optional().default(1).describe("Pro
|
|
|
74
81
|
export const CodeWrapSchema = z.boolean().optional().default(false).describe('Whether to enable automatic line wrapping. Default is false.');
|
|
75
82
|
// 文本样式段落定义 - 用于批量创建块工具
|
|
76
83
|
export const TextStyleBlockSchema = z.object({
|
|
77
|
-
textStyles: z.array(
|
|
78
|
-
text: z.string().describe('Text segment content. The actual text to display.'),
|
|
79
|
-
style: TextStyleSchema
|
|
80
|
-
})).describe('Array of text content objects with styles. A block can contain multiple text segments with different styles. Example: [{text:"Hello",style:{bold:true}},{text:" World",style:{italic:true}}]'),
|
|
84
|
+
textStyles: z.array(TextElementSchema).describe('Array of text content objects with styles. A block can contain multiple text segments with different styles, including both regular text and equations. Example: [{text:"Hello",style:{bold:true}},{equation:"1+2=3",style:{}}]'),
|
|
81
85
|
align: z.number().optional().default(1).describe('Text alignment: 1 for left (default), 2 for center, 3 for right.'),
|
|
82
86
|
});
|
|
83
87
|
// 代码块内容定义 - 用于批量创建块工具
|
|
@@ -101,11 +105,16 @@ export const ListBlockSchema = z.object({
|
|
|
101
105
|
// 块类型枚举 - 用于批量创建块工具
|
|
102
106
|
export const BlockTypeEnum = z.string().describe("Block type (required). Supports: 'text', 'code', 'heading', 'list', 'image', as well as 'heading1' through 'heading9'. " +
|
|
103
107
|
"For headings, we recommend using 'heading' with level property, but 'heading1'-'heading9' are also supported. " +
|
|
104
|
-
"For images, use 'image' to create empty image blocks that can be filled later."
|
|
108
|
+
"For images, use 'image' to create empty image blocks that can be filled later. " +
|
|
109
|
+
"For text blocks, you can include both regular text and equation elements in the same block.");
|
|
110
|
+
// 图片宽度参数定义
|
|
111
|
+
export const ImageWidthSchema = z.number().optional().describe('Image width in pixels (optional). If not provided, the original image width will be used.');
|
|
112
|
+
// 图片高度参数定义
|
|
113
|
+
export const ImageHeightSchema = z.number().optional().describe('Image height in pixels (optional). If not provided, the original image height will be used.');
|
|
105
114
|
// 图片块内容定义 - 用于批量创建块工具
|
|
106
115
|
export const ImageBlockSchema = z.object({
|
|
107
|
-
width:
|
|
108
|
-
height:
|
|
116
|
+
width: ImageWidthSchema,
|
|
117
|
+
height: ImageHeightSchema
|
|
109
118
|
});
|
|
110
119
|
// 块配置定义 - 用于批量创建块工具
|
|
111
120
|
export const BlockConfigSchema = z.object({
|
|
@@ -141,7 +150,15 @@ export const ImagePathOrUrlSchema = z.string().describe('Image path or URL (requ
|
|
|
141
150
|
// 图片文件名参数定义
|
|
142
151
|
export const ImageFileNameSchema = z.string().optional().describe('Image file name (optional). If not provided, a default name will be generated based on the source. ' +
|
|
143
152
|
'Should include the file extension, e.g., "image.png" or "photo.jpg".');
|
|
144
|
-
//
|
|
145
|
-
export const
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
// 批量图片上传绑定参数定义
|
|
154
|
+
export const ImagesArraySchema = z.array(z.object({
|
|
155
|
+
blockId: BlockIdSchema,
|
|
156
|
+
imagePathOrUrl: ImagePathOrUrlSchema,
|
|
157
|
+
fileName: ImageFileNameSchema.optional(),
|
|
158
|
+
})).describe('Array of image binding objects (required). Each object must include: blockId (target image block ID), imagePathOrUrl (local path or URL of the image), and optionally fileName (image file name, e.g., "image.png").');
|
|
159
|
+
// 画板ID参数定义
|
|
160
|
+
export const WhiteboardIdSchema = z.string().describe('Whiteboard ID (required). This is the token value from the board.token field when getting document blocks.\n' +
|
|
161
|
+
'When you find a block with block_type: 43, the whiteboard ID is located in board.token field.\n' +
|
|
162
|
+
'Example: "EPJKwvY5ghe3pVbKj9RcT2msnBX"');
|
|
163
|
+
// 文档标题参数定义
|
|
164
|
+
export const DocumentTitleSchema = z.string().describe('Document title (required). This will be displayed in the Feishu document list and document header.');
|
package/package.json
CHANGED
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "feishu-mcp",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Model Context Protocol server for Feishu integration",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"feishu-mcp": "./dist/cli.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist",
|
|
12
|
-
"README.md"
|
|
13
|
-
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsc && tsc-alias",
|
|
16
|
-
"type-check": "tsc --noEmit",
|
|
17
|
-
"start": "node dist/index.js",
|
|
18
|
-
"start:cli": "cross-env NODE_ENV=cli node dist/index.js",
|
|
19
|
-
"start:http": "node dist/index.js",
|
|
20
|
-
"dev": "cross-env NODE_ENV=development tsx watch src/index.ts",
|
|
21
|
-
"dev:cli": "cross-env NODE_ENV=development tsx watch src/index.ts --stdio",
|
|
22
|
-
"lint": "eslint . --ext .ts",
|
|
23
|
-
"format": "prettier --write \"src/**/*.ts\"",
|
|
24
|
-
"inspect": "pnpx @modelcontextprotocol/inspector",
|
|
25
|
-
"prepare": "pnpm run build",
|
|
26
|
-
"pub:release": "pnpm build && npm publish"
|
|
27
|
-
},
|
|
28
|
-
"engines": {
|
|
29
|
-
"node": "^20.17.0"
|
|
30
|
-
},
|
|
31
|
-
"repository": {
|
|
32
|
-
"type": "git",
|
|
33
|
-
"url": "https://github.com/cso1z/Feishu-MCP.git"
|
|
34
|
-
},
|
|
35
|
-
"keywords": [
|
|
36
|
-
"feishu",
|
|
37
|
-
"lark",
|
|
38
|
-
"mcp",
|
|
39
|
-
"typescript"
|
|
40
|
-
],
|
|
41
|
-
"author": "cso1z",
|
|
42
|
-
"license": "MIT",
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"@modelcontextprotocol/sdk": "^1.13.1",
|
|
45
|
-
"@types/yargs": "^17.0.33",
|
|
46
|
-
"axios": "^1.7.9",
|
|
47
|
-
"cross-env": "^7.0.3",
|
|
48
|
-
"dotenv": "^16.4.7",
|
|
49
|
-
"express": "^4.21.2",
|
|
50
|
-
"form-data": "^4.0.3",
|
|
51
|
-
"remeda": "^2.20.1",
|
|
52
|
-
"yargs": "^17.7.2",
|
|
53
|
-
"zod": "^3.24.2"
|
|
54
|
-
},
|
|
55
|
-
"devDependencies": {
|
|
56
|
-
"@types/express": "^5.0.0",
|
|
57
|
-
"@types/jest": "^29.5.11",
|
|
58
|
-
"@types/node": "^20.17.0",
|
|
59
|
-
"@typescript-eslint/eslint-plugin": "^8.24.0",
|
|
60
|
-
"@typescript-eslint/parser": "^8.24.0",
|
|
61
|
-
"eslint": "^9.20.1",
|
|
62
|
-
"eslint-config-prettier": "^10.0.1",
|
|
63
|
-
"jest": "^29.7.0",
|
|
64
|
-
"prettier": "^3.5.0",
|
|
65
|
-
"ts-jest": "^29.2.5",
|
|
66
|
-
"tsc-alias": "^1.8.10",
|
|
67
|
-
"tsx": "^4.19.2",
|
|
68
|
-
"typescript": "^5.7.3"
|
|
69
|
-
},
|
|
70
|
-
"pnpm": {
|
|
71
|
-
"overrides": {
|
|
72
|
-
"feishu-mcp": "link:"
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "feishu-mcp",
|
|
3
|
+
"version": "0.0.15",
|
|
4
|
+
"description": "Model Context Protocol server for Feishu integration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"feishu-mcp": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc && tsc-alias",
|
|
16
|
+
"type-check": "tsc --noEmit",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"start:cli": "cross-env NODE_ENV=cli node dist/index.js",
|
|
19
|
+
"start:http": "node dist/index.js",
|
|
20
|
+
"dev": "cross-env NODE_ENV=development tsx watch src/index.ts",
|
|
21
|
+
"dev:cli": "cross-env NODE_ENV=development tsx watch src/index.ts --stdio",
|
|
22
|
+
"lint": "eslint . --ext .ts",
|
|
23
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
24
|
+
"inspect": "pnpx @modelcontextprotocol/inspector",
|
|
25
|
+
"prepare": "pnpm run build",
|
|
26
|
+
"pub:release": "pnpm build && npm publish"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": "^20.17.0"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/cso1z/Feishu-MCP.git"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"feishu",
|
|
37
|
+
"lark",
|
|
38
|
+
"mcp",
|
|
39
|
+
"typescript"
|
|
40
|
+
],
|
|
41
|
+
"author": "cso1z",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.13.1",
|
|
45
|
+
"@types/yargs": "^17.0.33",
|
|
46
|
+
"axios": "^1.7.9",
|
|
47
|
+
"cross-env": "^7.0.3",
|
|
48
|
+
"dotenv": "^16.4.7",
|
|
49
|
+
"express": "^4.21.2",
|
|
50
|
+
"form-data": "^4.0.3",
|
|
51
|
+
"remeda": "^2.20.1",
|
|
52
|
+
"yargs": "^17.7.2",
|
|
53
|
+
"zod": "^3.24.2"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/express": "^5.0.0",
|
|
57
|
+
"@types/jest": "^29.5.11",
|
|
58
|
+
"@types/node": "^20.17.0",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^8.24.0",
|
|
60
|
+
"@typescript-eslint/parser": "^8.24.0",
|
|
61
|
+
"eslint": "^9.20.1",
|
|
62
|
+
"eslint-config-prettier": "^10.0.1",
|
|
63
|
+
"jest": "^29.7.0",
|
|
64
|
+
"prettier": "^3.5.0",
|
|
65
|
+
"ts-jest": "^29.2.5",
|
|
66
|
+
"tsc-alias": "^1.8.10",
|
|
67
|
+
"tsx": "^4.19.2",
|
|
68
|
+
"typescript": "^5.7.3"
|
|
69
|
+
},
|
|
70
|
+
"pnpm": {
|
|
71
|
+
"overrides": {
|
|
72
|
+
"feishu-mcp": "link:"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|