feishu-mcp 0.2.2 → 0.2.7

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.
Files changed (77) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +356 -266
  3. package/dist/cli/auth.js +117 -0
  4. package/dist/cli/commands/auth.js +141 -0
  5. package/dist/cli/commands/config.js +86 -0
  6. package/dist/cli/commands/guide.js +68 -0
  7. package/dist/cli/dispatcher.js +96 -0
  8. package/dist/cli/index.js +95 -0
  9. package/dist/manager/sseConnectionManager.js +2 -0
  10. package/dist/mcp/feishuMcp.js +25 -25
  11. package/dist/mcp/tools/blockTools.js +295 -0
  12. package/dist/mcp/tools/documentTools.js +105 -0
  13. package/dist/mcp/tools/feishuBlockTools.js +1 -285
  14. package/dist/mcp/tools/feishuTools.js +1 -67
  15. package/dist/mcp/tools/folderTools.js +99 -0
  16. package/dist/mcp/tools/toolHelpers.js +155 -0
  17. package/dist/modules/FeatureModule.js +1 -0
  18. package/dist/modules/ModuleRegistry.js +63 -0
  19. package/dist/modules/calendar/index.js +11 -0
  20. package/dist/modules/calendar/services/FeishuCalendarService.js +6 -0
  21. package/dist/modules/calendar/tools/calendarTools.js +6 -0
  22. package/dist/modules/document/index.js +15 -0
  23. package/dist/modules/document/services/FeishuBlockService.js +410 -0
  24. package/dist/modules/document/services/FeishuDocumentService.js +110 -0
  25. package/dist/modules/document/services/FeishuFoldService.js +187 -0
  26. package/dist/modules/document/services/FeishuSearchService.js +232 -0
  27. package/dist/modules/document/services/FeishuWhiteboardService.js +80 -0
  28. package/dist/modules/document/services/blockFactory.js +521 -0
  29. package/dist/modules/document/toolApi/blockToolApi.js +160 -0
  30. package/dist/modules/document/toolApi/documentToolApi.js +65 -0
  31. package/dist/modules/document/toolApi/folderToolApi.js +73 -0
  32. package/dist/modules/document/toolApi/index.js +3 -0
  33. package/dist/modules/document/tools/blockTools.js +138 -0
  34. package/dist/modules/document/tools/documentTools.js +64 -0
  35. package/dist/modules/document/tools/folderTools.js +46 -0
  36. package/dist/modules/document/tools/toolHelpers.js +155 -0
  37. package/dist/modules/index.js +5 -0
  38. package/dist/modules/member/index.js +11 -0
  39. package/dist/modules/member/services/FeishuMemberService.js +41 -0
  40. package/dist/modules/member/toolApi/index.js +1 -0
  41. package/dist/modules/member/toolApi/memberToolApi.js +54 -0
  42. package/dist/modules/member/tools/memberTools.js +26 -0
  43. package/dist/modules/task/index.js +11 -0
  44. package/dist/modules/task/services/FeishuTaskService.js +271 -0
  45. package/dist/modules/task/toolApi/__tests__/taskToolApi.test.js +98 -0
  46. package/dist/modules/task/toolApi/index.js +1 -0
  47. package/dist/modules/task/toolApi/taskToolApi.js +128 -0
  48. package/dist/modules/task/tools/taskTools.js +93 -0
  49. package/dist/server.js +43 -24
  50. package/dist/services/baseService.js +11 -2
  51. package/dist/services/blockFactory.js +167 -0
  52. package/dist/services/callbackService.js +1 -1
  53. package/dist/services/constants/feishuScopes.js +94 -0
  54. package/dist/services/feishu/FeishuBaseApiService.js +47 -0
  55. package/dist/services/feishu/FeishuBlockService.js +410 -0
  56. package/dist/services/feishu/FeishuDocumentBlockService.js +403 -0
  57. package/dist/services/feishu/FeishuDocumentService.js +110 -0
  58. package/dist/services/feishu/FeishuDriveService.js +62 -0
  59. package/dist/services/feishu/FeishuFoldService.js +187 -0
  60. package/dist/services/feishu/FeishuImageService.js +219 -0
  61. package/dist/services/feishu/FeishuScopeValidator.js +177 -0
  62. package/dist/services/feishu/FeishuSearchService.js +232 -0
  63. package/dist/services/feishu/FeishuWhiteboardService.js +80 -0
  64. package/dist/services/feishu/FeishuWikiService.js +134 -0
  65. package/dist/services/feishuApiService.js +246 -1760
  66. package/dist/services/feishuAuthService.js +43 -0
  67. package/dist/types/documentSchema.js +232 -0
  68. package/dist/types/feishuSchema.js +54 -77
  69. package/dist/types/memberSchema.js +35 -0
  70. package/dist/types/taskSchema.js +166 -0
  71. package/dist/utils/auth/tokenCacheManager.js +16 -3
  72. package/dist/utils/auth/tokenRefreshManager.js +2 -1
  73. package/dist/utils/config.js +47 -0
  74. package/dist/utils/document.js +116 -116
  75. package/dist/utils/error.js +0 -11
  76. package/dist/utils/paramUtils.js +0 -31
  77. package/package.json +77 -76
@@ -109,6 +109,49 @@ export class AuthService {
109
109
  throw new Error('刷新用户访问令牌失败');
110
110
  }
111
111
  }
