feishu-mcp 0.0.4 → 0.0.5

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.
@@ -41,6 +41,36 @@ export class FeishuService {
41
41
  this.appId = appId;
42
42
  this.appSecret = appSecret;
43
43
  }
44
+ // 包装和重新抛出错误的辅助方法
45
+ wrapAndThrowError(message, originalError) {
46
+ Logger.error(`${message}:`, originalError);
47
+ // 如果原始错误已经是FeishuError格式,直接重新抛出
48
+ if (originalError && typeof originalError === 'object' && 'status' in originalError && 'err' in originalError) {
49
+ throw originalError;
50
+ }
51
+ // 如果是AxiosError,抽取有用信息
52
+ if (originalError instanceof AxiosError && originalError.response) {
53
+ throw {
54
+ status: originalError.response.status,
55
+ err: `${message}: ${originalError.response.data?.msg || originalError.message || 'Unknown error'}`,
56
+ apiError: originalError.response.data
57
+ };
58
+ }
59
+ // 其他类型的错误,包装为一致的格式
60
+ if (originalError instanceof Error) {
61
+ throw {
62
+ status: 500,
63
+ err: `${message}: ${originalError.message}`,
64
+ apiError: originalError
65
+ };
66
+ }
67
+ // 未知错误类型
68
+ throw {
69
+ status: 500,
70
+ err: message,
71
+ apiError: originalError
72
+ };
73
+ }
44
74
  isTokenExpired() {
45
75
  if (!this.accessToken || !this.tokenExpireTime)
46
76
  return true;
@@ -62,7 +92,7 @@ export class FeishuService {
62
92
  Logger.log(`请求方法: POST`);
63
93
  Logger.log(`请求数据: ${JSON.stringify(requestData, null, 2)}`);
64
94
  const response = await axios.post(url, requestData);
65
- Logger.log(`响应状态码: ${response.status}`);
95
+ Logger.log(`响应状态码: ${response?.status}`);
66
96
  Logger.log(`响应头: ${JSON.stringify(response.headers, null, 2)}`);
67
97
  Logger.log(`响应数据: ${JSON.stringify(response.data, null, 2)}`);
68
98
  if (response.data.code !== 0) {
@@ -70,6 +100,7 @@ export class FeishuService {
70
100
  throw {
71
101
  status: response.status,
72
102
  err: response.data.msg || "Unknown error",
103
+ apiError: response.data
73
104
  };
74
105
  }
75
106
  this.accessToken = response.data.tenant_access_token;
@@ -86,6 +117,7 @@ export class FeishuService {
86
117
  throw {
87
118
  status: error.response.status,
88
119
  err: error.response.data?.msg || "Unknown error",
120
+ apiError: error.response.data
89
121
  };
90
122
  }
91
123
  Logger.error('获取访问令牌时发生未知错误:', error);
@@ -128,6 +160,7 @@ export class FeishuService {
128
160
  throw {
129
161
  status: error.response.status,
130
162
  err: error.response.data?.msg || "Unknown error",
163
+ apiError: error.response.data
131
164
  };
132
165
  }
133
166
  Logger.error('发送请求时发生未知错误:', error);
@@ -157,16 +190,7 @@ export class FeishuService {
157
190
  return docInfo;
158
191
  }
159
192
  catch (error) {
160
- Logger.error(`创建文档失败:`, error);
161
- if (error instanceof AxiosError) {
162
- Logger.error(`请求URL: ${error.config?.url}`);
163
- Logger.error(`请求方法: ${error.config?.method?.toUpperCase()}`);
164
- Logger.error(`状态码: ${error.response?.status}`);
165
- if (error.response?.data) {
166
- Logger.error(`错误详情: ${JSON.stringify(error.response.data, null, 2)}`);
167
- }
168
- }
169
- throw error;
193
+ this.wrapAndThrowError('创建文档失败', error);
170
194
  }
171
195
  }
172
196
  // 获取文档信息
@@ -383,6 +407,9 @@ export class FeishuService {
383
407
  createHeadingBlockContent(text, level = 1, align = 1) {
384
408
  // 确保标题级别在有效范围内(1-9)
385
409
  const safeLevel = Math.max(1, Math.min(9, level));
410
+ // 确保align值在合法范围内(1-3)
411
+ // 1表示居左,2表示居中,3表示居右
412
+ const safeAlign = (align === 1 || align === 2 || align === 3) ? align : 1;
386
413
  // 根据标题级别设置block_type和对应的属性名
387
414
  // 飞书API中,一级标题的block_type为3,二级标题为4,以此类推
388
415
  const blockType = 2 + safeLevel; // 一级标题为3,二级标题为4,以此类推
@@ -402,7 +429,7 @@ export class FeishuService {
402
429
  }
403
430
  ],
404
431
  style: {
405
- align: align,
432
+ align: safeAlign,
406
433
  folded: false
407
434
  }
408
435
  };
@@ -442,7 +469,10 @@ export class FeishuService {
442
469
  if (!docId) {
443
470
  throw new Error(`无效的文档ID: ${documentId}`);
444
471
  }
445
- Logger.log(`开始创建标题块,文档ID: ${docId},父块ID: ${parentBlockId},标题级别: ${level},插入位置: ${index}`);
472
+ // 确保align值在合法范围内(1-3)
473
+ // 1表示居左,2表示居中,3表示居右
474
+ const safeAlign = (align === 1 || align === 2 || align === 3) ? align : 1;
475
+ Logger.log(`开始创建标题块,文档ID: ${docId},父块ID: ${parentBlockId},标题级别: ${level},对齐方式: ${safeAlign},插入位置: ${index}`);
446
476
  // 确保标题级别在有效范围内(1-9)
447
477
  const safeLevel = Math.max(1, Math.min(9, level));
448
478
  // 根据标题级别设置block_type和对应的属性名
@@ -464,7 +494,7 @@ export class FeishuService {
464
494
  }
465
495
  ],
466
496
  style: {
467
- align: align,
497
+ align: safeAlign,
468
498
  folded: false
469
499
  }
470
500
  };
@@ -472,8 +502,110 @@ export class FeishuService {
472
502
  return await this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
473
503
  }
474
504
  catch (error) {
475
- Logger.error(`创建标题块失败:`, error);
476
- throw error;
505
+ this.wrapAndThrowError(`创建标题块失败`, error);
506
+ }
507
+ }
508
+ // 获取块内容
509
+ async getBlockContent(documentId, blockId) {
510
+ try {
511
+ const docId = this.extractDocIdFromUrl(documentId);
512
+ if (!docId) {
513
+ throw new Error(`无效的文档ID: ${documentId}`);
514
+ }
515
+ Logger.log(`开始获取块内容,文档ID: ${docId},块ID: ${blockId}`);
516
+ const endpoint = `/docx/v1/documents/${docId}/blocks/${blockId}?document_revision_id=-1`;
517
+ Logger.log(`准备请求API端点: ${endpoint}`);
518
+ const response = await this.request(endpoint);
519
+ if (response.code !== 0) {
520
+ throw new Error(`获取块内容失败: ${response.msg}`);
521
+ }
522
+ const blockContent = response.data?.block;
523
+ Logger.log(`块内容获取成功: ${JSON.stringify(blockContent, null, 2)}`);
524
+ return blockContent;
525
+ }
526
+ catch (error) {
527
+ this.wrapAndThrowError(`获取块内容失败`, error);
528
+ }
529
+ }
530
+ // 更新块文本内容
531
+ async updateBlockTextContent(documentId, blockId, textElements) {
532
+ try {
533
+ const docId = this.extractDocIdFromUrl(documentId);
534
+ if (!docId) {
535
+ throw new Error(`无效的文档ID: ${documentId}`);
536
+ }
537
+ Logger.log(`开始更新块文本内容,文档ID: ${docId},块ID: ${blockId}`);
538
+ const endpoint = `/docx/v1/documents/${docId}/blocks/${blockId}?document_revision_id=-1`;
539
+ Logger.log(`准备请求API端点: ${endpoint}`);
540
+ const elements = textElements.map(item => ({
541
+ text_run: {
542
+ content: item.text,
543
+ text_element_style: item.style || {}
544
+ }
545
+ }));
546
+ const data = {
547
+ update_text_elements: {
548
+ elements: elements
549
+ }
550
+ };
551
+ Logger.log(`请求数据: ${JSON.stringify(data, null, 2)}`);
552
+ const response = await this.request(endpoint, 'PATCH', data);
553
+ if (response.code !== 0) {
554
+ throw new Error(`更新块文本内容失败: ${response.msg}`);
555
+ }
556
+ Logger.log(`块文本内容更新成功: ${JSON.stringify(response.data, null, 2)}`);
557
+ return response.data;
558
+ }
559
+ catch (error) {
560
+ this.wrapAndThrowError(`更新块文本内容失败`, error);
561
+ }
562
+ }
563
+ // 创建列表块内容(有序或无序)
564
+ createListBlockContent(text, isOrdered = false, align = 1) {
565
+ // 确保 align 值在合法范围内(1-3)
566
+ const safeAlign = (align === 1 || align === 2 || align === 3) ? align : 1;
567
+ // 有序列表是 block_type: 13,无序列表是 block_type: 12
568
+ const blockType = isOrdered ? 13 : 12;
569
+ const propertyKey = isOrdered ? "ordered" : "bullet";
570
+ // 构建块内容
571
+ const blockContent = {
572
+ block_type: blockType
573
+ };
574
+ // 设置列表属性
575
+ blockContent[propertyKey] = {
576
+ elements: [
577
+ {
578
+ text_run: {
579
+ content: text,
580
+ text_element_style: {}
581
+ }
582
+ }
583
+ ],
584
+ style: {
585
+ align: safeAlign,
586
+ folded: false
587
+ }
588
+ };
589
+ return blockContent;
590
+ }
591
+ // 创建列表块(有序或无序)
592
+ async createListBlock(documentId, parentBlockId, text, isOrdered = false, index = 0, align = 1) {
593
+ try {
594
+ const docId = this.extractDocIdFromUrl(documentId);
595
+ if (!docId) {
596
+ throw new Error(`无效的文档ID: ${documentId}`);
597
+ }
598
+ // 确保align值在合法范围内(1-3)
599
+ const safeAlign = (align === 1 || align === 2 || align === 3) ? align : 1;
600
+ const listType = isOrdered ? "有序" : "无序";
601
+ Logger.log(`开始创建${listType}列表块,文档ID: ${docId},父块ID: ${parentBlockId},对齐方式: ${safeAlign},插入位置: ${index}`);
602
+ // 创建列表块内容
603
+ const blockContent = this.createListBlockContent(text, isOrdered, safeAlign);
604
+ Logger.log(`列表块内容: ${JSON.stringify(blockContent, null, 2)}`);
605
+ return await this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
606
+ }
607
+ catch (error) {
608
+ this.wrapAndThrowError(`创建${isOrdered ? "有序" : "无序"}列表块失败`, error);
477
609
  }
478
610
  }
479
611
  extractDocIdFromUrl(url) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feishu-mcp",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Model Context Protocol server for Feishu integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",