zwcad-mcp 0.2.1 → 0.2.2

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 (3) hide show
  1. package/README.md +4 -1
  2. package/lib/proxy.js +135 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  中望CAD(ZWCAD)MCP 服务,让大模型通过 [MCP 协议](https://modelcontextprotocol.io)直接操控中望CAD 平台与中望机械CAD,完成绘图、标注、图层/块/样式管理,以及图框、标题栏、明细表(BOM)等机械操作。
4
4
 
5
- **共计 39 个工具**:平台 26 个(不依赖机械环境)+ 机械 11 个(需中望机械CAD)+ 诊断 2 个。
5
+ **共计 41 个工具**:平台 26 个(不依赖机械环境)+ 机械 11 个(需中望机械CAD)+ 诊断 2 个 + 启动器侧 2 个。
6
+
7
+ > 启动器侧的两个工具(`zwcad_wait_ready`、`zwcad_launcher_diagnose`)在 Python 运行时尚未就绪时也可用,
8
+ > 用于等待初始化完成、以及在首次安装失败时自助排查。
6
9
 
7
10
  ## 为什么需要 Node 启动器
8
11
 
package/lib/proxy.js CHANGED
@@ -30,7 +30,7 @@
30
30
  const fs = require('fs');
31
31
  const path = require('path');
32
32
  const { spawn } = require('child_process');
33
- const { logError } = require('./errorlog');
33
+ const { logError, logFilePath } = require('./errorlog');
34
34
 
35
35
  const PROTOCOL_VERSION = '2024-11-05';
36
36
 
@@ -72,6 +72,26 @@ const PROXY_TOOLS = [
72
72
  additionalProperties: false,
73
73
  },
74
74
  },
75
+ {
76
+ name: 'zwcad_launcher_diagnose',
77
+ description:
78
+ '诊断启动器与 Python 运行时的状态,并返回最近的错误记录。\n\n' +
79
+ '与 zwcad_diagnose 的区别:本工具由 JS 启动器实现,**在 Python 尚未就绪时也可用**,' +
80
+ '因此是排查「首次安装失败 / 一直停在初始化」的首选;' +
81
+ 'zwcad_diagnose 依赖 Python 侧,只能在其就绪后调用。\n\n' +
82
+ '返回内容包括:运行时状态、Python 解释器路径与版本、虚拟环境状态、' +
83
+ '最近若干条错误日志(含事件名与诊断细节)。',
84
+ inputSchema: {
85
+ type: 'object',
86
+ properties: {
87
+ limit: {
88
+ type: 'number',
89
+ description: '返回最近多少条错误日志,默认 10,上限 50。',
90
+ },
91
+ },
92
+ additionalProperties: false,
93
+ },
94
+ },
75
95
  ];
76
96
 