112
+ /**
113
+ * 获取租户访问令牌
114
+ * 从缓存中获取或请求新的租户token
115
+ * @param appId 应用ID
116
+ * @param appSecret 应用密钥
117
+ * @param clientKey 客户端缓存键
118
+ * @returns 租户访问令牌
119
+ */
120
+ async getTenantAccessToken(appId, appSecret, clientKey) {
121
+ const tokenCacheManager = TokenCacheManager.getInstance();
122
+ const cachedToken = tokenCacheManager.getTenantToken(clientKey);
123
+ if (cachedToken) {
124
+ Logger.debug('使用缓存的租户访问令牌');
125
+ return cachedToken;
126
+ }
127
+ Logger.info('缓存中没有租户token,请求新的租户访问令牌');
128
+ try {
129
+ const requestData = { app_id: appId, app_secret: appSecret };
130
+ const url = 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal';
131
+ const headers = { 'Content-Type': 'application/json' };
132
+ Logger.debug('请求租户访问令牌:', url, requestData);
133
+ const response = await axios.post(url, requestData, { headers });
134
+ const data = response.data;
135
+ if (data.code !== 0) {
136
+ throw new Error(`获取租户访问令牌失败:${data.msg || '未知错误'} (错误码: ${data.code})`);
137
+ }
138
+ if (!data.tenant_access_token) {
139
+ throw new Error('获取租户访问令牌失败:响应中没有token');
140
+ }
141
+ const expire_at = Math.floor(Date.now() / 1000) + (data.expire || 0);
142
+ const tokenInfo = {
143
+ app_access_token: data.tenant_access_token,
144
+ expires_at: expire_at
145
+ };
146
+ tokenCacheManager.cacheTenantToken(clientKey, tokenInfo, data.expire);
147
+ Logger.info('租户访问令牌获取并缓存成功');
148
+ return data.tenant_access_token;
149
+ }
150
+ catch (error) {
151
+ Logger.error('获取租户访问令牌失败:', error);
152
+ throw new Error('获取租户访问令牌失败: ' + (error instanceof Error ? error.message : String(error)));
153
+ }
154
+ }
112
155
  /**
113
156
  * 获取用户访问令牌
114
157
  * 检查token状态,如果有效则返回缓存的token,如果过期则尝试刷新
@@ -0,0 +1,232 @@
1
+ import { z } from 'zod';
2
+ // 文档类型枚举(用于 get_feishu_document_info)
3
+ export const DocumentTypeSchema = z.enum(['document', 'wiki']).optional().describe('Document type (optional). "document" for regular document, "wiki" for Wiki document.');
4
+ // 文档ID或URL参数定义(仅支持普通文档)
5
+ export const DocumentIdSchema = z.string().describe('Document ID or URL (required). Supports the following formats:\n' +
6
+ '1. Standard document URL: https://xxx.feishu.cn/docs/xxx or https://xxx.feishu.cn/docx/xxx\n' +
7
+ '2. Direct document ID: e.g., JcKbdlokYoPIe0xDzJ1cduRXnRf');
8
+ // 文档ID或Wiki ID参数定义(用于 get_feishu_document_info,支持普通文档和Wiki文档)
9
+ export const DocumentIdOrWikiIdSchema = z.string().describe('Document ID, URL, or Wiki ID/URL (required). Supports regular document formats (https://xxx.feishu.cn/docx/xxx or direct ID) and Wiki formats (https://xxx.feishu.cn/wiki/xxxxx or Wiki token).');
10
+ // 父块ID参数定义
11
+ export const ParentBlockIdSchema = z.string().describe('Parent block ID (required). The block ID whose direct children will be operated on, without any URL prefix. ' +
12
+ 'For root-level operations, use the document ID as parentBlockId.');
13
+ // 块ID参数定义
14
+ export const BlockIdSchema = z.string().describe('Block ID (required). The ID of the target block.');
15
+ // 插入位置索引参数定义
16
+ export const IndexSchema = z.number().int().min(0).describe('Insertion position within the direct children of `parentBlockId` (0-based, title block excluded).\n' +
17
+ '0 = before the first content block; max = children.length (append after last block).\n' +
18
+ 'Example: document has 3 blocks → valid values are 0, 1, 2, 3.');
19
+ // 起始位置索引参数定义
20
+ export const StartIndexSchema = z.number().int().min(0).describe('Start of the target range within the direct children of `parentBlockId` (0-based, title block excluded).\n' +
21
+ '0 = first content block; max = children.length - 1.\n' +
22
+ 'Example: to target blocks at positions 2, 3, 4 → startIndex=2.');
23
+ // 结束位置索引参数定义
24
+ export const EndIndexSchema = z.number().int().min(1).describe('End of the target range (exclusive) within the direct children of `parentBlockId` (title block excluded).\n' +
25
+ 'Must satisfy endIndex > startIndex.\n' +
26
+ 'Example: to delete blocks at positions 2, 3, 4 → startIndex=2, endIndex=5; single block at 2 → startIndex=2, endIndex=3.');
27
+ // 文本对齐方式参数定义(带验证)
28
+ export const AlignSchemaWithValidation = z.number().optional().default(1).refine(val => val === 1 || val === 2 || val === 3, { message: "Alignment must be one of: 1 (left), 2 (center), or 3 (right)" }).describe('Text alignment (optional): 1 for left (default), 2 for center, 3 for right. Only these three values are allowed.');
29
+ // 文本样式属性定义
30
+ export const TextStylePropertiesSchema = {
31
+ bold: z.boolean().optional().describe('Whether to make text bold. Default is false, equivalent to **text** in Markdown.'),
32
+ italic: z.boolean().optional().describe('Whether to make text italic. Default is false, equivalent to *text* in Markdown.'),
33
+ underline: z.boolean().optional().describe('Whether to add underline. Default is false.'),
34
+ strikethrough: z.boolean().optional().describe('Whether to add strikethrough. Default is false, equivalent to ~~text~~ in Markdown.'),
35
+ inline_code: z.boolean().optional().describe('Whether to format as inline code. Default is false, equivalent to `code` in Markdown.'),
36
+ text_color: z.number().optional().refine(val => !val || (val >= 0 && val <= 7), {
37
+ message: "Text color must be between 0 and 7 inclusive"
38
+ }).describe('Text color (optional): 0=black (default), 1=gray, 2=brown, 3=orange, 4=yellow, 5=green, 6=blue, 7=purple.'),
39
+ background_color: z.number().optional().refine(val => !val || (val >= 1 && val <= 7), {
40
+ message: "Background color must be between 1 and 7 inclusive"
41
+ }).describe('Background color (optional): 1=gray, 2=brown, 3=orange, 4=yellow, 5=green, 6=blue, 7=purple.')
42
+ };
43
+ // 文本样式对象定义
44
+ export const TextStyleSchema = z.object(TextStylePropertiesSchema).optional().describe('Text style settings (optional). Omit entirely for unstyled text; only set properties that differ from the default (false).');
45
+ // 文本内容单元定义 - 支持普通文本和公式元素
46
+ export const TextElementSchema = z.union([
47
+ z.object({
48
+ text: z.string().describe('Text content. Provide plain text without markdown syntax; use style object for formatting.'),
49
+ style: TextStyleSchema
50
+ }).describe('Regular text element with optional styling.'),
51
+ z.object({
52
+ equation: z.string().describe('Mathematical equation content. The formula or expression to display. Format: LaTeX.'),
53
+ style: TextStyleSchema
54
+ }).describe('Mathematical equation element with optional styling.')
55
+ ]);
56
+ // 文本内容数组定义
57
+ 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}}]');
58
+ // 单块文本更新项定义
59
+ export const BlockTextUpdateItemSchema = z.object({
60
+ blockId: z.string().describe('Block ID (required). The ID of the block to update.'),
61
+ textElements: TextElementsArraySchema,
62
+ }).describe('A single block text update item: target blockId and its new text content.');
63
+ // 批量更新块文本数组定义
64
+ export const BlockTextUpdatesArraySchema = z.array(BlockTextUpdateItemSchema).min(1).describe('Array of block text update items (required, at least 1). ' +
65
+ 'Example: [{blockId:"xxx",textElements:[{text:"Hello",style:{bold:true}}]},{blockId:"yyy",textElements:[{text:"World"}]}]');
66
+ // 代码块语言参数定义
67
+ export const CodeLanguageSchema = z.number().optional().default(1).describe("Programming language code (optional). Common language codes:\n" +
68
+ "1: PlainText; 2: ABAP; 3: Ada; 4: Apache; 5: Apex; 6: Assembly; 7: Bash; 8: CSharp; 9: C++; 10: C; " +
69
+ "11: COBOL; 12: CSS; 13: CoffeeScript; 14: D; 15: Dart; 16: Delphi; 17: Django; 18: Dockerfile; 19: Erlang; 20: Fortran; " +
70
+ "22: Go; 23: Groovy; 24: HTML; 25: HTMLBars; 26: HTTP; 27: Haskell; 28: JSON; 29: Java; 30: JavaScript; " +
71
+ "31: Julia; 32: Kotlin; 33: LateX; 34: Lisp; 36: Lua; 37: MATLAB; 38: Makefile; 39: Markdown; 40: Nginx; " +
72
+ "41: Objective-C; 43: PHP; 44: Perl; 46: PowerShell; 47: Prolog; 48: ProtoBuf; 49: Python; 50: R; " +
73
+ "52: Ruby; 53: Rust; 54: SAS; 55: SCSS; 56: SQL; 57: Scala; 58: Scheme; 60: Shell; 61: Swift; 62: Thrift; " +
74
+ "63: TypeScript; 64: VBScript; 65: Visual Basic; 66: XML; 67: YAML; 68: CMake; 69: Diff; 70: Gherkin; 71: GraphQL. " +
75
+ "Default is 1 (PlainText).");
76
+ // 代码块自动换行参数定义
77
+ export const CodeWrapSchema = z.boolean().optional().default(false).describe('Whether to enable automatic line wrapping. Default is false.');
78
+ // 文本样式段落定义 - 用于批量创建块工具
79
+ export const TextStyleBlockSchema = z.object({
80
+ 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
+ align: z.number().optional().default(1).describe('Text alignment: 1 for left (default), 2 for center, 3 for right.'),
82
+ });
83
+ // 代码块内容定义 - 用于批量创建块工具
84
+ export const CodeBlockSchema = z.object({
85
+ code: z.string().describe('Code content. The complete code text to display.'),
86
+ language: CodeLanguageSchema,
87
+ wrap: CodeWrapSchema,
88
+ });
89
+ // 标题块内容定义 - 用于批量创建块工具
90
+ export const HeadingBlockSchema = z.object({
91
+ level: z.number().min(1).max(9).describe('Heading level from 1 to 9, where 1 is the largest (h1) and 9 is the smallest (h9).'),
92
+ content: z.string().describe('Heading text content.'),
93
+ align: AlignSchemaWithValidation,
94
+ });
95
+ // 列表块内容定义 - 用于批量创建块工具
96
+ export const ListBlockSchema = z.object({
97
+ content: z.string().describe('List item content. The actual text of the list item.'),
98
+ isOrdered: z.boolean().optional().default(false).describe('Whether this is an ordered (numbered) list item. Default is false (bullet point/unordered).'),
99
+ align: AlignSchemaWithValidation,
100
+ });
101
+ // 块类型枚举 - 用于批量创建块工具
102
+ export const BlockTypeEnum = z.enum([
103
+ 'text', 'code', 'heading', 'list', 'image', 'mermaid', 'whiteboard',
104
+ ]).describe("Block type. 'image' and 'whiteboard' create empty blocks to be filled after creation.");
105
+ // 图片宽度参数定义
106
+ export const ImageWidthSchema = z.number().optional().describe('Image width in pixels (optional). If not provided, the original image width will be used.');
107
+ // 图片高度参数定义
108
+ export const ImageHeightSchema = z.number().optional().describe('Image height in pixels (optional). If not provided, the original image height will be used.');
109
+ // 图片块内容定义 - 用于批量创建块工具
110
+ export const ImageBlockSchema = z.object({
111
+ width: ImageWidthSchema,
112
+ height: ImageHeightSchema
113
+ });
114
+ // Mermaid代码参数定义
115
+ export const MermaidCodeSchema = z.string().describe('Mermaid chart code (required). Node labels containing special characters ((), [], -->) must be wrapped in double quotes.\n' +
116
+ 'Example: A["finish()/返回键"] --> B');
117
+ export const MermaidBlockSchema = z.object({
118
+ code: MermaidCodeSchema,
119
+ });
120
+ // 画板对齐方式参数定义
121
+ export const WhiteboardAlignSchema = z.number().optional().default(2).describe('Whiteboard alignment: 1 for left, 2 for center (default), 3 for right.');
122
+ // 画板块内容定义 - 用于批量创建块工具
123
+ export const WhiteboardBlockSchema = z.object({
124
+ align: WhiteboardAlignSchema,
125
+ });
126
+ // 块配置定义 - 用于批量创建块工具
127
+ export const BlockConfigSchema = z.object({
128
+ blockType: BlockTypeEnum,
129
+ options: z.union([
130
+ z.object({ text: TextStyleBlockSchema }).describe("Text block options. Used when blockType is 'text'."),
131
+ z.object({ code: CodeBlockSchema }).describe("Code block options. Used when blockType is 'code'."),
132
+ z.object({ heading: HeadingBlockSchema }).describe("Heading block options. Used with both 'heading' and 'headingN' formats."),
133
+ z.object({ list: ListBlockSchema }).describe("List block options. Used when blockType is 'list'."),
134
+ z.object({ image: ImageBlockSchema }).describe("Image block options. Used when blockType is 'image'. Creates empty image blocks."),
135
+ z.object({ mermaid: MermaidBlockSchema }).describe("Mermaid block options. Used when blockType is 'mermaid'."),
136
+ z.object({ whiteboard: WhiteboardBlockSchema }).describe("Whiteboard block options. Used when blockType is 'whiteboard'. Creates an empty whiteboard; response includes board.token — pass it as whiteboardId to fill_whiteboard_with_plantuml."),
137
+ ]).describe('Options for the block type. Key must match blockType (e.g., blockType:"text" → options:{text:{...}}).'),
138
+ });
139
+ // 表格列数参数定义
140
+ export const TableColumnSizeSchema = z.number().min(1).describe('Table column size (required). The number of columns in the table. Must be at least 1.');
141
+ // 表格行数参数定义
142
+ export const TableRowSizeSchema = z.number().min(1).describe('Table row size (required). The number of rows in the table. Must be at least 1.');
143
+ // 表格单元格坐标参数定义
144
+ export const TableCellCoordinateSchema = z.object({
145
+ row: z.number().min(0).describe('Row coordinate (0-based). The row position of the cell in the table.'),
146
+ column: z.number().min(0).describe('Column coordinate (0-based). The column position of the cell in the table.')
147
+ });
148
+ // 表格单元格内容配置定义
149
+ export const TableCellContentSchema = z.object({
150
+ coordinate: TableCellCoordinateSchema,
151
+ content: BlockConfigSchema
152
+ });
153
+ // 表格创建参数定义 - 专门用于创建表格块工具
154
+ export const TableCreateSchema = z.object({
155
+ columnSize: TableColumnSizeSchema,
156
+ rowSize: TableRowSizeSchema,
157
+ cells: z.array(TableCellContentSchema).optional().describe('Array of cell configurations (optional). Each cell specifies its position (row, column) and content block configuration. ' +
158
+ 'If not provided, empty text blocks will be created for all cells. ' +
159
+ 'Multiple entries with the same coordinates add content blocks sequentially to that cell. ' +
160
+ 'Example: [{coordinate:{row:0,column:0}, content:{blockType:"text", options:{text:{textStyles:[{text:"Header"}]}}}}, ' +
161
+ '{coordinate:{row:0,column:0}, content:{blockType:"code", options:{code:{code:"console.log(\'hello\')", language:30}}}}] ' +
162
+ 'adds both a text block and a code block to cell (0,0).')
163
+ });
164
+ // 媒体ID参数定义
165
+ export const MediaIdSchema = z.string().describe('Image media token (required). Obtain from one of:\n' +
166
+ '1. An image block returned by get_feishu_document_blocks: block.image.token (block_type=27)\n' +
167
+ '2. Response of upload_and_bind_image_to_block: fileToken field');
168
+ // 额外参数定义 - 用于媒体资源下载
169
+ export const MediaExtraSchema = z.string().optional().describe('Extra query parameter for the Feishu media download API (optional). Required only for encrypted images; omit for standard image blocks.');
170
+ // 文件夹Token参数定义(必传)
171
+ export const FolderTokenSchema = z.string().describe('Folder token (required). The unique identifier for a folder in Feishu. ' +
172
+ 'Format is an alphanumeric string like "FWK2fMleClICfodlHHWc4Mygnhb".');
173
+ // 文件夹Token参数定义(可选,用于文档创建、获取文件列表等场景)
174
+ export const FolderTokenOptionalSchema = z.string().optional().describe('Folder token (optional, for Feishu Drive folder mode). The unique identifier for a folder in Feishu Drive. ' +
175
+ 'Format is an alphanumeric string like "FWK2fMleClICfodlHHWc4Mygnhb".');
176
+ // 文件夹名称参数定义
177
+ export const FolderNameSchema = z.string().describe('Folder name (required). The name for the new folder to be created.');
178
+ // 知识空间ID参数定义
179
+ export const SpaceIdSchema = z.string().describe('Space ID. Required when using wikiContext mode. The unique identifier for a wiki space in Feishu. ' +
180
+ 'Can be obtained from get_feishu_root_folder_info (wiki_spaces array or my_library.space_id). ' +
181
+ 'Format is typically like "74812***88644".');
182
+ // 父节点Token参数定义
183
+ export const ParentNodeTokenSchema = z.string().optional().describe('Parent node token (optional). The token of the parent node in a wiki space. ' +
184
+ 'If not provided, retrieves nodes from the root of the wiki space. ' +
185
+ 'Format is typically like "PdDWwIHD6****MhcIOY7npg".');
186
+ // 知识库节点上下文参数定义(包装 spaceId 和 parentNodeToken)
187
+ export const WikiSpaceNodeContextSchema = z.object({
188
+ spaceId: SpaceIdSchema,
189
+ parentNodeToken: ParentNodeTokenSchema,
190
+ }).optional().describe('Wiki space node context object (optional). Provide this instead of folderToken to operate in wiki space mode. ' +
191
+ 'spaceId is required; parentNodeToken is optional (omit to target the wiki root).');
192
+ // 搜索关键字参数定义
193
+ export const SearchKeySchema = z.string().describe('Search keyword (required). The keyword to search for in documents.');
194
+ // 搜索类型枚举
195
+ export const SearchTypeSchema = z.enum(['document', 'wiki', 'both']).optional().default('both').describe('Search type (optional, default: "both"). "document": only documents, "wiki": only wiki nodes, "both": both (default)');
196
+ // 知识库分页token参数定义
197
+ export const PageTokenSchema = z.string().optional().describe('Wiki page token (optional). Token from previous wiki search result for pagination. Only needed when fetching next page of wiki results.');
198
+ // 文档分页偏移量参数定义
199
+ export const OffsetSchema = z.number().optional().describe('Document offset (optional). Offset for document search pagination. Only needed when fetching next page of document results.');
200
+ // 图片路径或URL参数定义
201
+ export const ImagePathOrUrlSchema = z.string().describe('Image path or URL (required).\n' +
202
+ '1. Local absolute path: e.g., "C:\\path\\to\\image.jpg"\n' +
203
+ '2. HTTP/HTTPS URL: e.g., "https://example.com/image.png"');
204
+ // 图片文件名参数定义
205
+ export const ImageFileNameSchema = z.string().optional().describe('Image file name (optional). If not provided, a default name will be generated based on the source. ' +
206
+ 'Should include the file extension, e.g., "image.png" or "photo.jpg".');
207
+ // 批量图片上传绑定参数定义
208
+ export const ImagesArraySchema = z.array(z.object({
209
+ blockId: BlockIdSchema,
210
+ imagePathOrUrl: ImagePathOrUrlSchema,
211
+ fileName: ImageFileNameSchema.optional(),
212
+ })).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").');
213
+ // 画板ID参数定义
214
+ export const WhiteboardIdSchema = z.string().describe('Whiteboard token (required). Found in board.token of a block with block_type=43.\nExample: "EPJKwvY5ghe3pVbKj9RcT2msnBX"');
215
+ // 画板代码参数定义(支持 PlantUML 和 Mermaid)
216
+ export const WhiteboardCodeSchema = z.string().describe('Diagram code (required). The complete diagram code to create in the whiteboard.\n' +
217
+ 'Supports both PlantUML and Mermaid formats.\n' +
218
+ 'PlantUML example: "@startuml\nAlice -> Bob: Hello\n@enduml"\n' +
219
+ 'Mermaid example: "graph TD\nA[Start] --> B[End]"');
220
+ // 语法类型参数定义
221
+ export const SyntaxTypeSchema = z.enum(['plantuml', 'mermaid']).describe('Diagram syntax type (required): "plantuml" or "mermaid".');
222
+ // 画板内容配置定义(包含画板ID和内容配置)
223
+ export const WhiteboardContentSchema = z.object({
224
+ whiteboardId: WhiteboardIdSchema,
225
+ code: WhiteboardCodeSchema,
226
+ syntax_type: SyntaxTypeSchema,
227
+ }).describe('Whiteboard fill item: target whiteboard ID, diagram code, and syntax type.');
228
+ // 批量填充画板数组定义
229
+ export const WhiteboardFillArraySchema = z.array(WhiteboardContentSchema).describe('Array of whiteboard fill items (required).\n' +
230
+ 'Example: [{whiteboardId:"token1", code:"@startuml\\nAlice -> Bob: Hi\\n@enduml", syntax_type:"plantuml"}, {whiteboardId:"token2", code:"graph TD\\nA-->B", syntax_type:"mermaid"}]');
231
+ // 文档标题参数定义
232
+ export const DocumentTitleSchema = z.string().describe('Document title (required). This will be displayed in the Feishu document list and document header.');
@@ -8,37 +8,22 @@ export const DocumentIdSchema = z.string().describe('Document ID or URL (require
8
8
  // 文档ID或Wiki ID参数定义(用于 get_feishu_document_info,支持普通文档和Wiki文档)
9
9
  export const DocumentIdOrWikiIdSchema = z.string().describe('Document ID, URL, or Wiki ID/URL (required). Supports regular document formats (https://xxx.feishu.cn/docx/xxx or direct ID) and Wiki formats (https://xxx.feishu.cn/wiki/xxxxx or Wiki token).');
10
10
  // 父块ID参数定义
11
- export const ParentBlockIdSchema = z.string().describe('Parent block ID (required). Target block ID where content will be added, without any URL prefix. ' +
12
- 'For page-level (root level) insertion, extract and use only the document ID portion (not the full URL) as parentBlockId. ' +
13
- 'Obtain existing block IDs using the get_feishu_document_blocks tool.');
11
+ export const ParentBlockIdSchema = z.string().describe('Parent block ID (required). The block ID whose direct children will be operated on, without any URL prefix. ' +
12
+ 'For root-level operations, use the document ID as parentBlockId.');
14
13
  // 块ID参数定义
15
- export const BlockIdSchema = z.string().describe('Block ID (required). The ID of the specific block to get content from. You can obtain block IDs using the get_feishu_document_blocks tool.');
14
+ export const BlockIdSchema = z.string().describe('Block ID (required). The ID of the target block.');
16
15
  // 插入位置索引参数定义
17
- export const IndexSchema = z.number().describe('Insertion position index (required). This index is relative to the children array of the specified parentBlockId block (not the whole document).\n' +
18
- 'If parentBlockId is the document root (i.e., the document ID), index refers to the position among the document content blocks (excluding the title block itself).\n' +
19
- '0 means to insert as the first content block after the title.\n' +
20
- 'If children is empty or missing, use 0 to insert the first content block.\n' +
21
- 'For nested blocks, index is relative to the parent block\'s children.\n' +
22
- '**index must satisfy 0 index parentBlock.children.length, otherwise the API will return an error.**\n' +
23
- 'Note: The title block itself is not part of the children array and cannot be operated on with index.' +
24
- 'Specifies where the block should be inserted. Use 0 to insert at the beginning. ' +
25
- 'Use get_feishu_document_blocks tool to understand document structure if unsure. ' +
26
- 'For consecutive insertions, calculate next index as previous index + 1.');
27
- // 起始插入位置索引参数定义
28
- export const StartIndexSchema = z.number().describe('Starting insertion position index (required). This index is relative to the children array of the specified parentBlockId block.\n' +
29
- 'For the document root, this means the content blocks after the title. For other blocks, it means the sub-blocks under that block.\n' +
30
- 'The index does not include the title block itself.' +
31
- 'Specifies where the first block should be inserted or deleted. Use 0 to insert at the beginning. ' +
32
- 'Use get_feishu_document_blocks tool to understand document structure if unsure.');
16
+ export const IndexSchema = z.number().int().min(0).describe('Insertion position within the direct children of `parentBlockId` (0-based, title block excluded).\n' +
17
+ '0 = before the first content block; max = children.length (append after last block).\n' +
18
+ 'Example: document has 3 blocks valid values are 0, 1, 2, 3.');
19
+ // 起始位置索引参数定义
20
+ export const StartIndexSchema = z.number().int().min(0).describe('Start of the target range within the direct children of `parentBlockId` (0-based, title block excluded).\n' +
21
+ '0 = first content block; max = children.length - 1.\n' +
22
+ 'Example: to target blocks at positions 2, 3, 4 startIndex=2.');
33
23
  // 结束位置索引参数定义
34
- export const EndIndexSchema = z.number().describe('Ending position index (required). This index is relative to the children array of the specified parentBlockId block.\n' +
35
- 'For the document root, this means the content blocks after the title. For other blocks, it means the sub-blocks under that block.\n' +
36
- 'The index does not include the title block itself.' +
37
- 'Specifies the end of the range for deletion (exclusive). ' +
38
- 'For example, to delete blocks 2, 3, and 4, use startIndex=2, endIndex=5. ' +
39
- 'To delete a single block at position 2, use startIndex=2, endIndex=3.');
40
- // 文本对齐方式参数定义
41
- export const AlignSchema = z.number().optional().default(1).describe('Text alignment: 1 for left (default), 2 for center, 3 for right.');
24
+ export const EndIndexSchema = z.number().int().min(1).describe('End of the target range (exclusive) within the direct children of `parentBlockId` (title block excluded).\n' +
25
+ 'Must satisfy endIndex > startIndex.\n' +
26
+ 'Example: to delete blocks at positions 2, 3, 4 → startIndex=2, endIndex=5; single block at 2 → startIndex=2, endIndex=3.');
42
27
  // 文本对齐方式参数定义(带验证)
43
28
  export const AlignSchemaWithValidation = z.number().optional().default(1).refine(val => val === 1 || val === 2 || val === 3, { message: "Alignment must be one of: 1 (left), 2 (center), or 3 (right)" }).describe('Text alignment (optional): 1 for left (default), 2 for center, 3 for right. Only these three values are allowed.');
44
29
  // 文本样式属性定义
@@ -50,13 +35,13 @@ export const TextStylePropertiesSchema = {
50
35
  inline_code: z.boolean().optional().describe('Whether to format as inline code. Default is false, equivalent to `code` in Markdown.'),
51
36
  text_color: z.number().optional().refine(val => !val || (val >= 0 && val <= 7), {
52
37
  message: "Text color must be between 0 and 7 inclusive"
53
- }).describe('Text color value. Default is 0 (black). Available values are only: 1 (gray), 2 (brown), 3 (orange), 4 (yellow), 5 (green), 6 (blue), 7 (purple). Values outside this range will cause an error.'),
38
+ }).describe('Text color (optional): 0=black (default), 1=gray, 2=brown, 3=orange, 4=yellow, 5=green, 6=blue, 7=purple.'),
54
39
  background_color: z.number().optional().refine(val => !val || (val >= 1 && val <= 7), {
55
40
  message: "Background color must be between 1 and 7 inclusive"
56
- }).describe('Background color value. Available values are only: 1 (gray), 2 (brown), 3 (orange), 4 (yellow), 5 (green), 6 (blue), 7 (purple). Values outside this range will cause an error.')
41
+ }).describe('Background color (optional): 1=gray, 2=brown, 3=orange, 4=yellow, 5=green, 6=blue, 7=purple.')
57
42
  };
58
43
  // 文本样式对象定义
59
- export const TextStyleSchema = z.object(TextStylePropertiesSchema).optional().describe('Text style settings. Explicitly set style properties instead of relying on Markdown syntax conversion.');
44
+ export const TextStyleSchema = z.object(TextStylePropertiesSchema).optional().describe('Text style settings (optional). Omit entirely for unstyled text; only set properties that differ from the default (false).');
60
45
  // 文本内容单元定义 - 支持普通文本和公式元素
61
46
  export const TextElementSchema = z.union([
62
47
  z.object({
@@ -70,6 +55,14 @@ export const TextElementSchema = z.union([
70
55
  ]);
71
56
  // 文本内容数组定义
72
57
  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}}]');
58
+ // 单块文本更新项定义
59
+ export const BlockTextUpdateItemSchema = z.object({
60
+ blockId: z.string().describe('Block ID (required). The ID of the block to update.'),
61
+ textElements: TextElementsArraySchema,
62
+ }).describe('A single block text update item: target blockId and its new text content.');
63
+ // 批量更新块文本数组定义
64
+ export const BlockTextUpdatesArraySchema = z.array(BlockTextUpdateItemSchema).min(1).describe('Array of block text update items (required, at least 1). ' +
65
+ 'Example: [{blockId:"xxx",textElements:[{text:"Hello",style:{bold:true}}]},{blockId:"yyy",textElements:[{text:"World"}]}]');
73
66
  // 代码块语言参数定义
74
67
  export const CodeLanguageSchema = z.number().optional().default(1).describe("Programming language code (optional). Common language codes:\n" +
75
68
  "1: PlainText; 2: ABAP; 3: Ada; 4: Apache; 5: Apex; 6: Assembly; 7: Bash; 8: CSharp; 9: C++; 10: C; " +
@@ -96,7 +89,7 @@ export const CodeBlockSchema = z.object({
96
89
  // 标题块内容定义 - 用于批量创建块工具
97
90
  export const HeadingBlockSchema = z.object({
98
91
  level: z.number().min(1).max(9).describe('Heading level from 1 to 9, where 1 is the largest (h1) and 9 is the smallest (h9).'),
99
- content: z.string().describe('Heading text content. The actual text of the heading.'),
92
+ content: z.string().describe('Heading text content.'),
100
93
  align: AlignSchemaWithValidation,
101
94
  });
102
95
  // 列表块内容定义 - 用于批量创建块工具
@@ -106,11 +99,9 @@ export const ListBlockSchema = z.object({
106
99
  align: AlignSchemaWithValidation,
107
100
  });
108
101
  // 块类型枚举 - 用于批量创建块工具
109
- export const BlockTypeEnum = z.string().describe("Block type (required). Supports: 'text', 'code', 'heading', 'list', 'image','mermaid','whiteboard',as well as 'heading1' through 'heading9'. " +
110
- "For headings, we recommend using 'heading' with level property, but 'heading1'-'heading9' are also supported. " +
111
- "For images, use 'image' to create empty image blocks that can be filled later. " +
112
- "For whiteboards, use 'whiteboard' to create empty whiteboard blocks that return a token for filling content. " +
113
- "For text blocks, you can include both regular text and equation elements in the same block.");
102
+ export const BlockTypeEnum = z.enum([
103
+ 'text', 'code', 'heading', 'list', 'image', 'mermaid', 'whiteboard',
104
+ ]).describe("Block type. 'image' and 'whiteboard' create empty blocks to be filled after creation.");
114
105
  // 图片宽度参数定义
115
106
  export const ImageWidthSchema = z.number().optional().describe('Image width in pixels (optional). If not provided, the original image width will be used.');
116
107
  // 图片高度参数定义
@@ -121,10 +112,8 @@ export const ImageBlockSchema = z.object({
121
112
  height: ImageHeightSchema
122
113
  });
123
114
  // Mermaid代码参数定义
124
- export const MermaidCodeSchema = z.string().describe('Mermaid code (required). The complete Mermaid chart code, e.g. \'graph TD; A-->B;\'. ' +
125
- 'IMPORTANT: When node text contains special characters like parentheses (), brackets [], or arrows -->, ' +
126
- 'wrap the entire text in double quotes to prevent parsing errors. ' +
127
- 'Example: A["finish()/返回键"] instead of A[finish()/返回键].');
115
+ export const MermaidCodeSchema = z.string().describe('Mermaid chart code (required). Node labels containing special characters ((), [], -->) must be wrapped in double quotes.\n' +
116
+ 'Example: A["finish()/返回键"] --> B');
128
117
  export const MermaidBlockSchema = z.object({
129
118
  code: MermaidCodeSchema,
130
119
  });
@@ -144,9 +133,8 @@ export const BlockConfigSchema = z.object({
144
133
  z.object({ list: ListBlockSchema }).describe("List block options. Used when blockType is 'list'."),
145
134
  z.object({ image: ImageBlockSchema }).describe("Image block options. Used when blockType is 'image'. Creates empty image blocks."),
146
135
  z.object({ mermaid: MermaidBlockSchema }).describe("Mermaid block options. Used when blockType is 'mermaid'."),
147
- z.object({ whiteboard: WhiteboardBlockSchema }).describe("Whiteboard block options. Used when blockType is 'whiteboard'. Creates empty whiteboard blocks that return a token for filling content."),
148
- z.record(z.any()).describe("Fallback for any other block options")
149
- ]).describe('Options for the specific block type. Provide the corresponding options object based on blockType.'),
136
+ z.object({ whiteboard: WhiteboardBlockSchema }).describe("Whiteboard block options. Used when blockType is 'whiteboard'. Creates an empty whiteboard; response includes board.token pass it as whiteboardId to fill_whiteboard_with_plantuml."),
137
+ ]).describe('Options for the block type. Key must match blockType (e.g., blockType:"text" → options:{text:{...}}).'),
150
138
  });
151
139
  // 表格列数参数定义
152
140
  export const TableColumnSizeSchema = z.number().min(1).describe('Table column size (required). The number of columns in the table. Must be at least 1.');
@@ -168,42 +156,39 @@ export const TableCreateSchema = z.object({
168
156
  rowSize: TableRowSizeSchema,
169
157
  cells: z.array(TableCellContentSchema).optional().describe('Array of cell configurations (optional). Each cell specifies its position (row, column) and content block configuration. ' +
170
158
  'If not provided, empty text blocks will be created for all cells. ' +
171
- 'IMPORTANT: Multiple cells can have the same coordinates (row, column) - when this happens, ' +
172
- 'the content blocks will be added sequentially to the same cell, allowing you to create rich content ' +
173
- 'with multiple blocks (text, code, images, etc.) within a single cell. ' +
174
- 'Example: [{coordinate:{row:0,column:0}, content:{blockType:"text", options:{text:{textStyles:[{text:"Header"}]}}}, ' +
159
+ 'Multiple entries with the same coordinates add content blocks sequentially to that cell. ' +
160
+ 'Example: [{coordinate:{row:0,column:0}, content:{blockType:"text", options:{text:{textStyles:[{text:"Header"}]}}}}, ' +
175
161
  '{coordinate:{row:0,column:0}, content:{blockType:"code", options:{code:{code:"console.log(\'hello\')", language:30}}}}] ' +
176
- 'will add both a text block and a code block to cell (0,0).')
162
+ 'adds both a text block and a code block to cell (0,0).')
177
163
  });
178
164
  // 媒体ID参数定义
179
- export const MediaIdSchema = z.string().describe('Media ID (required). The unique identifier for a media resource (image, file, etc.) in Feishu. ' +
180
- 'Usually obtained from image blocks or file references in documents. ' +
181
- 'Format is typically like "boxcnrHpsg1QDqXAAAyachabcef".');
165
+ export const MediaIdSchema = z.string().describe('Image media token (required). Obtain from one of:\n' +
166
+ '1. An image block returned by get_feishu_document_blocks: block.image.token (block_type=27)\n' +
167
+ '2. Response of upload_and_bind_image_to_block: fileToken field');
182
168
  // 额外参数定义 - 用于媒体资源下载
183
- export const MediaExtraSchema = z.string().optional().describe('Extra parameters for media download (optional). ' +
184
- 'These parameters are passed directly to the Feishu API and can modify how the media is returned.');
169
+ export const MediaExtraSchema = z.string().optional().describe('Extra query parameter for the Feishu media download API (optional). Required only for encrypted images; omit for standard image blocks.');
185
170
  // 文件夹Token参数定义(必传)
186
171
  export const FolderTokenSchema = z.string().describe('Folder token (required). The unique identifier for a folder in Feishu. ' +
187
172
  'Format is an alphanumeric string like "FWK2fMleClICfodlHHWc4Mygnhb".');
188
173
  // 文件夹Token参数定义(可选,用于文档创建、获取文件列表等场景)
189
174
  export const FolderTokenOptionalSchema = z.string().optional().describe('Folder token (optional, for Feishu Drive folder mode). The unique identifier for a folder in Feishu Drive. ' +
190
- 'Format is an alphanumeric string like "FWK2fMleClICfodlHHWc4Mygnhb". ');
175
+ 'Format is an alphanumeric string like "FWK2fMleClICfodlHHWc4Mygnhb".');
191
176
  // 文件夹名称参数定义
192
177
  export const FolderNameSchema = z.string().describe('Folder name (required). The name for the new folder to be created.');
193
178
  // 知识空间ID参数定义
194
- export const SpaceIdSchema = z.string().describe('Space ID (optional, required for wiki space mode). The unique identifier for a wiki space in Feishu. ' +
179
+ export const SpaceIdSchema = z.string().describe('Space ID. Required when using wikiContext mode. The unique identifier for a wiki space in Feishu. ' +
195
180
  'Can be obtained from get_feishu_root_folder_info (wiki_spaces array or my_library.space_id). ' +
196
181
  'Format is typically like "74812***88644".');
197
182
  // 父节点Token参数定义
198
- export const ParentNodeTokenSchema = z.string().optional().describe('Parent node token (optional, used with spaceId). The token of the parent node in a wiki space. ' +
199
- 'If not provided or empty, will retrieve nodes from the root of the wiki space. ' +
183
+ export const ParentNodeTokenSchema = z.string().optional().describe('Parent node token (optional). The token of the parent node in a wiki space. ' +
184
+ 'If not provided, retrieves nodes from the root of the wiki space. ' +
200
185
  'Format is typically like "PdDWwIHD6****MhcIOY7npg".');
201
186
  // 知识库节点上下文参数定义(包装 spaceId 和 parentNodeToken)
202
187
  export const WikiSpaceNodeContextSchema = z.object({
203
- spaceId: SpaceIdSchema.optional(),
188
+ spaceId: SpaceIdSchema,
204
189
  parentNodeToken: ParentNodeTokenSchema,
205
- }).optional().describe('Wiki space node context object. Contains spaceId (required when using this object) and optional parentNodeToken. ' +
206
- 'Used for wiki space operations instead of folderToken.');
190
+ }).optional().describe('Wiki space node context object (optional). Provide this instead of folderToken to operate in wiki space mode. ' +
191
+ 'spaceId is required; parentNodeToken is optional (omit to target the wiki root).');
207
192
  // 搜索关键字参数定义
208
193
  export const SearchKeySchema = z.string().describe('Search keyword (required). The keyword to search for in documents.');
209
194
  // 搜索类型枚举
@@ -213,10 +198,9 @@ export const PageTokenSchema = z.string().optional().describe('Wiki page token (
213
198
  // 文档分页偏移量参数定义
214
199
  export const OffsetSchema = z.number().optional().describe('Document offset (optional). Offset for document search pagination. Only needed when fetching next page of document results.');
215
200
  // 图片路径或URL参数定义
216
- export const ImagePathOrUrlSchema = z.string().describe('Image path or URL (required). Supports the following formats:\n' +
217
- '1. Local file absolute path: e.g., "C:\\path\\to\\image.jpg"\n' +
218
- '2. HTTP/HTTPS URL: e.g., "https://example.com/image.png"\n' +
219
- 'The tool will automatically detect the format and handle accordingly.');
201
+ export const ImagePathOrUrlSchema = z.string().describe('Image path or URL (required).\n' +
202
+ '1. Local absolute path: e.g., "C:\\path\\to\\image.jpg"\n' +
203
+ '2. HTTP/HTTPS URL: e.g., "https://example.com/image.png"');
220
204
  // 图片文件名参数定义
221
205
  export const ImageFileNameSchema = z.string().optional().describe('Image file name (optional). If not provided, a default name will be generated based on the source. ' +
222
206
  'Should include the file extension, e.g., "image.png" or "photo.jpg".');
@@ -227,29 +211,22 @@ export const ImagesArraySchema = z.array(z.object({
227
211
  fileName: ImageFileNameSchema.optional(),
228
212
  })).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").');
229
213
  // 画板ID参数定义
230
- export const WhiteboardIdSchema = z.string().describe('Whiteboard ID (required). This is the token value from the board.token field when getting document blocks.\n' +
231
- 'When you find a block with block_type: 43, the whiteboard ID is located in board.token field.\n' +
232
- 'Example: "EPJKwvY5ghe3pVbKj9RcT2msnBX"');
214
+ export const WhiteboardIdSchema = z.string().describe('Whiteboard token (required). Found in board.token of a block with block_type=43.\nExample: "EPJKwvY5ghe3pVbKj9RcT2msnBX"');
233
215
  // 画板代码参数定义(支持 PlantUML 和 Mermaid)
234
216
  export const WhiteboardCodeSchema = z.string().describe('Diagram code (required). The complete diagram code to create in the whiteboard.\n' +
235
217
  'Supports both PlantUML and Mermaid formats.\n' +
236
218
  'PlantUML example: "@startuml\nAlice -> Bob: Hello\n@enduml"\n' +
237
219
  'Mermaid example: "graph TD\nA[Start] --> B[End]"');
238
220
  // 语法类型参数定义
239
- export const SyntaxTypeSchema = z.number().describe('Syntax type (required). Specifies the diagram syntax format.\n' +
240
- '1: PlantUML syntax\n' +
241
- '2: Mermaid syntax');
221
+ export const SyntaxTypeSchema = z.enum(['plantuml', 'mermaid']).describe('Diagram syntax type (required): "plantuml" or "mermaid".');
242
222
  // 画板内容配置定义(包含画板ID和内容配置)
243
223
  export const WhiteboardContentSchema = z.object({
244
224
  whiteboardId: WhiteboardIdSchema,
245
225
  code: WhiteboardCodeSchema,
246
226
  syntax_type: SyntaxTypeSchema,
247
- }).describe('Whiteboard content configuration. Contains the whiteboard ID, diagram code and syntax type.\n' +
248
- 'whiteboardId: The token value from board.token field when creating whiteboard block (required)\n' +
249
- 'code: The diagram code (PlantUML or Mermaid format) (required)\n' +
250
- 'syntax_type: 1 for PlantUML, 2 for Mermaid (required)');
227
+ }).describe('Whiteboard fill item: target whiteboard ID, diagram code, and syntax type.');
251
228
  // 批量填充画板数组定义
252
- export const WhiteboardFillArraySchema = z.array(WhiteboardContentSchema).describe('Array of whiteboard fill items (required). Each item must include whiteboardId, code and syntax_type.\n' +
253
- 'Example: [{whiteboardId:"token1", code:"@startuml...", syntax_type:1}, {whiteboardId:"token2", code:"graph TD...", syntax_type:2}]');
229
+ export const WhiteboardFillArraySchema = z.array(WhiteboardContentSchema).describe('Array of whiteboard fill items (required).\n' +
230
+ 'Example: [{whiteboardId:"token1", code:"@startuml\\nAlice -> Bob: Hi\\n@enduml", syntax_type:"plantuml"}, {whiteboardId:"token2", code:"graph TD\\nA-->B", syntax_type:"mermaid"}]');
254
231
  // 文档标题参数定义
255
232
  export const DocumentTitleSchema = z.string().describe('Document title (required). This will be displayed in the Feishu document list and document header.');
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Member module schema definitions (search/v1/user, contact/v3/users/batch).
3
+ * All `.describe()` in English; concise, no duplication. JSDoc above exports = one-line summary only.
4
+ */
5
+ import { z } from 'zod';
6
+ export const UserSearchQuerySchema = z.string().min(1).describe(`Search keyword (required).Matches display name and pinyin (full/partial/initials, case-insensitive). user_id is NOT matched—use userIdsParam.`);
7
+ export const UserSearchPageTokenSchema = z
8
+ .string()
9
+ .optional()
10
+ .describe('Pagination token (optional). Omit on first call; use page_token from previous response.');
11
+ export const UserIdTypeSchema = z
12
+ .enum(['open_id', 'union_id', 'user_id'])
13
+ .optional()
14
+ .default('open_id')
15
+ .describe('ID type: open_id (per-app) | union_id (per-developer) | user_id (tenant). Default open_id.');
16
+ export const UserIdValueSchema = z.string().min(1).describe('User ID value (open_id, union_id, or user_id per idType).');
17
+ /** Single search request: one keyword + optional pageToken for that keyword (each query is a separate API call). */
18
+ export const UserSearchQueryItemSchema = z.object({
19
+ query: UserSearchQuerySchema,
20
+ pageToken: UserSearchPageTokenSchema,
21
+ });
22
+ export const UserSearchQueriesSchema = z
23
+ .array(UserSearchQueryItemSchema)
24
+ .min(1)
25
+ .max(20)
26
+ .describe('Search by name. Each item: query + optional pageToken for that query. Min 1, max 20.');
27
+ export const UserIdItemSchema = z.object({
28
+ id: UserIdValueSchema,
29
+ idType: UserIdTypeSchema,
30
+ });
31
+ export const GetFeishuUsersUserIdsParamSchema = z
32
+ .array(UserIdItemSchema)
33
+ .min(1)
34
+ .max(50)
35
+ .describe('Get by ID list. Each item: id + idType. IDs may mix types.');