@atlisp/mcp 1.3.7 → 1.4.0

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 (2) hide show
  1. package/dist/atlisp-mcp.js +267 -81
  2. package/package.json +1 -1
@@ -34,7 +34,8 @@ var SessionTransport = class {
34
34
  this._started = true;
35
35
  }
36
36
  async send(message) {
37
- if (this._closed) return;
37
+ if (this._closed)
38
+ return;
38
39
  if (this._pendingRequest) {
39
40
  const { resolve, reject, timeout } = this._pendingRequest;
40
41
  clearTimeout(timeout);
@@ -43,7 +44,8 @@ var SessionTransport = class {
43
44
  }
44
45
  }
45
46
  async close() {
46
- if (this._closed) return;
47
+ if (this._closed)
48
+ return;
47
49
  this._closed = true;
48
50
  if (this._pendingRequest) {
49
51
  const { reject, timeout } = this._pendingRequest;
@@ -117,7 +119,7 @@ var FILE_EXTENSIONS = {
117
119
  "BricsCAD": [".des"]
118
120
  };
119
121
  var SERVER_NAME = "atlisp-mcp-server";
120
- var SERVER_VERSION = "1.1.2";
122
+ var SERVER_VERSION = "1.4.0";
121
123
  var MOCK_PACKAGES = [
122
124
  { name: "base", description: "\u57FA\u7840\u51FD\u6570\u5E93", version: "1.0.0" },
123
125
  { name: "at-pm", description: "\u5DE5\u7A0B\u9879\u76EE\u7BA1\u7406", version: "2.1.0" },
@@ -210,7 +212,8 @@ function setupStdoutHandler(w) {
210
212
  const lines = buffer.split("\n");
211
213
  buffer = lines.pop() || "";
212
214
  for (const line of lines) {
213
- if (!line.trim()) continue;
215
+ if (!line.trim())
216
+ continue;
214
217
  try {
215
218
  const result = JSON.parse(line);
216
219
  const rid = result.requestId;
@@ -235,7 +238,8 @@ function setupStdoutHandler(w) {
235
238
  }
236
239
  function getWorker() {
237
240
  if (!worker || !worker.connected) {
238
- if (worker) worker.kill();
241
+ if (worker)
242
+ worker.kill();
239
243
  worker = spawn("node", [workerPath], {
240
244
  stdio: ["pipe", "pipe", "pipe"]
241
245
  });
@@ -244,7 +248,8 @@ function getWorker() {
244
248
  const w = worker;
245
249
  w.on("exit", (code) => {
246
250
  console.error("worker exited:", code);
247
- if (w !== worker) return;
251
+ if (w !== worker)
252
+ return;
248
253
  w.connected = false;
249
254
  for (const [id, { reject, timeout }] of pendingRequests) {
250
255
  clearTimeout(timeout);
@@ -263,7 +268,8 @@ function getWorker() {
263
268
  return worker;
264
269
  }
265
270
  function startHeartbeat() {
266
- if (heartbeatTimer) clearInterval(heartbeatTimer);
271
+ if (heartbeatTimer)
272
+ clearInterval(heartbeatTimer);
267
273
  heartbeatTimer = setInterval(async () => {
268
274
  try {
269
275
  const result = await sendMessage({ type: "ping" });
@@ -308,7 +314,8 @@ var CadConnection = class {
308
314
  return process.platform === "win32";
309
315
  }
310
316
  async connect() {
311
- if (!this.isAvailable()) return false;
317
+ if (!this.isAvailable())
318
+ return false;
312
319
  try {
313
320
  const result = await sendMessage({ type: "connect" });
314
321
  if (result && result.success) {
@@ -324,13 +331,16 @@ var CadConnection = class {
324
331
  return false;
325
332
  }
326
333
  async sendCommand(code) {
327
- if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
334
+ if (!this.connected)
335
+ throw new Error("\u672A\u8FDE\u63A5 CAD");
328
336
  const result = await sendMessage({ type: "send", code, platform: _platform });
329
- if (result.success) return true;
337
+ if (result.success)
338
+ return true;
330
339
  throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
331
340
  }
332
341
  async sendCommandWithResult(code, encoding = null) {
333
- if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
342
+ if (!this.connected)
343
+ throw new Error("\u672A\u8FDE\u63A5 CAD");
334
344
  const result = await sendMessage({ type: "sendResult", code, platform: _platform, encoding: encoding || "" });
335
345
  if (result.success) {
336
346
  return result.result || "";
@@ -344,7 +354,8 @@ var CadConnection = class {
344
354
  return this.product;
345
355
  }
346
356
  async isBusy() {
347
- if (!this.connected) return false;
357
+ if (!this.connected)
358
+ return false;
348
359
  try {
349
360
  const result = await sendMessage({ type: "isBusy", platform: _platform });
350
361
  return result && result.isBusy;
@@ -353,7 +364,8 @@ var CadConnection = class {
353
364
  }
354
365
  }
355
366
  async hasDoc() {
356
- if (!this.connected) return { hasDoc: false, docCount: 0 };
367
+ if (!this.connected)
368
+ return { hasDoc: false, docCount: 0 };
357
369
  try {
358
370
  const result = await sendMessage({ type: "hasdoc", platform: _platform });
359
371
  return result || { hasDoc: false, docCount: 0 };
@@ -362,7 +374,8 @@ var CadConnection = class {
362
374
  }
363
375
  }
364
376
  async newDoc() {
365
- if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
377
+ if (!this.connected)
378
+ throw new Error("\u672A\u8FDE\u63A5 CAD");
366
379
  try {
367
380
  const result = await sendMessage({ type: "newdoc", platform: _platform });
368
381
  return result && result.success;
@@ -371,7 +384,8 @@ var CadConnection = class {
371
384
  }
372
385
  }
373
386
  async bringToFront() {
374
- if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
387
+ if (!this.connected)
388
+ throw new Error("\u672A\u8FDE\u63A5 CAD");
375
389
  try {
376
390
  const result = await sendMessage({ type: "bringToFront", platform: _platform });
377
391
  return result && result.success;
@@ -380,7 +394,8 @@ var CadConnection = class {
380
394
  }
381
395
  }
382
396
  async getInfo() {
383
- if (!this.connected) return "CAD \u672A\u8FDE\u63A5";
397
+ if (!this.connected)
398
+ return "CAD \u672A\u8FDE\u63A5";
384
399
  return `Platform: ${this.product}, Version: ${this.version}, Status: Connected`;
385
400
  }
386
401
  async disconnect() {
@@ -455,7 +470,8 @@ async function evalLisp(code, withResult = false, encoding = null) {
455
470
  return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
456
471
  }
457
472
  const trimmed = (code || "").trim();
458
- if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
473
+ if (!trimmed)
474
+ return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
459
475
  try {
460
476
  if (withResult) {
461
477
  const result = await cad.sendCommandWithResult(trimmed, encoding);
@@ -475,14 +491,17 @@ async function getCadInfo() {
475
491
  if (!cad.connected) {
476
492
  await cad.connect();
477
493
  }
478
- if (!cad.connected) return { content: [{ type: "text", text: "CAD \u672A\u8FDE\u63A5" }] };
494
+ if (!cad.connected)
495
+ return { content: [{ type: "text", text: "CAD \u672A\u8FDE\u63A5" }] };
479
496
  const info = await cad.getInfo();
480
497
  return { content: [{ type: "text", text: info }] };
481
498
  }
482
499
  async function atCommand(command) {
483
- if (!cad.connected) return { content: [{ type: "text", text: "\u8BF7\u5148\u8FDE\u63A5 CAD" }], isError: true };
500
+ if (!cad.connected)
501
+ return { content: [{ type: "text", text: "\u8BF7\u5148\u8FDE\u63A5 CAD" }], isError: true };
484
502
  const trimmed = (command || "").trim();
485
- if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
503
+ if (!trimmed)
504
+ return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
486
505
  await cad.sendCommand(trimmed + "\n");
487
506
  return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001\u547D\u4EE4" }] };
488
507
  }
@@ -490,7 +509,8 @@ async function newDocument() {
490
509
  if (!cad.connected) {
491
510
  await cad.connect();
492
511
  }
493
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
512
+ if (!cad.connected)
513
+ return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
494
514
  const result = await cad.newDoc();
495
515
  if (result) {
496
516
  return { content: [{ type: "text", text: "\u5DF2\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863" }] };
@@ -501,7 +521,8 @@ async function bringToFront() {
501
521
  if (!cad.connected) {
502
522
  await cad.connect();
503
523
  }
504
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
524
+ if (!cad.connected)
525
+ return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
505
526
  const result = await cad.bringToFront();
506
527
  if (result) {
507
528
  return { content: [{ type: "text", text: "\u5DF2\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0" }] };
@@ -512,7 +533,8 @@ async function installAtlisp() {
512
533
  if (!cad.connected) {
513
534
  await cad.connect();
514
535
  }
515
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
536
+ if (!cad.connected)
537
+ return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
516
538
  const installCode = `(progn(vl-load-com)(setq s strcat h "http" o(vlax-create-object (s"win"h".win"h"request.5.1"))v vlax-invoke e eval r read)(v o'open "get" (s h"://atlisp.""cn/@"):vlax-true)(v o'send)(v o'WaitforResponse 1000)(e(r(vlax-get-property o'ResponseText))))`;
517
539
  await cad.sendCommand(installCode + "\n");
518
540
  return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001 @lisp \u5B89\u88C5\u4EE3\u7801\u5230 CAD" }] };
@@ -521,20 +543,68 @@ async function listFunctionsInCad() {
521
543
  if (!cad.connected) {
522
544
  await cad.connect();
523
545
  }
524
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
546
+ if (!cad.connected)
547
+ return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
525
548
  try {
526
549
  const result = await cad.sendCommandWithResult("(atoms-family 0)", null);
527
- if (result) return { content: [{ type: "text", text: result }] };
550
+ if (result)
551
+ return { content: [{ type: "text", text: result }] };
528
552
  return { content: [{ type: "text", text: "CAD \u51FD\u6570\u5217\u8868\u4E3A\u7A7A" }] };
529
553
  } catch (e) {
530
554
  return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
531
555
  }
532
556
  }
557
+ async function getSystemStatus() {
558
+ const status = {
559
+ mcp: { version: process.env.npm_package_version || "unknown" },
560
+ worker: { alive: false },
561
+ cad: { connected: false, platform: null, version: null, busy: false, hasDoc: false },
562
+ lisp: { available: false, testResult: null, error: null },
563
+ packages: null
564
+ };
565
+ try {
566
+ status.worker.alive = await cad.ping();
567
+ } catch {
568
+ status.worker.alive = false;
569
+ }
570
+ status.cad.connected = cad.connected;
571
+ if (cad.connected) {
572
+ status.cad.platform = cad.getPlatform();
573
+ status.cad.version = cad.getVersion();
574
+ try {
575
+ status.cad.busy = await cad.isBusy();
576
+ } catch {
577
+ }
578
+ try {
579
+ const doc = await cad.hasDoc();
580
+ status.cad.hasDoc = doc.hasDoc;
581
+ } catch {
582
+ }
583
+ try {
584
+ const r = await cad.sendCommandWithResult("(+ 1 2)");
585
+ if (r !== null && r !== void 0) {
586
+ status.lisp.available = true;
587
+ status.lisp.testResult = String(r).trim();
588
+ }
589
+ } catch (e) {
590
+ status.lisp.error = e.message;
591
+ }
592
+ try {
593
+ const pkgResult = await cad.sendCommandWithResult("(@::package-list-in-local)");
594
+ if (pkgResult && pkgResult !== "nil") {
595
+ status.packages = pkgResult;
596
+ }
597
+ } catch {
598
+ }
599
+ }
600
+ return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
601
+ }
533
602
  async function initAtlisp() {
534
603
  if (!cad.connected) {
535
604
  await cad.connect();
536
605
  }
537
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
606
+ if (!cad.connected)
607
+ return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
538
608
  const code = `(if (null @::load-module)
539
609
  (progn
540
610
  (vl-load-com)
@@ -574,10 +644,13 @@ async function fetchPackages() {
574
644
  return null;
575
645
  }
576
646
  async function listPackages() {
577
- if (!cad.connected) await cad.connect();
578
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
647
+ if (!cad.connected)
648
+ await cad.connect();
649
+ if (!cad.connected)
650
+ return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
579
651
  const result = await cad.sendCommandWithResult("(@::package-list-in-local)");
580
- if (result && result !== "nil") return { content: [{ type: "text", text: result }] };
652
+ if (result && result !== "nil")
653
+ return { content: [{ type: "text", text: result }] };
581
654
  const packages = MOCK_PACKAGES.map((p) => `${p.name} v${p.version} - ${p.description}`);
582
655
  return { content: [{ type: "text", text: `\u5DF2\u5B89\u88C5\u7684 @lisp \u5305:
583
656
  ${packages.join("\n")}` }] };
@@ -594,10 +667,12 @@ async function searchPackages(query) {
594
667
  for (const p of items) {
595
668
  const name = (p.name || "").toLowerCase();
596
669
  const desc = (p.description || "").toLowerCase();
597
- if (name.includes(q) || desc.includes(q)) results.push(p);
670
+ if (name.includes(q) || desc.includes(q))
671
+ results.push(p);
598
672
  }
599
673
  } else {
600
- for (const p of items) results.push(p);
674
+ for (const p of items)
675
+ results.push(p);
601
676
  }
602
677
  if (results.length === 0) {
603
678
  return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5305\u542B "${query}" \u7684\u5305` }] };
@@ -639,7 +714,8 @@ async function getFunctionUsage(funcName, packageName) {
639
714
  const data = await response.json();
640
715
  const funcs = Object.values(data.all_functions || {});
641
716
  const func = funcs.find((f2) => f2.name === funcName || f2.name === `${packageName}:${funcName}`);
642
- if (func) results.push(func);
717
+ if (func)
718
+ results.push(func);
643
719
  }
644
720
  } catch (e) {
645
721
  log(`function-handlers fetch error: ${e.message}`);
@@ -650,7 +726,8 @@ async function getFunctionUsage(funcName, packageName) {
650
726
  const raw = await fs2.readFile(path3.join(FUNCTIONS_DIR, file), "utf-8");
651
727
  const data = JSON.parse(raw);
652
728
  const func = data.functions?.find((f2) => f2.name === funcName);
653
- if (func) results.push(func);
729
+ if (func)
730
+ results.push(func);
654
731
  }
655
732
  }
656
733
  if (results.length === 0) {
@@ -730,12 +807,26 @@ function getCached(key) {
730
807
  function setCache(key, data) {
731
808
  resourceCache.set(key, { data, timestamp: Date.now() });
732
809
  }
810
+ function clearCache(uri) {
811
+ if (uri) {
812
+ for (const key of resourceCache.keys()) {
813
+ if (key.startsWith(uri.split("://")[1]?.split("/")[0] + ":")) {
814
+ resourceCache.delete(key);
815
+ }
816
+ }
817
+ } else {
818
+ resourceCache.clear();
819
+ }
820
+ }
733
821
  function parseLispList(str) {
734
- if (!str || typeof str !== "string") return null;
822
+ if (!str || typeof str !== "string")
823
+ return null;
735
824
  const trimmed = str.trim();
736
- if (!trimmed.startsWith("(") || !trimmed.endsWith(")")) return null;
825
+ if (!trimmed.startsWith("(") || !trimmed.endsWith(")"))
826
+ return null;
737
827
  const content = trimmed.slice(1, -1).trim();
738
- if (!content) return [];
828
+ if (!content)
829
+ return [];
739
830
  const result = [];
740
831
  let depth = 0;
741
832
  let current = "";
@@ -777,15 +868,21 @@ function parseLispList(str) {
777
868
  }
778
869
  current += ch;
779
870
  }
780
- if (current.trim()) result.push(current.trim());
871
+ if (current.trim())
872
+ result.push(current.trim());
781
873
  return result.map((item) => {
782
874
  const t = item.trim();
783
- if (t === "nil") return null;
784
- if (t === "t") return true;
785
- if (t === "T") return true;
875
+ if (t === "nil")
876
+ return null;
877
+ if (t === "t")
878
+ return true;
879
+ if (t === "T")
880
+ return true;
786
881
  const num = Number(t);
787
- if (!isNaN(num) && t !== "") return num;
788
- if (t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
882
+ if (!isNaN(num) && t !== "")
883
+ return num;
884
+ if (t.startsWith('"') && t.endsWith('"'))
885
+ return t.slice(1, -1);
789
886
  return t;
790
887
  });
791
888
  }
@@ -800,14 +897,16 @@ var RESOURCES = [
800
897
  ];
801
898
  function parseQuery(uri) {
802
899
  const qIdx = uri.indexOf("?");
803
- if (qIdx === -1) return { base: uri, params: {} };
900
+ if (qIdx === -1)
901
+ return { base: uri, params: {} };
804
902
  const base = uri.substring(0, qIdx);
805
903
  const params = {};
806
904
  uri.substring(qIdx + 1).split("&").forEach((p) => {
807
905
  const eqIdx = p.indexOf("=");
808
906
  const k = eqIdx === -1 ? p : p.substring(0, eqIdx);
809
907
  const v = eqIdx === -1 ? "" : p.substring(eqIdx + 1);
810
- if (k) params[k] = decodeURIComponent(v);
908
+ if (k)
909
+ params[k] = decodeURIComponent(v);
811
910
  });
812
911
  return { base, params };
813
912
  }
@@ -843,7 +942,8 @@ async function readResource(uri) {
843
942
  }
844
943
  async function getCadInfo2() {
845
944
  const cached = getCached("cad:info");
846
- if (cached) return cached;
945
+ if (cached)
946
+ return cached;
847
947
  if (!cad.connected) {
848
948
  await cad.connect();
849
949
  }
@@ -870,9 +970,12 @@ async function getCadInfo2() {
870
970
  async function getLayers(filterName = null) {
871
971
  const cacheKey = filterName ? `layers:${filterName}` : "layers:all";
872
972
  const cached = getCached(cacheKey);
873
- if (cached) return cached;
874
- if (!cad.connected) await cad.connect();
875
- if (!cad.connected) return [];
973
+ if (cached)
974
+ return cached;
975
+ if (!cad.connected)
976
+ await cad.connect();
977
+ if (!cad.connected)
978
+ return [];
876
979
  try {
877
980
  const code = filterName ? `(tblnext "LAYER" T)` : `(progn (setq res nil)(while (setq e (tblnext "LAYER" (null res)))(setq res (cons (list (cdr (assoc 2 e)) (cdr (assoc 62 e)) (cdr (assoc 70 e))) res))) (reverse res))`;
878
981
  const result = await cad.sendCommandWithResult(code);
@@ -919,8 +1022,10 @@ async function getLayers(filterName = null) {
919
1022
  async function getEntities(filterType = null) {
920
1023
  const cacheKey = filterType ? `entities:${filterType}` : "entities:all";
921
1024
  const cached = getCached(cacheKey);
922
- if (cached) return cached;
923
- if (!cad.connected) await cad.connect();
1025
+ if (cached)
1026
+ return cached;
1027
+ if (!cad.connected)
1028
+ await cad.connect();
924
1029
  if (!cad.connected) {
925
1030
  const result = { total: 0, byType: {} };
926
1031
  setCache(cacheKey, result);
@@ -974,8 +1079,10 @@ async function getEntities(filterType = null) {
974
1079
  }
975
1080
  }
976
1081
  async function getDwgName() {
977
- if (!cad.connected) await cad.connect();
978
- if (!cad.connected) return { name: null };
1082
+ if (!cad.connected)
1083
+ await cad.connect();
1084
+ if (!cad.connected)
1085
+ return { name: null };
979
1086
  try {
980
1087
  const code = `(vl-princ-to-string (getvar "dwgname"))`;
981
1088
  const name = await cad.sendCommandWithResult(code);
@@ -985,8 +1092,10 @@ async function getDwgName() {
985
1092
  }
986
1093
  }
987
1094
  async function getDwgPath() {
988
- if (!cad.connected) await cad.connect();
989
- if (!cad.connected) return { path: null, name: null };
1095
+ if (!cad.connected)
1096
+ await cad.connect();
1097
+ if (!cad.connected)
1098
+ return { path: null, name: null };
990
1099
  try {
991
1100
  const code = `(progn
992
1101
  (setq prefix (vl-princ-to-string (getvar "dwgprefix")))
@@ -1004,7 +1113,8 @@ async function getDwgPath() {
1004
1113
  }
1005
1114
  if (!parsed) {
1006
1115
  const listResult = parseLispList(result);
1007
- if (listResult && listResult.length >= 2) parsed = listResult;
1116
+ if (listResult && listResult.length >= 2)
1117
+ parsed = listResult;
1008
1118
  }
1009
1119
  if (parsed && Array.isArray(parsed) && parsed.length >= 2) {
1010
1120
  return { path: String(parsed[0]), name: String(parsed[1]) };
@@ -1015,17 +1125,71 @@ async function getDwgPath() {
1015
1125
  }
1016
1126
  }
1017
1127
  async function getPackages(filterName = null) {
1018
- const packages = MOCK_PACKAGES.map((p) => ({
1019
- name: p.name,
1020
- version: p.version,
1021
- description: p.description,
1022
- loaded: true
1023
- }));
1024
- if (filterName) {
1025
- const pkg = packages.find((p) => p.name === filterName);
1026
- return pkg ? [pkg] : [];
1128
+ const cacheKey = filterName ? `packages:${filterName}` : "packages:all";
1129
+ const cached = getCached(cacheKey);
1130
+ if (cached)
1131
+ return cached;
1132
+ if (!cad.connected)
1133
+ await cad.connect();
1134
+ if (!cad.connected)
1135
+ return [];
1136
+ try {
1137
+ const code = `(mapcar 'cdr @::*local-pkgs*)`;
1138
+ const result = await cad.sendCommandWithResult(code);
1139
+ if (!result || result === "" || result === "nil") {
1140
+ setCache(cacheKey, []);
1141
+ return [];
1142
+ }
1143
+ let raw = [];
1144
+ try {
1145
+ raw = JSON.parse(result);
1146
+ } catch {
1147
+ raw = parseLispList(result) || [];
1148
+ }
1149
+ if (!Array.isArray(raw))
1150
+ raw = [];
1151
+ const packages = raw.map((item) => {
1152
+ if (Array.isArray(item)) {
1153
+ const pkg = { name: "", version: "0.0.0", description: "", loaded: true };
1154
+ for (const entry of item) {
1155
+ if (entry && typeof entry === "object" && !Array.isArray(entry)) {
1156
+ const [[key, val]] = Object.entries(entry);
1157
+ const k = String(key);
1158
+ const v = String(val);
1159
+ if (k === ":NAME")
1160
+ pkg.name = v;
1161
+ else if (k === ":VERSION")
1162
+ pkg.version = v;
1163
+ else if (k === ":DESCRIPTION")
1164
+ pkg.description = v;
1165
+ else if (k === ":FULL-NAME")
1166
+ pkg.fullName = v;
1167
+ else if (k === ":AUTHOR")
1168
+ pkg.author = v;
1169
+ else if (k === ":CATEGORY")
1170
+ pkg.category = v;
1171
+ else if (k === ":URL")
1172
+ pkg.url = v;
1173
+ }
1174
+ }
1175
+ return pkg;
1176
+ }
1177
+ if (typeof item === "string" || typeof item === "number") {
1178
+ return { name: String(item), version: "0.0.0", loaded: true };
1179
+ }
1180
+ return null;
1181
+ }).filter(Boolean);
1182
+ if (filterName) {
1183
+ const filtered = packages.filter((p) => p.name === filterName);
1184
+ setCache(cacheKey, filtered);
1185
+ return filtered;
1186
+ }
1187
+ setCache(cacheKey, packages);
1188
+ return packages;
1189
+ } catch (e) {
1190
+ setCache(cacheKey, []);
1191
+ return [];
1027
1192
  }
1028
- return packages;
1029
1193
  }
1030
1194
  function getPlatforms() {
1031
1195
  return CAD_PLATFORMS.map((name) => ({
@@ -1314,10 +1478,14 @@ ${generateSuggestions(entities, layers)}`
1314
1478
  }
1315
1479
  function generateSuggestions(entities, layers) {
1316
1480
  const suggestions = [];
1317
- if (entities.total > 1e3) suggestions.push("- \u56FE\u7EB8\u5B9E\u4F53\u8F83\u591A\uFF0C\u5EFA\u8BAE\u4F7F\u7528\u56FE\u5C42\u5206\u7C7B\u7BA1\u7406");
1318
- if (layers.length > 20) suggestions.push("- \u56FE\u5C42\u8F83\u591A\uFF0C\u5EFA\u8BAE\u6E05\u7406\u672A\u4F7F\u7528\u7684\u56FE\u5C42");
1319
- if (entities.byType.LINE && entities.byType.LINE > 500) suggestions.push("- \u7EBF\u6761\u8F83\u591A\uFF0C\u53EF\u8003\u8651\u4F7F\u7528 BLOCK \u51CF\u5C11\u5B9E\u4F53\u6570\u91CF");
1320
- if (!entities.byType.DIMENSION) suggestions.push("- \u672A\u53D1\u73B0\u5C3A\u5BF8\u6807\u6CE8\uFF0C\u5EFA\u8BAE\u6DFB\u52A0\u6807\u6CE8");
1481
+ if (entities.total > 1e3)
1482
+ suggestions.push("- \u56FE\u7EB8\u5B9E\u4F53\u8F83\u591A\uFF0C\u5EFA\u8BAE\u4F7F\u7528\u56FE\u5C42\u5206\u7C7B\u7BA1\u7406");
1483
+ if (layers.length > 20)
1484
+ suggestions.push("- \u56FE\u5C42\u8F83\u591A\uFF0C\u5EFA\u8BAE\u6E05\u7406\u672A\u4F7F\u7528\u7684\u56FE\u5C42");
1485
+ if (entities.byType.LINE && entities.byType.LINE > 500)
1486
+ suggestions.push("- \u7EBF\u6761\u8F83\u591A\uFF0C\u53EF\u8003\u8651\u4F7F\u7528 BLOCK \u51CF\u5C11\u5B9E\u4F53\u6570\u91CF");
1487
+ if (!entities.byType.DIMENSION)
1488
+ suggestions.push("- \u672A\u53D1\u73B0\u5C3A\u5BF8\u6807\u6CE8\uFF0C\u5EFA\u8BAE\u6DFB\u52A0\u6807\u6CE8");
1321
1489
  return suggestions.length ? suggestions.join("\n") : "- \u56FE\u7EB8\u7ED3\u6784\u826F\u597D";
1322
1490
  }
1323
1491
  async function generateBatchLinesPrompt(args) {
@@ -2045,9 +2213,11 @@ var SseSession = class {
2045
2213
  return lines.join("\n") + "\n\n";
2046
2214
  }
2047
2215
  _setupDrain() {
2048
- if (typeof this.#res.on !== "function") return;
2216
+ if (typeof this.#res.on !== "function")
2217
+ return;
2049
2218
  this.#drainHandler = () => {
2050
- if (!this.#active) return;
2219
+ if (!this.#active)
2220
+ return;
2051
2221
  if (this.#backpressureBuffer.length > 0) {
2052
2222
  this._drainBuffer();
2053
2223
  }
@@ -2069,7 +2239,8 @@ var SseSession = class {
2069
2239
  }
2070
2240
  }
2071
2241
  _write(content) {
2072
- if (!this.#active) return false;
2242
+ if (!this.#active)
2243
+ return false;
2073
2244
  try {
2074
2245
  if (this.#backpressureBuffer.length > 0) {
2075
2246
  this.#backpressureBuffer.push(content);
@@ -2339,7 +2510,8 @@ var SERVER_CAPABILITIES = {
2339
2510
  prompts: {}
2340
2511
  };
2341
2512
  var { port, host, transport, apiKey, rateLimitWindow, rateLimitMax } = config_default;
2342
- if (apiKey) log("API Key authentication enabled");
2513
+ if (apiKey)
2514
+ log("API Key authentication enabled");
2343
2515
  var mcpLimiter = rateLimit({
2344
2516
  windowMs: rateLimitWindow,
2345
2517
  max: rateLimitMax,
@@ -2387,9 +2559,11 @@ ${CAD_PLATFORMS.join("\n")}
2387
2559
  init_atlisp: () => initAtlisp(),
2388
2560
  get_function_usage: (a) => getFunctionUsage(a.name, a.package),
2389
2561
  list_symbols: (a) => listSymbols(a.package),
2562
+ get_system_status: () => getSystemStatus(),
2390
2563
  import_funlib: async (a) => {
2391
2564
  const data = await loadAtlibFunctionLib();
2392
- if (!data) return { content: [{ type: "text", text: "\u52A0\u8F7D\u51FD\u6570\u5E93\u5931\u8D25" }], isError: true };
2565
+ if (!data)
2566
+ return { content: [{ type: "text", text: "\u52A0\u8F7D\u51FD\u6570\u5E93\u5931\u8D25" }], isError: true };
2393
2567
  if (a.format === "list") {
2394
2568
  const items = Object.values(data.all_functions || {}).map((f) => `${f.name}: ${f.description || ""}`);
2395
2569
  return { content: [{ type: "text", text: items.join("\n") }] };
@@ -2547,11 +2721,17 @@ var tools = [
2547
2721
  format: { type: "string", enum: ["json", "list"], description: "\u8FD4\u56DE\u683C\u5F0F\uFF1Ajson\uFF08\u5B8C\u6574JSON\uFF09\u6216 list\uFF08\u51FD\u6570\u540D:\u63CF\u8FF0 \u5217\u8868\uFF09\uFF0C\u9ED8\u8BA4 json" }
2548
2722
  }
2549
2723
  }
2724
+ },
2725
+ {
2726
+ name: "get_system_status",
2727
+ description: "\u83B7\u53D6\u5B8C\u6574\u7CFB\u7EDF\u8BCA\u65AD\u4FE1\u606F\uFF1AMCP \u72B6\u6001\u3001CAD \u8FDE\u63A5\u3001Lisp \u6267\u884C\u3001\u5305\u5217\u8868",
2728
+ inputSchema: { type: "object", properties: {} }
2550
2729
  }
2551
2730
  ];
2552
2731
  async function handleToolCall(name, args) {
2553
2732
  const handler = TOOL_HANDLERS[name];
2554
- if (!handler) return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
2733
+ if (!handler)
2734
+ return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
2555
2735
  try {
2556
2736
  const result = await handler(args || {});
2557
2737
  notifyResourceChanges(name);
@@ -2563,14 +2743,16 @@ async function handleToolCall(name, args) {
2563
2743
  }
2564
2744
  function notifyResourceChanges(toolName) {
2565
2745
  const resourceMap = {
2566
- connect_cad: ["atlisp://cad/info"],
2746
+ connect_cad: ["atlisp://cad/info", "atlisp://cad/layers", "atlisp://dwg/name", "atlisp://dwg/path", "atlisp://packages"],
2567
2747
  new_document: ["atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/layers", "atlisp://cad/entities"],
2568
2748
  install_package: ["atlisp://packages"],
2569
- install_atlisp: ["atlisp://packages"]
2749
+ install_atlisp: ["atlisp://packages", "atlisp://cad/info"],
2750
+ init_atlisp: ["atlisp://packages", "atlisp://cad/info"]
2570
2751
  };
2571
2752
  const uris = resourceMap[toolName];
2572
2753
  if (uris) {
2573
2754
  for (const uri of uris) {
2755
+ clearCache(uri);
2574
2756
  if (isSubscribed(uri)) {
2575
2757
  notify(uri, null);
2576
2758
  }
@@ -2672,7 +2854,8 @@ async function startServer() {
2672
2854
  const JSON_CONTENT_TYPES = ["application/json", "application/json-rpc", "application/vnd.api+json"];
2673
2855
  app.use(async (req, res, next) => {
2674
2856
  const ct = (req.headers["content-type"] || "").toLowerCase();
2675
- if (!JSON_CONTENT_TYPES.some((t) => ct.startsWith(t))) return next();
2857
+ if (!JSON_CONTENT_TYPES.some((t) => ct.startsWith(t)))
2858
+ return next();
2676
2859
  try {
2677
2860
  const buf = await rawBody(req, { limit: "10mb" });
2678
2861
  const m = ct.match(/charset\s*=\s*([^\s;]+)/i);
@@ -2693,9 +2876,11 @@ async function startServer() {
2693
2876
  const PUBLIC_PATHS = ["/health"];
2694
2877
  if (apiKey) {
2695
2878
  app.use((req, res, next) => {
2696
- if (PUBLIC_PATHS.includes(req.path)) return next();
2879
+ if (PUBLIC_PATHS.includes(req.path))
2880
+ return next();
2697
2881
  const auth = req.get("Authorization");
2698
- if (auth === `Bearer ${apiKey}`) return next();
2882
+ if (auth === `Bearer ${apiKey}`)
2883
+ return next();
2699
2884
  res.status(401).json({ error: "Unauthorized" });
2700
2885
  });
2701
2886
  }
@@ -2710,7 +2895,8 @@ async function startServer() {
2710
2895
  res.setHeader("Access-Control-Expose-Headers", "mcp-session-id, content-type, x-accel-buffering");
2711
2896
  res.setHeader("Access-Control-Max-Age", "86400");
2712
2897
  res.setHeader("Vary", "Accept");
2713
- if (req.method === "OPTIONS") return res.status(204).end();
2898
+ if (req.method === "OPTIONS")
2899
+ return res.status(204).end();
2714
2900
  next();
2715
2901
  });
2716
2902
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlisp/mcp",
3
- "version": "1.3.7",
3
+ "version": "1.4.0",
4
4
  "description": "MCP Server for @lisp on CAD",
5
5
  "type": "module",
6
6
  "bin": {