77
97
  /**
@@ -494,13 +514,14 @@ function createProxy(opts) {
494
514
  } else if (r.state === 'failed') {
495
515
  text =
496
516
  `服务初始化失败:${r.error}\n\n` +
497
- `请让用户执行以下命令查看诊断:\n npx -y zwcad-mcp --doctor\n\n` +
517
+ `下一步:调用 zwcad_launcher_diagnose 获取完整的错误记录与运行环境信息,` +
518
+ `据此判断原因并告知用户具体怎么修。\n\n` +
498
519
  `常见原因:未安装 Python 3.10+、网络无法访问 PyPI、磁盘空间不足。`;
499
520
  } else {
500
521
  text =
501
522
  `已等待 ${seconds} 秒,初始化仍未完成,仍在安装依赖。\n\n` +
502
- `可以再调用一次本工具继续等待,或告知用户首次安装耗时较长、请稍候。\n` +
503
- `若累计超过 2 分钟仍无进展,让用户执行:\n npx -y zwcad-mcp --doctor`;
523
+ `下一步:可再调用一次本工具继续等待;若累计超过 2 分钟仍无进展,` +
524
+ `调用 zwcad_launcher_diagnose 查看错误记录。`;
504
525
  }
505
526
 
506
527
  writeJson({
@@ -514,6 +535,19 @@ function createProxy(opts) {
514
535
  return;
515
536
  }
516
537
 
538
+ if (name === 'zwcad_launcher_diagnose') {
539
+ let limit = Number(args.limit);
540
+ if (!Number.isFinite(limit) || limit <= 0) limit = 10;
541
+ limit = Math.min(limit, 50);
542
+
543
+ writeJson({
544
+ jsonrpc: '2.0',
545
+ id: msg.id,
546
+ result: { content: [{ type: 'text', text: buildDiagnosisReport(limit) }] },
547
+ });
548
+ return;
549
+ }
550
+
517
551
  writeJson({
518
552
  jsonrpc: '2.0',
519
553
  id: msg.id,
@@ -524,6 +558,103 @@ function createProxy(opts) {
524
558
  });
525
559
  }
526
560
 
561
+ /**
562
+ * 生成诊断报告。
563
+ *
564
+ * 目标是让 AI 读完就能判断问题出在哪,并把结论用自然语言转述给用户,
565
+ * 而不是把一坨技术细节甩给用户让他自己去跑命令。
566
+ */
567
+ function buildDiagnosisReport(limit) {
568
+ const lines = [];
569
+ const stateText =
570
+ runtimeState === 'ready' && childReady
571
+ ? '已就绪'
572
+ : runtimeState === 'failed'
573
+ ? '失败'
574
+ : '正在准备';
575
+
576
+ lines.push('## 运行时状态');
577
+ lines.push(`状态:${stateText}`);
578
+ if (runtimeState === 'failed' && runtimeError) {
579
+ lines.push(`错误:${runtimeError}`);
580
+ }
581
+ if (childReady) {
582
+ lines.push('Python 侧已完成握手,可以正常接受请求。');
583
+ }
584
+
585
+ lines.push('');
586
+ lines.push('## 运行环境');
587
+ lines.push(`Node:${process.version}(${process.platform}/${process.arch})`);
588
+ lines.push(`启动器版本:${opts.version || '未知'}`);
589
+ lines.push(`工作目录:${cwd}`);
590
+ lines.push(
591
+ `环境变量:ZWCAD_MCP_PYTHON=${process.env.ZWCAD_MCP_PYTHON || '(未设置)'},` +
592
+ `ZWCAD_MCP_PIP_INDEX=${process.env.ZWCAD_MCP_PIP_INDEX || '(未设置)'}`
593
+ );
594
+
595
+ lines.push('');
596
+ lines.push('## 错误记录');
597
+ const entries = readRecentErrors(limit);
598
+ if (entries.length === 0) {
599
+ lines.push('无错误记录。若问题仍存在,可能是尚在安装中,或故障未落到日志点。');
600
+ } else {
601
+ lines.push(`日志文件:${logFilePath()}`);
602
+ lines.push('');
603
+ for (const e of entries) {
604
+ lines.push(`- [${e.time}] **${e.event}**`);
605
+ if (e.detail) {
606
+ lines.push(' ' + e.detail.replace(/\n/g, '\n '));
607
+ }
608
+ }
609
+ }
610
+
611
+ lines.push('');
612
+ lines.push('## 如何判读');
613
+ lines.push('- `runtime.python.not_found`:本机没有 3.10+ 的 Python。');
614
+ lines.push(' 让用户装 Python(建议 3.13),或设 `ZWCAD_MCP_PYTHON` 指向已有解释器。');
615
+ lines.push('- `runtime.install.failed`:装依赖失败。看 detail 里的 `pipIndex` 与 `error`,');
616
+ lines.push(' 多为网络问题,让用户设 `ZWCAD_MCP_PIP_INDEX` 为国内镜像后重连。');
617
+ lines.push('- `python.spawn.failed` / `python.exited`:Python 进程起不来或崩溃,');
618
+ lines.push(' 看 detail 里的 `stderrTail`(Python 自身的异常堆栈)。');
619
+ lines.push('- 无任何错误记录但仍不可用:多半仍在安装,让其再等或重连。');
620
+
621
+ return lines.join('\n');
622
+ }
623
+
624
+ /**
625
+ * 读取最近 N 条错误日志,转成易读文本。
626
+ *
627
+ * 读取失败一律返回空数组——诊断工具自己不能因为读不到日志而报错。
628
+ */
629
+ function readRecentErrors(limit) {
630
+ let raw;
631
+ try {
632
+ raw = fs.readFileSync(logFilePath(), 'utf8');
633
+ } catch {
634
+ return [];
635
+ }
636
+
637
+ const out = [];
638
+ const all = raw.trim().split('\n').slice(-limit);
639
+ for (const line of all) {
640
+ try {
641
+ const r = JSON.parse(line);
642
+ let detail = '';
643
+ if (r.detail && Object.keys(r.detail).length > 0) {
644
+ try {
645
+ detail = JSON.stringify(r.detail, null, 1);
646
+ } catch {
647
+ detail = '[无法序列化]';
648
+ }
649
+ }
650
+ out.push({ time: r.time, event: r.event, detail });
651
+ } catch {
652
+ /* 跳过损坏行 */
653
+ }
654
+ }
655
+ return out;
656
+ }
657
+
527
658
  /**
528
659
  * 未就绪时的工具调用:返回结构化错误,让 AI 知道该等还是该修环境。
529
660
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zwcad-mcp",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "中望CAD(ZWCAD)MCP 服务:用自然语言驱动 ZWCAD 二维平台与中望机械 CAD,完成绘图、标注、图层管理与机械图框/BOM 操作",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",