@zzp123/mcp-zentao 1.18.2 → 1.18.4

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/config.js CHANGED
@@ -22,7 +22,7 @@ export function loadConfig() {
22
22
  }
23
23
  }
24
24
  catch (error) {
25
- console.error('读取配置文件失败:', error);
25
+ process.stderr.write('[ERROR] Failed to load config: ' + String(error) + '\n');
26
26
  }
27
27
  return null;
28
28
  }
package/dist/index-dev.js CHANGED
@@ -14,15 +14,12 @@ if (configIndex !== -1 && configIndex + 1 < args.length) {
14
14
  // 获取 --config 后面的 JSON 字符串并解析
15
15
  const jsonStr = args[configIndex + 1];
16
16
  configData = JSON.parse(jsonStr);
17
- console.log('成功解析配置数据:', configData);
18
17
  // 如果配置数据中包含 config 对象,则保存配置
19
18
  if (configData.config) {
20
- console.log('正在保存配置...');
21
19
  saveConfig(configData.config);
22
20
  }
23
21
  }
24
22
  catch (error) {
25
- console.error('配置解析失败:', error);
26
23
  process.exit(1);
27
24
  }
28
25
  }
@@ -34,10 +31,8 @@ const server = new McpServer({
34
31
  // Initialize ZentaoAPI instance
35
32
  let zentaoApi = null;
36
33
  export default async function main(params) {
37
- console.log('接收到的参数:', params);
38
34
  // 如果传入了配置信息,就保存它
39
35
  if (params.config) {
40
- console.log('保存新的配置信息...');
41
36
  saveConfig(params.config);
42
37
  }
43
38
  }
@@ -518,13 +513,11 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
518
513
  const path = await import('path');
519
514
  const { execSync } = await import('child_process');
520
515
  try {
521
- console.log('[uploadImageFromClipboard] 开始执行...');
522
516
  // 读取系统剪贴板图片
523
517
  let fileBuffer;
524
518
  let finalFilename;
525
519
  // Windows: 使用 PowerShell 脚本读取剪贴板
526
520
  if (process.platform === 'win32') {
527
- console.log('[uploadImageFromClipboard] Windows平台,使用PowerShell脚本读取剪贴板...');
528
521
  // 内嵌 PowerShell 脚本,避免依赖外部文件
529
522
  const psScript = `Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); $bytes = $ms.ToArray(); $ms.Close(); $base64 = [Convert]::ToBase64String($bytes); Write-Output $base64 } else { Write-Output 'NoImage' }`;
530
523
  try {
@@ -536,7 +529,6 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
536
529
  stdio: ['pipe', 'pipe', 'pipe']
537
530
  }).trim();
538
531
  if (!result || result.includes('NoImage') || result.includes('Error')) {
539
- console.error('[uploadImageFromClipboard] 剪贴板中没有图片');
540
532
  return {
541
533
  content: [{
542
534
  type: "text",
@@ -550,10 +542,8 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
550
542
  }
551
543
  fileBuffer = Buffer.from(result, 'base64');
552
544
  finalFilename = filename || `clipboard_${Date.now()}.png`;
553
- console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
554
545
  }
555
546
  catch (err) {
556
- console.error('[uploadImageFromClipboard] Windows读取剪贴板失败:', err);
557
547
  return {
558
548
  content: [{
559
549
  type: "text",
@@ -569,17 +559,14 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
569
559
  }
570
560
  // macOS: 使用 pngpaste
571
561
  else if (process.platform === 'darwin') {
572
- console.log('[uploadImageFromClipboard] macOS平台,使用pngpaste读取剪贴板...');
573
562
  const tempFile = path.join(process.cwd(), '.temp_clipboard.png');
574
563
  try {
575
564
  execSync(`pngpaste "${tempFile}"`);
576
565
  fileBuffer = fs.readFileSync(tempFile);
577
566
  fs.unlinkSync(tempFile);
578
567
  finalFilename = filename || `clipboard_${Date.now()}.png`;
579
- console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
580
568
  }
581
569
  catch (err) {
582
- console.error('[uploadImageFromClipboard] macOS读取剪贴板失败:', err);
583
570
  return {
584
571
  content: [{
585
572
  type: "text",
@@ -594,17 +581,14 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
594
581
  }
595
582
  // Linux: 使用 xclip
596
583
  else {
597
- console.log('[uploadImageFromClipboard] Linux平台,使用xclip读取剪贴板...');
598
584
  try {
599
585
  const buffer = execSync('xclip -selection clipboard -t image/png -o', {
600
586
  maxBuffer: 10 * 1024 * 1024
601
587
  });
602
588
  fileBuffer = buffer;
603
589
  finalFilename = filename || `clipboard_${Date.now()}.png`;
604
- console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
605
590
  }
606
591
  catch (err) {
607
- console.error('[uploadImageFromClipboard] Linux读取剪贴板失败:', err);
608
592
  return {
609
593
  content: [{
610
594
  type: "text",
@@ -620,21 +604,17 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
620
604
  // 创建 img 文件夹
621
605
  const imgDir = path.join(process.cwd(), 'img');
622
606
  if (!fs.existsSync(imgDir)) {
623
- console.log(`[uploadImageFromClipboard] 创建目录: ${imgDir}`);
624
607
  fs.mkdirSync(imgDir, { recursive: true });
625
608
  }
626
609
  // 保存到 img 文件夹
627
610
  const savedPath = path.join(imgDir, finalFilename);
628
611
  fs.writeFileSync(savedPath, fileBuffer);
629
- console.log(`[uploadImageFromClipboard] 图片已保存到: ${savedPath}`);
630
612
  // 上传到禅道
631
- console.log('[uploadImageFromClipboard] 开始上传到禅道...');
632
613
  const uploadResult = await zentaoApi.uploadFile({
633
614
  file: fileBuffer,
634
615
  filename: finalFilename,
635
616
  uid
636
617
  });
637
- console.log('[uploadImageFromClipboard] 上传成功,结果:', uploadResult);
638
618
  // 生成禅道需要的 HTML 格式
639
619
  const fileId = uploadResult.id;
640
620
  const baseUrl = zentaoApi.getConfig().url;
@@ -652,7 +632,6 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
652
632
  message: `图片已保存到本地并上传到禅道`,
653
633
  tip: `更新 Bug 描述时,请使用 imageHtml 字段中的 HTML 代码。图片会被包裹在 <p> 标签中以确保正确显示。`
654
634
  };
655
- console.log('[uploadImageFromClipboard] 返回结果:', response);
656
635
  return {
657
636
  content: [{
658
637
  type: "text",
@@ -661,7 +640,6 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
661
640
  };
662
641
  }
663
642
  catch (error) {
664
- console.error('[uploadImageFromClipboard] 发生错误:', error);
665
643
  const errorResponse = {
666
644
  success: false,
667
645
  error: error.message || String(error),
@@ -807,4 +785,7 @@ server.tool("deleteComment", "删除评论 - 只能删除自己的评论,管
807
785
  });
808
786
  // Start receiving messages on stdin and sending messages on stdout
809
787
  const transport = new StdioServerTransport();
810
- await server.connect(transport).catch(console.error);
788
+ await server.connect(transport).catch(err => {
789
+ process.stderr.write('[FATAL] MCP server failed: ' + String(err) + '\n');
790
+ process.exit(1);
791
+ });
package/dist/index-pm.js CHANGED
@@ -14,15 +14,15 @@ if (configIndex !== -1 && configIndex + 1 < args.length) {
14
14
  // 获取 --config 后面的 JSON 字符串并解析
15
15
  const jsonStr = args[configIndex + 1];
16
16
  configData = JSON.parse(jsonStr);
17
- console.log('成功解析配置数据:', configData);
17
+ process.stdout.write('成功解析配置数据: ' + JSON.stringify(configData) + '\n');
18
18
  // 如果配置数据中包含 config 对象,则保存配置
19
19
  if (configData.config) {
20
- console.log('正在保存配置...');
20
+ process.stdout.write('正在保存配置...\n');
21
21
  saveConfig(configData.config);
22
22
  }
23
23
  }
24
24
  catch (error) {
25
- console.error('配置解析失败:', error);
25
+ process.stderr.write('配置解析失败: ' + String(error) + '\n');
26
26
  process.exit(1);
27
27
  }
28
28
  }
@@ -34,10 +34,10 @@ const server = new McpServer({
34
34
  // Initialize ZentaoAPI instance
35
35
  let zentaoApi = null;
36
36
  export default async function main(params) {
37
- console.log('接收到的参数:', params);
37
+ process.stdout.write('接收到的参数: ' + JSON.stringify(params) + '\n');
38
38
  // 如果传入了配置信息,就保存它
39
39
  if (params.config) {
40
- console.log('保存新的配置信息...');
40
+ process.stdout.write('保存新的配置信息...\n');
41
41
  saveConfig(params.config);
42
42
  }
43
43
  }
@@ -375,76 +375,58 @@ server.tool("createPlan", {
375
375
  content: [{ type: "text", text: JSON.stringify(plan, null, 2) }]
376
376
  };
377
377
  });
378
- server.tool("changeStory", "需求变更 - 支持软件需求(story)和用户需求(requirement),自动识别类型。支持对象或JSON字符串格式", {
378
+ server.tool("changeStory", "需求变更 - 支持软件需求(story)和用户需求(requirement),自动识别类型", {
379
379
  storyId: z.number().describe("需求ID(可以是story或requirement类型)"),
380
- update: z.union([
381
- z.object({
382
- // 基本信息
383
- title: z.string().optional(),
384
- product: z.number().optional(),
385
- parent: z.number().optional(),
386
- module: z.number().optional(),
387
- branch: z.number().optional(),
388
- plan: z.union([z.number(), z.array(z.number())]).optional(),
389
- type: z.string().optional(),
390
- // 来源信息
391
- source: z.string().optional(),
392
- sourceNote: z.string().optional(),
393
- // 分类与优先级
394
- category: z.string().optional(),
395
- pri: z.number().optional(),
396
- estimate: z.number().optional(),
397
- // 状态与阶段
398
- stage: z.string().optional(),
399
- status: z.string().optional(),
400
- // 关键词与标识
401
- keywords: z.string().optional(),
402
- color: z.string().optional(),
403
- grade: z.number().optional(),
404
- // 人员相关
405
- mailto: z.array(z.string()).optional(),
406
- reviewer: z.array(z.string()).optional().describe("评审人员列表(通常为必填,除非设置needNotReview=true跳过评审)"),
407
- assignedTo: z.string().optional(),
408
- closedBy: z.string().optional(),
409
- feedbackBy: z.string().optional(),
410
- // 关闭相关
411
- closedReason: z.enum(['done', 'subdivided', 'duplicate', 'postponed', 'willnotdo', 'cancel', 'bydesign']).optional(),
412
- duplicateStory: z.number().optional(),
413
- // 评审相关
414
- needNotReview: z.boolean().optional().describe("是否跳过评审,如果不提供reviewer则应设置为true"),
415
- // 通知相关
416
- notifyEmail: z.string().optional(),
417
- // 描述内容
418
- spec: z.string().optional(),
419
- verify: z.string().optional(),
420
- // 备注
421
- comment: z.string().optional()
422
- }),
423
- z.string()
424
- ]).describe(`更新需求(使用 PUT 接口)
380
+ update: z.object({
381
+ // 基本信息
382
+ title: z.string().optional(),
383
+ product: z.number().optional(),
384
+ parent: z.number().optional(),
385
+ module: z.number().optional(),
386
+ branch: z.number().optional(),
387
+ plan: z.union([z.number(), z.array(z.number())]).optional(),
388
+ type: z.string().optional(),
389
+ // 来源信息
390
+ source: z.string().optional(),
391
+ sourceNote: z.string().optional(),
392
+ // 分类与优先级
393
+ category: z.string().optional(),
394
+ pri: z.number().optional(),
395
+ estimate: z.number().optional(),
396
+ // 状态与阶段
397
+ stage: z.string().optional(),
398
+ status: z.string().optional(),
399
+ // 关键词与标识
400
+ keywords: z.string().optional(),
401
+ color: z.string().optional(),
402
+ grade: z.number().optional(),
403
+ // 人员相关
404
+ mailto: z.array(z.string()).optional(),
405
+ reviewer: z.array(z.string()).optional().describe("评审人员列表(通常为必填,除非设置needNotReview=true跳过评审)"),
406
+ assignedTo: z.string().optional(),
407
+ closedBy: z.string().optional(),
408
+ feedbackBy: z.string().optional(),
409
+ // 关闭相关
410
+ closedReason: z.enum(['done', 'subdivided', 'duplicate', 'postponed', 'willnotdo', 'cancel', 'bydesign']).optional(),
411
+ duplicateStory: z.number().optional(),
412
+ // 评审相关
413
+ needNotReview: z.boolean().optional().describe("是否跳过评审,如果不提供reviewer则应设置为true"),
414
+ // 通知相关
415
+ notifyEmail: z.string().optional(),
416
+ // 描述内容
417
+ spec: z.string().optional(),
418
+ verify: z.string().optional(),
419
+ // 备注
420
+ comment: z.string().optional()
421
+ }).describe(`更新需求(使用 PUT 接口)
425
422
 
426
423
  此工具使用标准的"修改需求其他字段"接口(PUT /stories/:id),支持修改29个字段。
427
424
  自动处理评审人问题,无需手动设置 needNotReview。
428
-
429
- 支持对象或JSON字符串格式。
430
425
  `.trim())
431
426
  }, async ({ storyId, update }) => {
432
427
  if (!zentaoApi)
433
428
  throw new Error("Please initialize Zentao API first");
434
- // 如果 update 是字符串,尝试解析为对象
435
- let updateData;
436
- if (typeof update === 'string') {
437
- try {
438
- updateData = JSON.parse(update);
439
- }
440
- catch (error) {
441
- throw new Error(`Invalid JSON string: ${error instanceof Error ? error.message : 'Unknown error'}`);
442
- }
443
- }
444
- else {
445
- updateData = update;
446
- }
447
- const story = await zentaoApi.changeStory(storyId, updateData);
429
+ const story = await zentaoApi.changeStory(storyId, update);
448
430
  return {
449
431
  content: [{ type: "text", text: JSON.stringify(story, null, 2) }]
450
432
  };
@@ -639,13 +621,13 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
639
621
  const path = await import('path');
640
622
  const { execSync } = await import('child_process');
641
623
  try {
642
- console.log('[uploadImageFromClipboard] 开始执行...');
624
+ process.stdout.write('[uploadImageFromClipboard] 开始执行...\n');
643
625
  // 读取系统剪贴板图片
644
626
  let fileBuffer;
645
627
  let finalFilename;
646
628
  // Windows: 使用 PowerShell 脚本读取剪贴板
647
629
  if (process.platform === 'win32') {
648
- console.log('[uploadImageFromClipboard] Windows平台,使用PowerShell脚本读取剪贴板...');
630
+ process.stdout.write('[uploadImageFromClipboard] Windows平台,使用PowerShell脚本读取剪贴板...\n');
649
631
  // 内嵌 PowerShell 脚本,避免依赖外部文件
650
632
  const psScript = `Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); $bytes = $ms.ToArray(); $ms.Close(); $base64 = [Convert]::ToBase64String($bytes); Write-Output $base64 } else { Write-Output 'NoImage' }`;
651
633
  try {
@@ -657,7 +639,7 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
657
639
  stdio: ['pipe', 'pipe', 'pipe']
658
640
  }).trim();
659
641
  if (!result || result.includes('NoImage') || result.includes('Error')) {
660
- console.error('[uploadImageFromClipboard] 剪贴板中没有图片');
642
+ process.stderr.write('[uploadImageFromClipboard] 剪贴板中没有图片\n');
661
643
  return {
662
644
  content: [{
663
645
  type: "text",
@@ -671,10 +653,10 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
671
653
  }
672
654
  fileBuffer = Buffer.from(result, 'base64');
673
655
  finalFilename = filename || `clipboard_${Date.now()}.png`;
674
- console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
656
+ process.stdout.write(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes\n`);
675
657
  }
676
658
  catch (err) {
677
- console.error('[uploadImageFromClipboard] Windows读取剪贴板失败:', err);
659
+ process.stderr.write('[uploadImageFromClipboard] Windows读取剪贴板失败: ' + String(err) + '\n');
678
660
  return {
679
661
  content: [{
680
662
  type: "text",
@@ -690,17 +672,17 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
690
672
  }
691
673
  // macOS: 使用 pngpaste
692
674
  else if (process.platform === 'darwin') {
693
- console.log('[uploadImageFromClipboard] macOS平台,使用pngpaste读取剪贴板...');
675
+ process.stdout.write('[uploadImageFromClipboard] macOS平台,使用pngpaste读取剪贴板...\n');
694
676
  const tempFile = path.join(process.cwd(), '.temp_clipboard.png');
695
677
  try {
696
678
  execSync(`pngpaste "${tempFile}"`);
697
679
  fileBuffer = fs.readFileSync(tempFile);
698
680
  fs.unlinkSync(tempFile);
699
681
  finalFilename = filename || `clipboard_${Date.now()}.png`;
700
- console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
682
+ process.stdout.write(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes\n`);
701
683
  }
702
684
  catch (err) {
703
- console.error('[uploadImageFromClipboard] macOS读取剪贴板失败:', err);
685
+ process.stderr.write('[uploadImageFromClipboard] macOS读取剪贴板失败: ' + String(err) + '\n');
704
686
  return {
705
687
  content: [{
706
688
  type: "text",
@@ -715,17 +697,17 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
715
697
  }
716
698
  // Linux: 使用 xclip
717
699
  else {
718
- console.log('[uploadImageFromClipboard] Linux平台,使用xclip读取剪贴板...');
700
+ process.stdout.write('[uploadImageFromClipboard] Linux平台,使用xclip读取剪贴板...\n');
719
701
  try {
720
702
  const buffer = execSync('xclip -selection clipboard -t image/png -o', {
721
703
  maxBuffer: 10 * 1024 * 1024
722
704
  });
723
705
  fileBuffer = buffer;
724
706
  finalFilename = filename || `clipboard_${Date.now()}.png`;
725
- console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
707
+ process.stdout.write(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes\n`);
726
708
  }
727
709
  catch (err) {
728
- console.error('[uploadImageFromClipboard] Linux读取剪贴板失败:', err);
710
+ process.stderr.write('[uploadImageFromClipboard] Linux读取剪贴板失败: ' + String(err) + '\n');
729
711
  return {
730
712
  content: [{
731
713
  type: "text",
@@ -741,21 +723,21 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
741
723
  // 创建 img 文件夹
742
724
  const imgDir = path.join(process.cwd(), 'img');
743
725
  if (!fs.existsSync(imgDir)) {
744
- console.log(`[uploadImageFromClipboard] 创建目录: ${imgDir}`);
726
+ process.stdout.write(`[uploadImageFromClipboard] 创建目录: ${imgDir}\n`);
745
727
  fs.mkdirSync(imgDir, { recursive: true });
746
728
  }
747
729
  // 保存到 img 文件夹
748
730
  const savedPath = path.join(imgDir, finalFilename);
749
731
  fs.writeFileSync(savedPath, fileBuffer);
750
- console.log(`[uploadImageFromClipboard] 图片已保存到: ${savedPath}`);
732
+ process.stdout.write(`[uploadImageFromClipboard] 图片已保存到: ${savedPath}\n`);
751
733
  // 上传到禅道
752
- console.log('[uploadImageFromClipboard] 开始上传到禅道...');
734
+ process.stdout.write('[uploadImageFromClipboard] 开始上传到禅道...\n');
753
735
  const uploadResult = await zentaoApi.uploadFile({
754
736
  file: fileBuffer,
755
737
  filename: finalFilename,
756
738
  uid
757
739
  });
758
- console.log('[uploadImageFromClipboard] 上传成功,结果:', uploadResult);
740
+ process.stdout.write('[uploadImageFromClipboard] 上传成功,结果: ' + JSON.stringify(uploadResult) + '\n');
759
741
  // 生成禅道需要的 HTML 格式
760
742
  const fileId = uploadResult.id;
761
743
  const baseUrl = zentaoApi.getConfig().url;
@@ -773,7 +755,7 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
773
755
  message: `图片已保存到本地并上传到禅道`,
774
756
  tip: `更新 Bug 描述时,请使用 imageHtml 字段中的 HTML 代码。图片会被包裹在 <p> 标签中以确保正确显示。`
775
757
  };
776
- console.log('[uploadImageFromClipboard] 返回结果:', response);
758
+ process.stdout.write('[uploadImageFromClipboard] 返回结果: ' + JSON.stringify(response) + '\n');
777
759
  return {
778
760
  content: [{
779
761
  type: "text",
@@ -782,7 +764,7 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
782
764
  };
783
765
  }
784
766
  catch (error) {
785
- console.error('[uploadImageFromClipboard] 发生错误:', error);
767
+ process.stderr.write('[uploadImageFromClipboard] 发生错误: ' + String(error) + '\n');
786
768
  const errorResponse = {
787
769
  success: false,
788
770
  error: error.message || String(error),
@@ -928,4 +910,7 @@ server.tool("deleteComment", "删除评论 - 只能删除自己的评论,管
928
910
  });
929
911
  // Start receiving messages on stdin and sending messages on stdout
930
912
  const transport = new StdioServerTransport();
931
- await server.connect(transport).catch(console.error);
913
+ await server.connect(transport).catch(err => {
914
+ process.stderr.write('[FATAL] MCP server failed: ' + String(err) + '\n');
915
+ process.exit(1);
916
+ });
package/dist/index-qa.js CHANGED
@@ -14,15 +14,12 @@ if (configIndex !== -1 && configIndex + 1 < args.length) {
14
14
  // 获取 --config 后面的 JSON 字符串并解析
15
15
  const jsonStr = args[configIndex + 1];
16
16
  configData = JSON.parse(jsonStr);
17
- console.log('成功解析配置数据:', configData);
18
17
  // 如果配置数据中包含 config 对象,则保存配置
19
18
  if (configData.config) {
20
- console.log('正在保存配置...');
21
19
  saveConfig(configData.config);
22
20
  }
23
21
  }
24
22
  catch (error) {
25
- console.error('配置解析失败:', error);
26
23
  process.exit(1);
27
24
  }
28
25
  }
@@ -34,10 +31,8 @@ const server = new McpServer({
34
31
  // Initialize ZentaoAPI instance
35
32
  let zentaoApi = null;
36
33
  export default async function main(params) {
37
- console.log('接收到的参数:', params);
38
34
  // 如果传入了配置信息,就保存它
39
35
  if (params.config) {
40
- console.log('保存新的配置信息...');
41
36
  saveConfig(params.config);
42
37
  }
43
38
  }
@@ -455,13 +450,11 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
455
450
  const path = await import('path');
456
451
  const { execSync } = await import('child_process');
457
452
  try {
458
- console.log('[uploadImageFromClipboard] 开始执行...');
459
453
  // 读取系统剪贴板图片
460
454
  let fileBuffer;
461
455
  let finalFilename;
462
456
  // Windows: 使用 PowerShell 脚本读取剪贴板
463
457
  if (process.platform === 'win32') {
464
- console.log('[uploadImageFromClipboard] Windows平台,使用PowerShell脚本读取剪贴板...');
465
458
  // 内嵌 PowerShell 脚本,避免依赖外部文件
466
459
  const psScript = `Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); $bytes = $ms.ToArray(); $ms.Close(); $base64 = [Convert]::ToBase64String($bytes); Write-Output $base64 } else { Write-Output 'NoImage' }`;
467
460
  try {
@@ -473,7 +466,6 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
473
466
  stdio: ['pipe', 'pipe', 'pipe']
474
467
  }).trim();
475
468
  if (!result || result.includes('NoImage') || result.includes('Error')) {
476
- console.error('[uploadImageFromClipboard] 剪贴板中没有图片');
477
469
  return {
478
470
  content: [{
479
471
  type: "text",
@@ -487,10 +479,8 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
487
479
  }
488
480
  fileBuffer = Buffer.from(result, 'base64');
489
481
  finalFilename = filename || `clipboard_${Date.now()}.png`;
490
- console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
491
482
  }
492
483
  catch (err) {
493
- console.error('[uploadImageFromClipboard] Windows读取剪贴板失败:', err);
494
484
  return {
495
485
  content: [{
496
486
  type: "text",
@@ -506,17 +496,14 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
506
496
  }
507
497
  // macOS: 使用 pngpaste
508
498
  else if (process.platform === 'darwin') {
509
- console.log('[uploadImageFromClipboard] macOS平台,使用pngpaste读取剪贴板...');
510
499
  const tempFile = path.join(process.cwd(), '.temp_clipboard.png');
511
500
  try {
512
501
  execSync(`pngpaste "${tempFile}"`);
513
502
  fileBuffer = fs.readFileSync(tempFile);
514
503
  fs.unlinkSync(tempFile);
515
504
  finalFilename = filename || `clipboard_${Date.now()}.png`;
516
- console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
517
505
  }
518
506
  catch (err) {
519
- console.error('[uploadImageFromClipboard] macOS读取剪贴板失败:', err);
520
507
  return {
521
508
  content: [{
522
509
  type: "text",
@@ -531,17 +518,14 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
531
518
  }
532
519
  // Linux: 使用 xclip
533
520
  else {
534
- console.log('[uploadImageFromClipboard] Linux平台,使用xclip读取剪贴板...');
535
521
  try {
536
522
  const buffer = execSync('xclip -selection clipboard -t image/png -o', {
537
523
  maxBuffer: 10 * 1024 * 1024
538
524
  });
539
525
  fileBuffer = buffer;
540
526
  finalFilename = filename || `clipboard_${Date.now()}.png`;
541
- console.log(`[uploadImageFromClipboard] 已读取剪贴板图片,大小: ${fileBuffer.length} bytes`);
542
527
  }
543
528
  catch (err) {
544
- console.error('[uploadImageFromClipboard] Linux读取剪贴板失败:', err);
545
529
  return {
546
530
  content: [{
547
531
  type: "text",
@@ -557,21 +541,17 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
557
541
  // 创建 img 文件夹
558
542
  const imgDir = path.join(process.cwd(), 'img');
559
543
  if (!fs.existsSync(imgDir)) {
560
- console.log(`[uploadImageFromClipboard] 创建目录: ${imgDir}`);
561
544
  fs.mkdirSync(imgDir, { recursive: true });
562
545
  }
563
546
  // 保存到 img 文件夹
564
547
  const savedPath = path.join(imgDir, finalFilename);
565
548
  fs.writeFileSync(savedPath, fileBuffer);
566
- console.log(`[uploadImageFromClipboard] 图片已保存到: ${savedPath}`);
567
549
  // 上传到禅道
568
- console.log('[uploadImageFromClipboard] 开始上传到禅道...');
569
550
  const uploadResult = await zentaoApi.uploadFile({
570
551
  file: fileBuffer,
571
552
  filename: finalFilename,
572
553
  uid
573
554
  });
574
- console.log('[uploadImageFromClipboard] 上传成功,结果:', uploadResult);
575
555
  // 生成禅道需要的 HTML 格式
576
556
  const fileId = uploadResult.id;
577
557
  const baseUrl = zentaoApi.getConfig().url;
@@ -589,7 +569,6 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
589
569
  message: `图片已保存到本地并上传到禅道`,
590
570
  tip: `更新 Bug 描述时,请使用 imageHtml 字段中的 HTML 代码。图片会被包裹在 <p> 标签中以确保正确显示。`
591
571
  };
592
- console.log('[uploadImageFromClipboard] 返回结果:', response);
593
572
  return {
594
573
  content: [{
595
574
  type: "text",
@@ -598,7 +577,6 @@ server.tool("uploadImageFromClipboard", "上传剪贴板图片到禅道 - 自动
598
577
  };
599
578
  }
600
579
  catch (error) {
601
- console.error('[uploadImageFromClipboard] 发生错误:', error);
602
580
  const errorResponse = {
603
581
  success: false,
604
582
  error: error.message || String(error),
@@ -744,4 +722,4 @@ server.tool("deleteComment", "删除评论 - 只能删除自己的评论,管
744
722
  });
745
723
  // Start receiving messages on stdin and sending messages on stdout
746
724
  const transport = new StdioServerTransport();
747
- await server.connect(transport).catch(console.error);
725
+ await server.connect(transport).catch(err => { process.stderr.write('[FATAL] MCP server failed: ' + String(err) + '\n'); process.exit(1); });