@atlisp/mcp 1.8.25 → 1.8.26

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.
@@ -851,7 +851,7 @@ var init_cad = __esm({
851
851
  throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
852
852
  });
853
853
  }
854
- async sendCommandWithResult(code, encoding = null) {
854
+ async sendCommandWithResult(code, encoding = null, raw = false) {
855
855
  if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
856
856
  return new Promise((resolve, reject) => {
857
857
  this._commandQueue.enqueue({
@@ -863,7 +863,12 @@ var init_cad = __esm({
863
863
  reject
864
864
  }, PRIORITY_NORMAL);
865
865
  }).then((result) => {
866
- if (result.success) return result.result || "";
866
+ if (result.success) {
867
+ if (raw && result.rawBase64 !== void 0) {
868
+ return { text: result.result || "", rawBase64: result.rawBase64 };
869
+ }
870
+ return result.result || "";
871
+ }
867
872
  throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
868
873
  });
869
874
  }
@@ -1224,8 +1229,8 @@ var init_cad = __esm({
1224
1229
  async sendCommand(code) {
1225
1230
  return _resolveConn().sendCommand(code);
1226
1231
  },
1227
- async sendCommandWithResult(code, encoding) {
1228
- return _resolveConn().sendCommandWithResult(code, encoding);
1232
+ async sendCommandWithResult(code, encoding, raw) {
1233
+ return _resolveConn().sendCommandWithResult(code, encoding, raw);
1229
1234
  },
1230
1235
  getVersion() {
1231
1236
  return _resolveConn().getVersion();
@@ -8529,6 +8534,7 @@ __export(cad_handlers_exports, {
8529
8534
  newDocument: () => newDocument,
8530
8535
  setSystemVariable: () => setSystemVariable
8531
8536
  });
8537
+ import iconv from "iconv-lite";
8532
8538
  async function connectCad(platform = null) {
8533
8539
  log(`connect_cad called${platform ? ` (target: ${platform})` : ""}, cad.connected before: ${cad.connected}`);
8534
8540
  try {
@@ -8611,9 +8617,15 @@ var init_cad_handlers = __esm({
8611
8617
  ${detail}`);
8612
8618
  }
8613
8619
  if (withResult) {
8614
- const result = await cad.sendCommandWithResult(trimmed, encoding);
8615
- if (result !== null) {
8616
- return mcpSuccess(result);
8620
+ const useRaw = encoding ? true : false;
8621
+ const resp = await cad.sendCommandWithResult(trimmed, encoding, useRaw);
8622
+ if (resp !== null) {
8623
+ if (useRaw && resp.rawBase64) {
8624
+ const buf = Buffer.from(resp.rawBase64, "base64");
8625
+ const decoded = iconv.decode(buf, encoding);
8626
+ return mcpSuccess(decoded);
8627
+ }
8628
+ return mcpSuccess(resp);
8617
8629
  }
8618
8630
  return mcpError("\u6267\u884C\u5931\u8D25\u6216\u65E0\u8FD4\u56DE\u503C");
8619
8631
  }
@@ -18771,7 +18783,7 @@ import { randomUUID as randomUUID6, createHash } from "crypto";
18771
18783
  import fs14 from "fs";
18772
18784
  import path18 from "path";
18773
18785
  import { fileURLToPath as fileURLToPath7 } from "url";
18774
- import iconv from "iconv-lite";
18786
+ import iconv2 from "iconv-lite";
18775
18787
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
18776
18788
 
18777
18789
  // src/cad-event-bus.js
@@ -18982,7 +18994,7 @@ function createJsonBodyParser() {
18982
18994
  let charset = m ? m[1].toLowerCase() : "utf-8";
18983
18995
  const charsetMap = { "gb2312": "gbk", "gb18030": "gbk", "gbk": "gbk", "936": "gbk" };
18984
18996
  charset = charsetMap[charset] || charset;
18985
- const str = iconv.decode(buf, charset);
18997
+ const str = iconv2.decode(buf, charset);
18986
18998
  req.body = JSON.parse(str);
18987
18999
  next();
18988
19000
  } catch (e) {
Binary file
@@ -257,6 +257,7 @@ string encoding = d.encoding != null ? d.encoding.ToString() : "";
257
257
  while (retries > 0) {
258
258
  try {
259
259
  byte[] bytes = System.IO.File.ReadAllBytes(tempFile);
260
+ string rawBase64 = System.Convert.ToBase64String(bytes);
260
261
  System.Text.Encoding enc;
261
262
 
262
263
  if (!string.IsNullOrEmpty(encoding)) {
@@ -294,9 +295,9 @@ string encoding = d.encoding != null ? d.encoding.ToString() : "";
294
295
  }
295
296
  }
296
297
  if (!string.IsNullOrEmpty(result)) {
297
- return new { success = true, result = result };
298
+ return new { success = true, result = result, rawBase64 = rawBase64 };
298
299
  }
299
- return new { success = true, result = "" };
300
+ return new { success = true, result = "", rawBase64 = rawBase64 };
300
301
  }
301
302
  return new { success = true, result = "" };
302
303
  }
@@ -0,0 +1,242 @@
1
+ import { cad } from '../cad.js';
2
+ import { escapeLispString } from '../handler-utils.js';
3
+
4
+ function ensureCad() {
5
+ if (!cad.connected) throw new Error('未连接 CAD');
6
+ }
7
+
8
+ function esc(s) {
9
+ return escapeLispString(String(s));
10
+ }
11
+
12
+ function buildFileList(files) {
13
+ return files.map(f => `"${esc(f)}"`).join(' ');
14
+ }
15
+
16
+ export async function batchDocCheckStandards(args) {
17
+ ensureCad();
18
+ const { files, requiredLayers, forbiddenLayerPrefixes, requiredTextStyles, requiredDimStyles, maxLayerCount } = args;
19
+ if (!files?.length) return { content: [{ type: 'text', text: '需要 files 参数' }], isError: true };
20
+
21
+ const fileList = buildFileList(files);
22
+ const reqLayers = (requiredLayers || []).map(l => `"${esc(l)}"`).join(' ');
23
+ const forPrefixes = (forbiddenLayerPrefixes || []).map(p => `"${esc(p)}"`).join(' ');
24
+ const reqTStyles = (requiredTextStyles || []).map(s => `"${esc(s)}"`).join(' ');
25
+ const reqDStyles = (requiredDimStyles || []).map(s => `"${esc(s)}"`).join(' ');
26
+
27
+ const lisp = `(progn
28
+ (vl-load-com)
29
+ (setq files (list ${fileList}))
30
+ (setq req-layers (list ${reqLayers}))
31
+ (setq for-prefixes (list ${forPrefixes}))
32
+ (setq req-tstyles (list ${reqTStyles}))
33
+ (setq req-dstyles (list ${reqDStyles}))
34
+ (setq max-layer ${maxLayerCount || 9999})
35
+ (setq all-reports (list))
36
+ (foreach f files
37
+ (setq report (list (cons "file" f) (cons "ok" T) (cons "issues" (list))))
38
+ (if (findfile f)
39
+ (progn
40
+ (command "._OPEN" f)
41
+ (setq issues (list))
42
+ ;; check required layers
43
+ (foreach l req-layers
44
+ (if (not (tblsearch "LAYER" l))
45
+ (setq issues (cons (list "missing_layer" (strcat "缺少必需图层: " l)) issues))))
46
+ ;; check forbidden layer prefixes
47
+ (setq all-layers (list))
48
+ (setq i 0)
49
+ (while (setq e (tblnext "LAYER" (if (= i 0) T nil)))
50
+ (setq all-layers (cons (cdr (assoc 2 e)) all-layers))
51
+ (setq i (1+ i)))
52
+ (foreach p for-prefixes
53
+ (foreach l all-layers
54
+ (if (wcmatch (strcase l) (strcat (strcase p) "*"))
55
+ (setq issues (cons (list "forbidden_layer" (strcat "禁止图层前缀: " l " 匹配 " p)) issues)))))
56
+ ;; check layer count
57
+ (if (> (length all-layers) max-layer)
58
+ (setq issues (cons (list "too_many_layers" (strcat "图层数量 " (itoa (length all-layers)) " 超过限制 " (itoa max-layer))) issues)))
59
+ ;; check required text styles
60
+ (foreach s req-tstyles
61
+ (if (not (tblsearch "STYLE" s))
62
+ (setq issues (cons (list "missing_text_style" (strcat "缺少必需文字样式: " s)) issues))))
63
+ ;; check required dim styles
64
+ (foreach s req-dstyles
65
+ (if (not (tblsearch "DIMSTYLE" s))
66
+ (setq issues (cons (list "missing_dim_style" (strcat "缺少必需标注样式: " s)) issues))))
67
+ (if issues
68
+ (progn
69
+ (setq report (cons (cons "ok" nil) report))
70
+ (setq report (cons (cons "issues" issues) report)))
71
+ )
72
+ (command "_.CLOSE" "_N")
73
+ )
74
+ (setq report (cons (cons "ok" nil) (cons (cons "issues" (list (list "file_not_found" "文件不存在"))) report)))
75
+ )
76
+ (setq all-reports (cons report all-reports))
77
+ )
78
+ (princ all-reports)
79
+ (princ))
80
+ `;
81
+
82
+ try {
83
+ const raw = await cad.sendCommandWithResult(lisp);
84
+ let reports;
85
+ try {
86
+ reports = typeof raw === 'string' ? JSON.parse(raw) : raw;
87
+ } catch {
88
+ reports = [{ file: 'parse_error', ok: false, issues: [{ type: 'parse_error', msg: String(raw).substring(0, 200) }] }];
89
+ }
90
+ return { content: [{ type: 'text', text: JSON.stringify(reports, null, 2) }] };
91
+ } catch (e) {
92
+ return { content: [{ type: 'text', text: `批量检查失败: ${e.message}` }], isError: true };
93
+ }
94
+ }
95
+
96
+ export async function batchDocFindReplace(args) {
97
+ ensureCad();
98
+ const { files, pattern, replacement, caseSensitive, textTypes } = args;
99
+ if (!files?.length) return { content: [{ type: 'text', text: '需要 files 参数' }], isError: true };
100
+ if (!pattern) return { content: [{ type: 'text', text: '需要 pattern 参数' }], isError: true };
101
+
102
+ const fileList = buildFileList(files);
103
+ const caseFlag = caseSensitive ? 'nil' : 'T';
104
+ const types = textTypes || ['ALL'];
105
+ const textFilter = types.includes('ALL')
106
+ ? '(cons 0 "TEXT,MTEXT")'
107
+ : `(cons 0 "${types.join(',')}")`;
108
+
109
+ const lisp = `(progn
110
+ (vl-load-com)
111
+ (setq files (list ${fileList}))
112
+ (setq find-pat "${esc(pattern)}")
113
+ (setq repl-text "${esc(replacement)}")
114
+ (setq all-results (list))
115
+ (foreach f files
116
+ (setq result (list (cons "file" f) (cons "ok" T) (cons "count" 0)))
117
+ (if (findfile f)
118
+ (progn
119
+ (command "._OPEN" f)
120
+ (setq count 0)
121
+ (setq ss (ssget "_X" ${textFilter}))
122
+ (if ss
123
+ (progn
124
+ (setq i 0)
125
+ (while (< i (sslength ss))
126
+ (setq e (ssname ss i))
127
+ (setq elist (entget e))
128
+ (setq old (cdr (assoc 1 elist)))
129
+ (if old
130
+ (progn
131
+ (setq new (vl-string-subst repl-text find-pat old))
132
+ (if (/= new old)
133
+ (progn
134
+ (setq elist (subst (cons 1 new) (assoc 1 elist) elist))
135
+ (entmod elist)
136
+ (setq count (1+ count))
137
+ )
138
+ )
139
+ )
140
+ )
141
+ (setq i (1+ i))
142
+ )
143
+ )
144
+ )
145
+ (setq result (cons (cons "count" count) result))
146
+ (command "_.QSAVE")
147
+ (command "_.CLOSE" "_Y")
148
+ )
149
+ (setq result (cons (cons "ok" nil) (cons (cons "error" "文件不存在") result)))
150
+ )
151
+ (setq all-results (cons result all-results))
152
+ )
153
+ (princ (vl-prin1-to-string all-results))
154
+ (princ))
155
+ `;
156
+
157
+ try {
158
+ const raw = await cad.sendCommandWithResult(lisp);
159
+ let results;
160
+ try {
161
+ results = typeof raw === 'string' ? JSON.parse(raw) : raw;
162
+ } catch {
163
+ results = [{ file: 'parse_error', ok: false, error: String(raw).substring(0, 200) }];
164
+ }
165
+ const total = results.reduce((s, r) => s + (r.count || 0), 0);
166
+ const text = `替换完成: 共处理 ${results.length} 个文件,${total} 处替换\n${JSON.stringify(results, null, 2)}`;
167
+ return { content: [{ type: 'text', text }] };
168
+ } catch (e) {
169
+ return { content: [{ type: 'text', text: `批量替换失败: ${e.message}` }], isError: true };
170
+ }
171
+ }
172
+
173
+ export async function batchDocCleanup(args) {
174
+ ensureCad();
175
+ const { files, purgeBlocks, purgeLayers, purgeStyles, runAudit, runOverkill, saveAndClose } = args;
176
+ if (!files?.length) return { content: [{ type: 'text', text: '需要 files 参数' }], isError: true };
177
+
178
+ const fileList = buildFileList(files);
179
+ const doBlocks = purgeBlocks !== false;
180
+ const doLayers = purgeLayers !== false;
181
+ const doStyles = purgeStyles !== false;
182
+ const doAudit = runAudit !== false;
183
+
184
+ const lisp = `(progn
185
+ (vl-load-com)
186
+ (setq files (list ${fileList}))
187
+ (setq all-results (list))
188
+ (foreach f files
189
+ (setq result (list (cons "file" f) (cons "ok" T)))
190
+ (if (findfile f)
191
+ (progn
192
+ (command "._OPEN" f)
193
+ (setq details (list))
194
+ ;; audit
195
+ (if ${doAudit}
196
+ (progn
197
+ (command "_.AUDIT" "_Y")
198
+ (setq details (cons "audit_done" details))
199
+ )
200
+ )
201
+ ;; purge
202
+ (if ${doBlocks} (command "_.PURGE" "_B" "*" "_N"))
203
+ (if ${doLayers} (command "_.PURGE" "_LA" "*" "_N"))
204
+ (if ${doStyles} (command "_.PURGE" "_ST" "*" "_N"))
205
+ (setq details (cons "purge_done" details))
206
+ ;; overkill
207
+ (if ${runOverkill}
208
+ (progn
209
+ (command "_.OVERKILL" (ssget "_X") "" "_Y")
210
+ (setq details (cons "overkill_done" details))
211
+ )
212
+ )
213
+ (setq result (cons (cons "actions" details) result))
214
+ (if ${saveAndClose}
215
+ (progn
216
+ (command "_.QSAVE")
217
+ (command "_.CLOSE" "_Y")
218
+ )
219
+ (command "_.CLOSE" "_N")
220
+ )
221
+ )
222
+ (setq result (cons (cons "ok" nil) (cons (cons "error" "文件不存在") result)))
223
+ )
224
+ (setq all-results (cons result all-results))
225
+ )
226
+ (princ (vl-prin1-to-string all-results))
227
+ (princ))
228
+ `;
229
+
230
+ try {
231
+ const raw = await cad.sendCommandWithResult(lisp);
232
+ let results;
233
+ try {
234
+ results = typeof raw === 'string' ? JSON.parse(raw) : raw;
235
+ } catch {
236
+ results = [{ file: 'parse_error', ok: false, error: String(raw).substring(0, 200) }];
237
+ }
238
+ return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
239
+ } catch (e) {
240
+ return { content: [{ type: 'text', text: `批量清理失败: ${e.message}` }], isError: true };
241
+ }
242
+ }
@@ -1,3 +1,4 @@
1
+ import iconv from 'iconv-lite';
1
2
  import { cad } from '../cad.js';
2
3
  import { log } from '../logger.js';
3
4
  import { withCadConnection, mcpSuccess, mcpError, escapeLispString } from '../handler-utils.js';
@@ -32,9 +33,15 @@ export const evalLisp = withCadConnection(async (code, withResult = false, encod
32
33
  }
33
34
 
34
35
  if (withResult) {
35
- const result = await cad.sendCommandWithResult(trimmed, encoding);
36
- if (result !== null) {
37
- return mcpSuccess(result);
36
+ const useRaw = encoding ? true : false;
37
+ const resp = await cad.sendCommandWithResult(trimmed, encoding, useRaw);
38
+ if (resp !== null) {
39
+ if (useRaw && resp.rawBase64) {
40
+ const buf = Buffer.from(resp.rawBase64, 'base64');
41
+ const decoded = iconv.decode(buf, encoding);
42
+ return mcpSuccess(decoded);
43
+ }
44
+ return mcpSuccess(resp);
38
45
  }
39
46
  return mcpError('执行失败或无返回值');
40
47
  }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "analyze-and-report",
3
+ "description": "分析当前图纸实体并生成统计报告。先获取所有实体,再逐个获取详细信息",
4
+ "steps": [
5
+ {
6
+ "name": "entities",
7
+ "tool": "stream_entities",
8
+ "args": { "chunk": 500, "offset": 0 }
9
+ },
10
+ {
11
+ "name": "info",
12
+ "tool": "get_cad_info",
13
+ "args": {}
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "batch-export-pdfs",
3
+ "description": "遍历所有布局并逐个导出为 PDF。先列出布局,再对每个布局执行导出",
4
+ "steps": [
5
+ {
6
+ "name": "layouts",
7
+ "tool": "list_layouts",
8
+ "args": {}
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "batch-layer-move",
3
+ "description": "按条件选择实体并移动到目标图层。先选择指定类型/图层的实体,再移动到新图层",
4
+ "steps": [
5
+ {
6
+ "name": "selection",
7
+ "tool": "select_entities",
8
+ "args": { "filter": { "type": "LINE", "layer": "0" } }
9
+ },
10
+ {
11
+ "name": "move",
12
+ "tool": "batch_set_layer",
13
+ "args": {
14
+ "handles": "$ref:selection.result",
15
+ "layerName": "TargetLayer"
16
+ }
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "export-current-drawing",
3
+ "description": "导出当前图纸为 PDF。先获取图纸信息,再执行导出",
4
+ "steps": [
5
+ {
6
+ "name": "info",
7
+ "tool": "get_cad_info",
8
+ "args": {}
9
+ },
10
+ {
11
+ "name": "export",
12
+ "tool": "export_pdf",
13
+ "args": {
14
+ "outputPath": "$ref:info.docName.pdf"
15
+ }
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "purge-and-save",
3
+ "description": "清理未使用的块、图层、样式,然后保存当前图形",
4
+ "steps": [
5
+ {
6
+ "name": "purge_blocks",
7
+ "tool": "purge_all",
8
+ "args": {}
9
+ },
10
+ {
11
+ "name": "save",
12
+ "tool": "save_dwg",
13
+ "args": {}
14
+ }
15
+ ]
16
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlisp/mcp",
3
- "version": "1.8.25",
3
+ "version": "1.8.26",
4
4
  "description": "MCP Server for @lisp on CAD,support AutoCAD/GstarCAD/ZWCAD/BricsCAD or CAD platform compatible with AutoLISP",
5
5
  "type": "module",
6
6
  "bin": {