@atlisp/mcp 1.5.1 → 1.5.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 (2) hide show
  1. package/dist/atlisp-mcp.js +1855 -1975
  2. package/package.json +1 -1
@@ -1,43 +1,62 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __esm = (fn, res) => function __init() {
5
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
- };
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
2
+
3
+ // src/atlisp-mcp.js
4
+ import path8 from "path";
5
+ import { fileURLToPath as fileURLToPath4 } from "url";
6
+ import { createRequire } from "module";
7
+ import express2 from "express";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+
10
+ // src/cad.js
11
+ import { spawn } from "child_process";
12
+ import path from "path";
13
+ import { fileURLToPath } from "url";
14
+ import { randomUUID } from "crypto";
11
15
 
12
16
  // src/constants.js
13
- var CAD_PLATFORMS, FILE_EXTENSIONS, SERVER_NAME, SERVER_VERSION, MOCK_PACKAGES;
14
- var init_constants = __esm({
15
- "src/constants.js"() {
16
- CAD_PLATFORMS = ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"];
17
- FILE_EXTENSIONS = {
18
- "AutoCAD": [".fas", ".vlx"],
19
- "ZWCAD": [".zelx", ".vls"],
20
- "GStarCAD": [".fas", ".vlx"],
21
- "BricsCAD": [".des"]
22
- };
23
- SERVER_NAME = "atlisp-mcp-server";
24
- SERVER_VERSION = "1.5.0";
25
- MOCK_PACKAGES = [
26
- { name: "base", description: "\u57FA\u7840\u51FD\u6570\u5E93", version: "1.0.0" },
27
- { name: "at-pm", description: "\u5DE5\u7A0B\u9879\u76EE\u7BA1\u7406", version: "2.1.0" },
28
- { name: "network", description: "\u7F51\u7EDC\u529F\u80FD\u6A21\u5757", version: "1.5.0" },
29
- { name: "userman", description: "\u7528\u6237\u7BA1\u7406", version: "1.2.0" },
30
- { name: "pkgman", description: "\u5305\u7BA1\u7406\u5668", version: "2.0.0" },
31
- { name: "sidebar", description: "\u4FA7\u8FB9\u680F\u5DE5\u5177", version: "1.0.0" },
32
- { name: "aibot", description: "AI \u52A9\u624B", version: "0.9.0" },
33
- { name: "tips", description: "\u6BCF\u65E5\u63D0\u793A", version: "1.0.0" },
34
- { name: "function", description: "\u51FD\u6570\u5E93", version: "3.0.0" }
35
- ];
36
- }
37
- });
17
+ var CAD_PLATFORMS = ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"];
18
+ var FILE_EXTENSIONS = {
19
+ "AutoCAD": [".fas", ".vlx"],
20
+ "ZWCAD": [".zelx", ".vls"],
21
+ "GStarCAD": [".fas", ".vlx"],
22
+ "BricsCAD": [".des"]
23
+ };
24
+ var SERVER_NAME = "atlisp-mcp-server";
25
+ var SERVER_VERSION = "1.5.0";
26
+ var MOCK_PACKAGES = [
27
+ { name: "base", description: "\u57FA\u7840\u51FD\u6570\u5E93", version: "1.0.0" },
28
+ { name: "at-pm", description: "\u5DE5\u7A0B\u9879\u76EE\u7BA1\u7406", version: "2.1.0" },
29
+ { name: "network", description: "\u7F51\u7EDC\u529F\u80FD\u6A21\u5757", version: "1.5.0" },
30
+ { name: "userman", description: "\u7528\u6237\u7BA1\u7406", version: "1.2.0" },
31
+ { name: "pkgman", description: "\u5305\u7BA1\u7406\u5668", version: "2.0.0" },
32
+ { name: "sidebar", description: "\u4FA7\u8FB9\u680F\u5DE5\u5177", version: "1.0.0" },
33
+ { name: "aibot", description: "AI \u52A9\u624B", version: "0.9.0" },
34
+ { name: "tips", description: "\u6BCF\u65E5\u63D0\u793A", version: "1.0.0" },
35
+ { name: "function", description: "\u51FD\u6570\u5E93", version: "3.0.0" }
36
+ ];
38
37
 
39
38
  // src/config.js
40
39
  import { z } from "zod";
40
+ var envSchema = z.object({
41
+ PORT: z.string().optional().default("8110"),
42
+ HOST: z.string().optional().default("0.0.0.0"),
43
+ TRANSPORT: z.enum(["http", "stdio"]).optional().default("http"),
44
+ MCP_API_KEY: z.string().optional().default(""),
45
+ ENABLE_SSE: z.string().optional().default("true"),
46
+ MESSAGE_TIMEOUT: z.string().optional().default("60000"),
47
+ HEARTBEAT_INTERVAL: z.string().optional().default("30000"),
48
+ BUSY_RETRIES: z.string().optional().default("10"),
49
+ BUSY_DELAY: z.string().optional().default("500"),
50
+ ENABLE_CORS: z.string().optional().default("true"),
51
+ CORS_ORIGIN: z.string().optional().default("*"),
52
+ RATE_LIMIT_WINDOW: z.string().optional().default("60000"),
53
+ RATE_LIMIT_MAX: z.string().optional().default("100"),
54
+ RESOURCE_CACHE_TTL: z.string().optional().default("5000"),
55
+ FUNCTION_LIB_CACHE_TTL: z.string().optional().default("300000"),
56
+ DEBUG: z.string().optional().default("false"),
57
+ DEBUG_FILE: z.string().optional().default(""),
58
+ FUNCTION_LIB_URL: z.string().optional().default("http://s3.atlisp.cn/json/functions.json")
59
+ });
41
60
  function parseArgs() {
42
61
  const args = process.argv.slice(2);
43
62
  const result = { transport: "http", port: "8110", host: "0.0.0.0" };
@@ -57,65 +76,50 @@ function parseArgs() {
57
76
  }
58
77
  return result;
59
78
  }
60
- var envSchema, cliArgs, envResult, env, config, config_default;
61
- var init_config = __esm({
62
- "src/config.js"() {
63
- envSchema = z.object({
64
- PORT: z.string().optional().default("8110"),
65
- HOST: z.string().optional().default("0.0.0.0"),
66
- TRANSPORT: z.enum(["http", "stdio"]).optional().default("http"),
67
- MCP_API_KEY: z.string().optional().default(""),
68
- ENABLE_SSE: z.string().optional().default("true"),
69
- MESSAGE_TIMEOUT: z.string().optional().default("60000"),
70
- HEARTBEAT_INTERVAL: z.string().optional().default("30000"),
71
- BUSY_RETRIES: z.string().optional().default("10"),
72
- BUSY_DELAY: z.string().optional().default("500"),
73
- ENABLE_CORS: z.string().optional().default("true"),
74
- CORS_ORIGIN: z.string().optional().default("*"),
75
- RATE_LIMIT_WINDOW: z.string().optional().default("60000"),
76
- RATE_LIMIT_MAX: z.string().optional().default("100"),
77
- RESOURCE_CACHE_TTL: z.string().optional().default("5000"),
78
- FUNCTION_LIB_CACHE_TTL: z.string().optional().default("300000"),
79
- DEBUG: z.string().optional().default("false"),
80
- DEBUG_FILE: z.string().optional().default(""),
81
- FUNCTION_LIB_URL: z.string().optional().default("http://s3.atlisp.cn/json/functions.json")
82
- });
83
- cliArgs = parseArgs();
84
- envResult = envSchema.safeParse(process.env);
85
- if (!envResult.success) {
86
- console.error("\u73AF\u5883\u53D8\u91CF\u914D\u7F6E\u9519\u8BEF:", envResult.error.flatten().fieldErrors);
87
- process.exit(1);
88
- }
89
- env = envResult.data;
90
- config = {
91
- port: parseInt(env.PORT || cliArgs.port, 10),
92
- host: env.HOST || cliArgs.host,
93
- transport: process.env.TRANSPORT ? env.TRANSPORT : cliArgs.transport,
94
- apiKey: env.MCP_API_KEY,
95
- enableSse: env.ENABLE_SSE === "true",
96
- messageTimeout: parseInt(env.MESSAGE_TIMEOUT, 10),
97
- heartbeatInterval: parseInt(env.HEARTBEAT_INTERVAL, 10),
98
- busyRetries: parseInt(env.BUSY_RETRIES, 10),
99
- busyDelay: parseInt(env.BUSY_DELAY, 10),
100
- enableCors: env.ENABLE_CORS === "true",
101
- corsOrigin: env.CORS_ORIGIN,
102
- rateLimitWindow: parseInt(env.RATE_LIMIT_WINDOW, 10),
103
- rateLimitMax: parseInt(env.RATE_LIMIT_MAX, 10),
104
- resourceCacheTtl: parseInt(env.RESOURCE_CACHE_TTL, 10),
105
- functionLibCacheTtl: parseInt(env.FUNCTION_LIB_CACHE_TTL, 10),
106
- debug: env.DEBUG === "true",
107
- debugFile: env.DEBUG_FILE,
108
- functionLibUrl: env.FUNCTION_LIB_URL
109
- };
110
- config_default = config;
111
- }
112
- });
79
+ var cliArgs = parseArgs();
80
+ var envResult = envSchema.safeParse(process.env);
81
+ if (!envResult.success) {
82
+ console.error("\u73AF\u5883\u53D8\u91CF\u914D\u7F6E\u9519\u8BEF:", envResult.error.flatten().fieldErrors);
83
+ process.exit(1);
84
+ }
85
+ var env = envResult.data;
86
+ var config = {
87
+ port: parseInt(env.PORT || cliArgs.port, 10),
88
+ host: env.HOST || cliArgs.host,
89
+ transport: process.env.TRANSPORT && env.TRANSPORT ? env.TRANSPORT : cliArgs.transport,
90
+ apiKey: env.MCP_API_KEY,
91
+ enableSse: env.ENABLE_SSE === "true",
92
+ messageTimeout: parseInt(env.MESSAGE_TIMEOUT, 10),
93
+ heartbeatInterval: parseInt(env.HEARTBEAT_INTERVAL, 10),
94
+ busyRetries: parseInt(env.BUSY_RETRIES, 10),
95
+ busyDelay: parseInt(env.BUSY_DELAY, 10),
96
+ enableCors: env.ENABLE_CORS === "true",
97
+ corsOrigin: env.CORS_ORIGIN,
98
+ rateLimitWindow: parseInt(env.RATE_LIMIT_WINDOW, 10),
99
+ rateLimitMax: parseInt(env.RATE_LIMIT_MAX, 10),
100
+ resourceCacheTtl: parseInt(env.RESOURCE_CACHE_TTL, 10),
101
+ functionLibCacheTtl: parseInt(env.FUNCTION_LIB_CACHE_TTL, 10),
102
+ debug: env.DEBUG === "true",
103
+ debugFile: env.DEBUG_FILE,
104
+ functionLibUrl: env.FUNCTION_LIB_URL
105
+ };
106
+ var config_default = config;
113
107
 
114
108
  // src/cad.js
115
- import { spawn } from "child_process";
116
- import path from "path";
117
- import { fileURLToPath } from "url";
118
- import { randomUUID } from "crypto";
109
+ var MESSAGE_TIMEOUT = config_default.messageTimeout;
110
+ var HEARTBEAT_INTERVAL = config_default.heartbeatInterval;
111
+ var MAX_RESTART_ATTEMPTS = 5;
112
+ var RESTART_BACKOFF_BASE = 1e3;
113
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
114
+ var workerPath = path.join(__dirname, "cad-worker.js");
115
+ var worker = null;
116
+ var _platform = null;
117
+ var heartbeatTimer = null;
118
+ var pendingRequests = /* @__PURE__ */ new Map();
119
+ var stdoutHandlerSetup = false;
120
+ var MAX_BUFFER_SIZE = 1024 * 1024;
121
+ var restartCount = 0;
122
+ var restartTimer = null;
119
123
  function resetWorker() {
120
124
  for (const [id, { reject, timeout }] of pendingRequests) {
121
125
  clearTimeout(timeout);
@@ -255,142 +259,125 @@ function sendMessage(msg) {
255
259
  }
256
260
  });
257
261
  }
258
- var MESSAGE_TIMEOUT, HEARTBEAT_INTERVAL, MAX_RESTART_ATTEMPTS, RESTART_BACKOFF_BASE, __dirname, workerPath, worker, _platform, heartbeatTimer, pendingRequests, stdoutHandlerSetup, MAX_BUFFER_SIZE, restartCount, restartTimer, CadConnection, cad;
259
- var init_cad = __esm({
260
- "src/cad.js"() {
261
- init_constants();
262
- init_config();
263
- MESSAGE_TIMEOUT = config_default.messageTimeout;
264
- HEARTBEAT_INTERVAL = config_default.heartbeatInterval;
265
- MAX_RESTART_ATTEMPTS = 5;
266
- RESTART_BACKOFF_BASE = 1e3;
267
- __dirname = path.dirname(fileURLToPath(import.meta.url));
268
- workerPath = path.join(__dirname, "cad-worker.js");
269
- worker = null;
270
- _platform = null;
271
- heartbeatTimer = null;
272
- pendingRequests = /* @__PURE__ */ new Map();
273
- stdoutHandlerSetup = false;
274
- MAX_BUFFER_SIZE = 1024 * 1024;
275
- restartCount = 0;
276
- restartTimer = null;
277
- CadConnection = class {
278
- constructor() {
279
- this.connected = false;
280
- this.version = null;
281
- this.product = null;
282
- }
283
- isAvailable() {
284
- return process.platform === "win32";
285
- }
286
- async connect(platform = null) {
287
- if (!this.isAvailable()) return false;
288
- try {
289
- const msg = platform ? { type: "connect", platform } : { type: "connect" };
290
- const result = await sendMessage(msg);
291
- if (result && result.success) {
292
- this.version = result.version;
293
- this.product = result.platform;
294
- _platform = result.platform;
295
- this.connected = true;
296
- return true;
297
- }
298
- } catch (e) {
299
- console.error("CAD connect error:", e.message);
300
- }
301
- return false;
302
- }
303
- async sendCommand(code) {
304
- if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
305
- const result = await sendMessage({ type: "send", code, platform: _platform });
306
- if (result.success) return true;
307
- throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
308
- }
309
- async sendCommandWithResult(code, encoding = null) {
310
- if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
311
- const result = await sendMessage({ type: "sendResult", code, platform: _platform, encoding: encoding || "" });
312
- if (result.success) {
313
- return result.result || "";
314
- }
315
- throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
316
- }
317
- getVersion() {
318
- return this.version;
319
- }
320
- getPlatform() {
321
- return this.product;
322
- }
323
- async isBusy() {
324
- if (!this.connected) return false;
325
- try {
326
- const result = await sendMessage({ type: "isBusy", platform: _platform });
327
- return result && result.isBusy;
328
- } catch (e) {
329
- return false;
330
- }
331
- }
332
- async hasDoc() {
333
- if (!this.connected) return { hasDoc: false, docCount: 0 };
334
- try {
335
- const result = await sendMessage({ type: "hasdoc", platform: _platform });
336
- return result || { hasDoc: false, docCount: 0 };
337
- } catch (e) {
338
- return { hasDoc: false, docCount: 0 };
339
- }
340
- }
341
- async newDoc() {
342
- if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
343
- try {
344
- const result = await sendMessage({ type: "newdoc", platform: _platform });
345
- return result && result.success;
346
- } catch (e) {
347
- return false;
348
- }
349
- }
350
- async bringToFront() {
351
- if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
352
- try {
353
- const result = await sendMessage({ type: "bringToFront", platform: _platform });
354
- return result && result.success;
355
- } catch (e) {
356
- return false;
357
- }
358
- }
359
- async getInfo() {
360
- if (!this.connected) return "CAD \u672A\u8FDE\u63A5";
361
- return `Platform: ${this.product}, Version: ${this.version}, Status: Connected`;
362
- }
363
- async disconnect() {
364
- this.connected = false;
365
- this.version = null;
366
- this.product = null;
367
- _platform = null;
368
- resetWorker();
369
- }
370
- async ping() {
371
- try {
372
- const result = await sendMessage({ type: "ping" });
373
- return result && result.pong;
374
- } catch (e) {
375
- return false;
376
- }
377
- }
378
- _reset() {
379
- this.connected = false;
380
- this.version = null;
381
- this.product = null;
382
- _platform = null;
383
- resetWorker();
262
+ var CadConnection = class {
263
+ constructor() {
264
+ this.connected = false;
265
+ this.version = null;
266
+ this.product = null;
267
+ }
268
+ isAvailable() {
269
+ return process.platform === "win32";
270
+ }
271
+ async connect(platform = null) {
272
+ if (!this.isAvailable()) return false;
273
+ try {
274
+ const msg = platform ? { type: "connect", platform } : { type: "connect" };
275
+ const result = await sendMessage(msg);
276
+ if (result && result.success) {
277
+ this.version = result.version;
278
+ this.product = result.platform;
279
+ _platform = result.platform;
280
+ this.connected = true;
281
+ return true;
384
282
  }
385
- };
386
- cad = new CadConnection();
283
+ } catch (e) {
284
+ console.error("CAD connect error:", e.message);
285
+ }
286
+ return false;
387
287
  }
388
- });
288
+ async sendCommand(code) {
289
+ if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
290
+ const result = await sendMessage({ type: "send", code, platform: _platform });
291
+ if (result.success) return true;
292
+ throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
293
+ }
294
+ async sendCommandWithResult(code, encoding = null) {
295
+ if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
296
+ const result = await sendMessage({ type: "sendResult", code, platform: _platform, encoding: encoding || "" });
297
+ if (result.success) {
298
+ return result.result || "";
299
+ }
300
+ throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
301
+ }
302
+ getVersion() {
303
+ return this.version;
304
+ }
305
+ getPlatform() {
306
+ return this.product;
307
+ }
308
+ async isBusy() {
309
+ if (!this.connected) return false;
310
+ try {
311
+ const result = await sendMessage({ type: "isBusy", platform: _platform });
312
+ return result && result.isBusy;
313
+ } catch (e) {
314
+ return false;
315
+ }
316
+ }
317
+ async hasDoc() {
318
+ if (!this.connected) return { hasDoc: false, docCount: 0 };
319
+ try {
320
+ const result = await sendMessage({ type: "hasdoc", platform: _platform });
321
+ return result || { hasDoc: false, docCount: 0 };
322
+ } catch (e) {
323
+ return { hasDoc: false, docCount: 0 };
324
+ }
325
+ }
326
+ async newDoc() {
327
+ if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
328
+ try {
329
+ const result = await sendMessage({ type: "newdoc", platform: _platform });
330
+ return result && result.success;
331
+ } catch (e) {
332
+ return false;
333
+ }
334
+ }
335
+ async bringToFront() {
336
+ if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
337
+ try {
338
+ const result = await sendMessage({ type: "bringToFront", platform: _platform });
339
+ return result && result.success;
340
+ } catch (e) {
341
+ return false;
342
+ }
343
+ }
344
+ async getInfo() {
345
+ if (!this.connected) return "CAD \u672A\u8FDE\u63A5";
346
+ return `Platform: ${this.product}, Version: ${this.version}, Status: Connected`;
347
+ }
348
+ async disconnect() {
349
+ this.connected = false;
350
+ this.version = null;
351
+ this.product = null;
352
+ _platform = null;
353
+ resetWorker();
354
+ }
355
+ async ping() {
356
+ try {
357
+ const result = await sendMessage({ type: "ping" });
358
+ return result && result.pong;
359
+ } catch (e) {
360
+ return false;
361
+ }
362
+ }
363
+ _reset() {
364
+ this.connected = false;
365
+ this.version = null;
366
+ this.product = null;
367
+ _platform = null;
368
+ resetWorker();
369
+ }
370
+ };
371
+ var cad = new CadConnection();
389
372
 
390
373
  // src/logger.js
391
374
  import fs from "fs";
392
375
  import path2 from "path";
393
376
  import os from "os";
377
+ var DEBUG_FILE = config_default.debugFile || path2.join(os.tmpdir(), "mcp-server-debug.log");
378
+ var MAX_LOG_SIZE = 10 * 1024 * 1024;
379
+ var MAX_LOG_FILES = 5;
380
+ var stream = null;
394
381
  function rotateLog() {
395
382
  if (!fs.existsSync(DEBUG_FILE)) return;
396
383
  const stat = fs.statSync(DEBUG_FILE);
@@ -433,8 +420,9 @@ function formatMessage(level, message, meta = {}) {
433
420
  return JSON.stringify(entry);
434
421
  }
435
422
  function log(message, meta = {}) {
423
+ getStream().write(formatMessage("INFO", message, meta) + "\n");
436
424
  if (config_default.debug) {
437
- getStream().write(formatMessage("INFO", message, meta) + "\n");
425
+ console.log(`[INFO] ${message}`, meta);
438
426
  }
439
427
  }
440
428
  function error(message, meta = {}) {
@@ -449,508 +437,115 @@ function closeLog() {
449
437
  stream = null;
450
438
  }
451
439
  }
452
- var DEBUG_FILE, MAX_LOG_SIZE, MAX_LOG_FILES, stream;
453
- var init_logger = __esm({
454
- "src/logger.js"() {
455
- init_config();
456
- DEBUG_FILE = config_default.debugFile || path2.join(os.tmpdir(), "mcp-server-debug.log");
457
- MAX_LOG_SIZE = 10 * 1024 * 1024;
458
- MAX_LOG_FILES = 5;
459
- stream = null;
460
- }
461
- });
462
440
 
463
- // src/handlers/cad-handlers.js
464
- async function connectCad(platform = null) {
465
- log(`connect_cad called${platform ? ` (target: ${platform})` : ""}, cad.connected before: ${cad.connected}`);
466
- try {
467
- log("calling cad.connect()...");
468
- const connected = await cad.connect(platform);
469
- log("cad.connect() returned: " + connected + ", cad.connected: " + cad.connected);
470
- if (connected) {
471
- return { content: [{ type: "text", text: `\u5DF2\u8FDE\u63A5\u5230 ${cad.getPlatform()} ${cad.getVersion()}` }] };
472
- }
473
- } catch (e) {
474
- log("connect_cad error: " + e.message);
441
+ // src/handlers/resource-handlers.js
442
+ import fs2 from "fs";
443
+ import path3 from "path";
444
+ import { fileURLToPath as fileURLToPath2 } from "url";
445
+ var __dirname2 = path3.dirname(fileURLToPath2(import.meta.url));
446
+ var STANDARDS_DIR = path3.join(__dirname2, "..", "standards");
447
+ var CACHE_TTL = config_default.resourceCacheTtl;
448
+ var resourceCache = /* @__PURE__ */ new Map();
449
+ function getCached(key) {
450
+ const entry = resourceCache.get(key);
451
+ if (entry && Date.now() - entry.timestamp < CACHE_TTL) {
452
+ return entry.data;
475
453
  }
476
- return { content: [{ type: "text", text: "\u672A\u80FD\u8FDE\u63A5\u6216\u542F\u52A8 CAD\uFF0C\u8BF7\u786E\u4FDD CAD \u5DF2\u5B89\u88C5" }], isError: true };
454
+ resourceCache.delete(key);
455
+ return null;
477
456
  }
478
- async function evalLisp(code, withResult = false, encoding = null) {
479
- if (!cad.connected) {
480
- await cad.connect();
481
- }
482
- if (!cad.connected) {
483
- return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
484
- }
485
- const trimmed = (code || "").trim();
486
- if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
487
- try {
488
- if (withResult) {
489
- const result = await cad.sendCommandWithResult(trimmed, encoding);
490
- if (result !== null) {
491
- return { content: [{ type: "text", text: result }] };
457
+ function setCache(key, data) {
458
+ resourceCache.set(key, { data, timestamp: Date.now() });
459
+ }
460
+ function clearCache(uri) {
461
+ if (uri) {
462
+ for (const key of resourceCache.keys()) {
463
+ if (key.startsWith(uri.split("://")[1]?.split("/")[0] + ":")) {
464
+ resourceCache.delete(key);
492
465
  }
493
- return { content: [{ type: "text", text: "\u6267\u884C\u5931\u8D25\u6216\u65E0\u8FD4\u56DE\u503C" }], isError: true };
494
- } else {
495
- await cad.sendCommand(trimmed + "\n");
496
- return { content: [{ type: "text", text: `\u5DF2\u53D1\u9001\u547D\u4EE4` }] };
497
466
  }
498
- } catch (e) {
499
- return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
467
+ } else {
468
+ resourceCache.clear();
500
469
  }
501
470
  }
502
- async function getCadInfo() {
503
- if (!cad.connected) {
504
- await cad.connect();
471
+ function parseLispList(str) {
472
+ if (!str || typeof str !== "string") return null;
473
+ const trimmed = str.trim();
474
+ if (!trimmed.startsWith("(") || !trimmed.endsWith(")")) return null;
475
+ const content = trimmed.slice(1, -1).trim();
476
+ if (!content) return [];
477
+ const result = [];
478
+ let depth = 0;
479
+ let current = "";
480
+ let inString = false;
481
+ let escaped = false;
482
+ for (let i = 0; i < content.length; i++) {
483
+ const ch = content[i];
484
+ if (escaped) {
485
+ current += ch;
486
+ escaped = false;
487
+ continue;
488
+ }
489
+ if (ch === "\\") {
490
+ current += ch;
491
+ escaped = true;
492
+ continue;
493
+ }
494
+ if (ch === '"') {
495
+ current += ch;
496
+ inString = !inString;
497
+ continue;
498
+ }
499
+ if (inString) {
500
+ current += ch;
501
+ continue;
502
+ }
503
+ if (ch === "(" || ch === "[") {
504
+ current += ch;
505
+ depth++;
506
+ } else if (ch === ")" || ch === "]") {
507
+ current += ch;
508
+ depth--;
509
+ } else if (ch === " " && depth === 0) {
510
+ if (current.trim()) {
511
+ result.push(current.trim());
512
+ current = "";
513
+ }
514
+ continue;
515
+ }
516
+ current += ch;
505
517
  }
506
- if (!cad.connected) return { content: [{ type: "text", text: "CAD \u672A\u8FDE\u63A5" }] };
507
- const info = await cad.getInfo();
508
- return { content: [{ type: "text", text: info }] };
509
- }
510
- async function atCommand(command) {
511
- if (!cad.connected) return { content: [{ type: "text", text: "\u8BF7\u5148\u8FDE\u63A5 CAD" }], isError: true };
512
- const trimmed = (command || "").trim();
513
- if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
514
- await cad.sendCommand(trimmed + "\n");
515
- return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001\u547D\u4EE4" }] };
518
+ if (current.trim()) result.push(current.trim());
519
+ return result.map((item) => {
520
+ const t = item.trim();
521
+ if (t === "nil") return null;
522
+ if (t === "t") return true;
523
+ if (t === "T") return true;
524
+ const num = Number(t);
525
+ if (!isNaN(num) && t !== "") return num;
526
+ if (t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
527
+ return t;
528
+ });
516
529
  }
517
- async function newDocument() {
518
- if (!cad.connected) {
519
- await cad.connect();
530
+ function parseLispRecursive(str) {
531
+ if (!str || typeof str !== "string") return null;
532
+ str = str.trim();
533
+ if (!str) return null;
534
+ let pos = 0;
535
+ function skipWs() {
536
+ while (pos < str.length && /\s/.test(str[pos])) pos++;
520
537
  }
521
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
522
- const result = await cad.newDoc();
523
- if (result) {
524
- return { content: [{ type: "text", text: "\u5DF2\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863" }] };
525
- }
526
- return { content: [{ type: "text", text: "\u65B0\u5EFA\u6587\u6863\u5931\u8D25" }], isError: true };
527
- }
528
- async function bringToFront() {
529
- if (!cad.connected) {
530
- await cad.connect();
531
- }
532
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
533
- const result = await cad.bringToFront();
534
- if (result) {
535
- return { content: [{ type: "text", text: "\u5DF2\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0" }] };
536
- }
537
- return { content: [{ type: "text", text: "\u5207\u6362\u524D\u53F0\u5931\u8D25" }], isError: true };
538
- }
539
- async function installAtlisp() {
540
- if (!cad.connected) {
541
- await cad.connect();
542
- }
543
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
544
- 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))))`;
545
- await cad.sendCommand(installCode + "\n");
546
- return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001 @lisp \u5B89\u88C5\u4EE3\u7801\u5230 CAD" }] };
547
- }
548
- async function listFunctionsInCad() {
549
- if (!cad.connected) {
550
- await cad.connect();
551
- }
552
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
553
- try {
554
- const result = await cad.sendCommandWithResult("(atoms-family 0)", null);
555
- if (result) return { content: [{ type: "text", text: result }] };
556
- return { content: [{ type: "text", text: "CAD \u51FD\u6570\u5217\u8868\u4E3A\u7A7A" }] };
557
- } catch (e) {
558
- return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
559
- }
560
- }
561
- async function getSystemStatus() {
562
- const status = {
563
- mcp: { version: process.env.npm_package_version || "unknown" },
564
- worker: { alive: false },
565
- cad: { connected: false, platform: null, version: null, busy: false, hasDoc: false },
566
- lisp: { available: false, testResult: null, error: null },
567
- packages: null
568
- };
569
- try {
570
- status.worker.alive = await cad.ping();
571
- } catch {
572
- status.worker.alive = false;
573
- }
574
- status.cad.connected = cad.connected;
575
- if (cad.connected) {
576
- status.cad.platform = cad.getPlatform();
577
- status.cad.version = cad.getVersion();
578
- try {
579
- status.cad.busy = await cad.isBusy();
580
- } catch {
581
- }
582
- try {
583
- const doc = await cad.hasDoc();
584
- status.cad.hasDoc = doc.hasDoc;
585
- } catch {
586
- }
587
- try {
588
- const r = await cad.sendCommandWithResult("(+ 1 2)");
589
- if (r !== null && r !== void 0) {
590
- status.lisp.available = true;
591
- status.lisp.testResult = String(r).trim();
592
- }
593
- } catch (e) {
594
- status.lisp.error = e.message;
595
- }
596
- try {
597
- const pkgResult = await cad.sendCommandWithResult("(@::package-list-in-local)");
598
- if (pkgResult && pkgResult !== "nil") {
599
- status.packages = pkgResult;
600
- }
601
- } catch {
602
- }
603
- }
604
- return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
605
- }
606
- async function initAtlisp() {
607
- if (!cad.connected) {
608
- await cad.connect();
609
- }
610
- if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
611
- const code = `(if (null @::load-module)
612
- (progn
613
- (vl-load-com)
614
- (setq s strcat h"http"o(vlax-create-object (s"win"h".win"h"request.5.1"))
615
- v vlax-invoke e eval r read)(v o'open "get" (s h"://""atlisp.""cn/cloud"):vlax-true)
616
- (v o'send)
617
- (v o'WaitforResponse 1000)
618
- (e(r(vlax-get o'ResponseText)))))`;
619
- try {
620
- await cad.sendCommand(code + "\n");
621
- return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001 @lisp \u51FD\u6570\u5E93\u52A0\u8F7D\u4EE3\u7801\u5230 CAD" }] };
622
- } catch (e) {
623
- return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
624
- }
625
- }
626
- var init_cad_handlers = __esm({
627
- "src/handlers/cad-handlers.js"() {
628
- init_cad();
629
- init_logger();
630
- }
631
- });
632
-
633
- // src/handlers/package-handlers.js
634
- async function fetchPackages() {
635
- const now = Date.now();
636
- if (cachedPackages && cachedAt && now - cachedAt < CACHE_TTL) {
637
- return cachedPackages;
638
- }
639
- try {
640
- const response = await fetch(PACKAGES_LIST_URL, { headers: { "User-Agent": "atlisp-mcp" } });
641
- if (response.ok) {
642
- const data = await response.json();
643
- cachedPackages = data;
644
- cachedAt = now;
645
- return data;
646
- }
647
- } catch (e) {
648
- }
649
- return cachedPackages || MOCK_PACKAGES;
650
- }
651
- function flattenPackages(data) {
652
- if (Array.isArray(data)) return data;
653
- if (data && Array.isArray(data.packages)) return data.packages;
654
- return [];
655
- }
656
- async function listPackages() {
657
- if (!cad.connected) await cad.connect();
658
- if (!cad.connected) {
659
- const data2 = await fetchPackages();
660
- const items2 = flattenPackages(data2);
661
- const text2 = items2.length ? items2.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n") : "\u65E0\u53EF\u7528\u5305";
662
- return { content: [{ type: "text", text: text2 }] };
663
- }
664
- const result = await cad.sendCommandWithResult("(@::package-list-in-local)");
665
- if (result && result !== "nil") return { content: [{ type: "text", text: result }] };
666
- const data = await fetchPackages();
667
- const items = flattenPackages(data);
668
- const text = items.length ? `\u4ECE\u6CE8\u518C\u8868\u83B7\u53D6\u7684 @lisp \u5305:
669
- ${items.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n")}` : MOCK_PACKAGES.map((p) => `${p.name} v${p.version} - ${p.description}`).join("\n");
670
- return { content: [{ type: "text", text: `\u5DF2\u5B89\u88C5\u7684 @lisp \u5305:
671
- ${text}` }] };
672
- }
673
- async function searchPackages(query) {
674
- const data = await fetchPackages();
675
- if (!data) {
676
- return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6\u5305\u5217\u8868" }], isError: true };
677
- }
678
- const items = flattenPackages(data);
679
- const results = [];
680
- if (query) {
681
- const q = query.toLowerCase();
682
- for (const p of items) {
683
- const name = (p.name || "").toLowerCase();
684
- const desc = (p.description || "").toLowerCase();
685
- if (name.includes(q) || desc.includes(q)) results.push(p);
686
- }
687
- } else {
688
- for (const p of items) results.push(p);
689
- }
690
- if (results.length === 0) {
691
- return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5305\u542B "${query}" \u7684\u5305` }] };
692
- }
693
- return { content: [{ type: "text", text: `\u641C\u7D22\u7ED3\u679C (${results.length}):
694
- ${results.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n")}` }] };
695
- }
696
- async function installPackage(packageName) {
697
- const data = await fetchPackages();
698
- const items = flattenPackages(data);
699
- const pkg = items.find((p) => p.name === packageName);
700
- if (!pkg) {
701
- return { content: [{ type: "text", text: `\u9519\u8BEF: \u5305 "${packageName}" \u4E0D\u5B58\u5728` }], isError: true };
702
- }
703
- if (cad.connected) {
704
- await cad.sendCommand(`(@::load-module '${packageName})
705
- `);
706
- }
707
- return { content: [{ type: "text", text: `\u5305 "${packageName}" \u5B89\u88C5\u547D\u4EE4\u5DF2\u53D1\u9001\uFF01` }] };
708
- }
709
- var PACKAGES_LIST_URL, cachedPackages, cachedAt, CACHE_TTL;
710
- var init_package_handlers = __esm({
711
- "src/handlers/package-handlers.js"() {
712
- init_cad();
713
- init_constants();
714
- PACKAGES_LIST_URL = process.env.PACKAGES_LIST_URL || "http://s3.atlisp.cn/packages-list?fmt=json";
715
- cachedPackages = null;
716
- cachedAt = null;
717
- CACHE_TTL = 10 * 60 * 1e3;
718
- }
719
- });
720
-
721
- // src/handlers/function-handlers.js
722
- import fs2 from "fs";
723
- import path3 from "path";
724
- import os2 from "os";
725
- function readCacheFromDisk() {
726
- try {
727
- const stat = fs2.statSync(FUNCLIB_CACHE_FILE);
728
- if (Date.now() - stat.mtimeMs < FUNCLIB_CACHE_TTL) {
729
- const raw = fs2.readFileSync(FUNCLIB_CACHE_FILE, "utf-8");
730
- return JSON.parse(raw);
731
- }
732
- } catch {
733
- }
734
- return null;
735
- }
736
- function writeCacheToDisk(data) {
737
- try {
738
- fs2.mkdirSync(CACHE_DIR, { recursive: true });
739
- fs2.writeFileSync(FUNCLIB_CACHE_FILE, JSON.stringify(data), "utf-8");
740
- } catch (e) {
741
- log(`function-handlers writeCache error: ${e.message}`);
742
- }
743
- }
744
- async function fetchFunctions() {
745
- const diskCache = readCacheFromDisk();
746
- if (diskCache) return diskCache;
747
- try {
748
- const response = await fetch(FUNCTIONS_URL, { headers: { "User-Agent": "atlisp-mcp" } });
749
- if (response.ok) {
750
- const data = await response.json();
751
- writeCacheToDisk(data);
752
- return data;
753
- }
754
- } catch (e) {
755
- log(`function-handlers fetch error: ${e.message}`);
756
- }
757
- return diskCache;
758
- }
759
- async function getFunctionUsage(funcName, packageName) {
760
- try {
761
- let results = [];
762
- const data = await fetchFunctions();
763
- if (data) {
764
- const funcs = Object.values(data.all_functions || {});
765
- const func = funcs.find((f2) => f2.name === funcName || f2.name === `${packageName}:${funcName}`);
766
- if (func) results.push(func);
767
- }
768
- if (results.length === 0 && FUNCTIONS_DIR) {
769
- try {
770
- const files = fs2.readdirSync(FUNCTIONS_DIR).filter((f2) => f2.endsWith(".json"));
771
- for (const file of files) {
772
- const raw = fs2.readFileSync(path3.join(FUNCTIONS_DIR, file), "utf-8");
773
- const data2 = JSON.parse(raw);
774
- const func = data2.functions?.find((f2) => f2.name === funcName);
775
- if (func) results.push(func);
776
- }
777
- } catch {
778
- }
779
- }
780
- if (results.length === 0) {
781
- return { content: [{ type: "text", text: `\u672A\u627E\u5230\u51FD\u6570: ${funcName}` }], isError: true };
782
- }
783
- const f = results[0];
784
- const text = `\u51FD\u6570: ${f.name}
785
- \u63CF\u8FF0: ${f.description || "-"}
786
- \u53C2\u6570: ${f.params?.join(", ") || "-"}
787
- \u8FD4\u56DE\u503C: ${f.return || "-"}
788
- \u793A\u4F8B: ${f.usage || "-"}`;
789
- return { content: [{ type: "text", text }] };
790
- } catch (e) {
791
- return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
792
- }
793
- }
794
- async function listSymbols(packageName) {
795
- try {
796
- let allFuncs = [];
797
- const data = await fetchFunctions();
798
- if (data) {
799
- const funcs = Object.values(data.all_functions || {});
800
- if (packageName) {
801
- allFuncs = funcs.filter((f) => f.name.startsWith(`${packageName}:`));
802
- } else {
803
- allFuncs = funcs.map((f) => `${f.name}: ${f.description || "-"}`);
804
- }
805
- }
806
- if (allFuncs.length === 0 && FUNCTIONS_DIR) {
807
- try {
808
- const files = fs2.readdirSync(FUNCTIONS_DIR).filter((f) => f.endsWith(".json"));
809
- for (const file of files) {
810
- const raw = fs2.readFileSync(path3.join(FUNCTIONS_DIR, file), "utf-8");
811
- const data2 = JSON.parse(raw);
812
- const funcs = Object.values(data2.all_functions || {});
813
- if (packageName) {
814
- allFuncs = funcs.filter((f) => f.name.startsWith(`${packageName}:`));
815
- } else {
816
- for (const f of funcs) {
817
- allFuncs.push(`${f.name}: ${f.description || "-"}`);
818
- }
819
- }
820
- }
821
- } catch (err) {
822
- log(`listSymbols readDir error: ${err.message}`);
823
- }
824
- }
825
- if (allFuncs.length === 0) {
826
- return await listFunctionsInCad();
827
- }
828
- if (packageName) {
829
- return { content: [{ type: "text", text: `${packageName} \u5305\u51FD\u6570 (${allFuncs.length}):
830
- ${allFuncs.map((f) => f.name).join("\n")}` }] };
831
- }
832
- return { content: [{ type: "text", text: allFuncs.join("\n") }] };
833
- } catch (e) {
834
- return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
835
- }
836
- }
837
- var FUNCTIONS_DIR, FUNCTIONS_URL, CACHE_DIR, FUNCLIB_CACHE_FILE, FUNCLIB_CACHE_TTL;
838
- var init_function_handlers = __esm({
839
- "src/handlers/function-handlers.js"() {
840
- init_logger();
841
- init_cad_handlers();
842
- FUNCTIONS_DIR = process.env.FUNCTIONS_DIR || "";
843
- FUNCTIONS_URL = process.env.FUNCTIONS_URL || "http://s3.atlisp.cn/json/functions.json";
844
- CACHE_DIR = path3.join(os2.homedir(), ".atlisp", "cache");
845
- FUNCLIB_CACHE_FILE = path3.join(CACHE_DIR, "functions.json");
846
- FUNCLIB_CACHE_TTL = 24 * 60 * 60 * 1e3;
847
- }
848
- });
849
-
850
- // src/handlers/resource-handlers.js
851
- import fs3 from "fs";
852
- import path4 from "path";
853
- import { fileURLToPath as fileURLToPath2 } from "url";
854
- function getCached(key) {
855
- const entry = resourceCache.get(key);
856
- if (entry && Date.now() - entry.timestamp < CACHE_TTL2) {
857
- return entry.data;
858
- }
859
- resourceCache.delete(key);
860
- return null;
861
- }
862
- function setCache(key, data) {
863
- resourceCache.set(key, { data, timestamp: Date.now() });
864
- }
865
- function clearCache(uri) {
866
- if (uri) {
867
- for (const key of resourceCache.keys()) {
868
- if (key.startsWith(uri.split("://")[1]?.split("/")[0] + ":")) {
869
- resourceCache.delete(key);
870
- }
871
- }
872
- } else {
873
- resourceCache.clear();
874
- }
875
- }
876
- function parseLispList(str) {
877
- if (!str || typeof str !== "string") return null;
878
- const trimmed = str.trim();
879
- if (!trimmed.startsWith("(") || !trimmed.endsWith(")")) return null;
880
- const content = trimmed.slice(1, -1).trim();
881
- if (!content) return [];
882
- const result = [];
883
- let depth = 0;
884
- let current = "";
885
- let inString = false;
886
- let escaped = false;
887
- for (let i = 0; i < content.length; i++) {
888
- const ch = content[i];
889
- if (escaped) {
890
- current += ch;
891
- escaped = false;
892
- continue;
893
- }
894
- if (ch === "\\") {
895
- current += ch;
896
- escaped = true;
897
- continue;
898
- }
899
- if (ch === '"') {
900
- current += ch;
901
- inString = !inString;
902
- continue;
903
- }
904
- if (inString) {
905
- current += ch;
906
- continue;
907
- }
908
- if (ch === "(" || ch === "[") {
909
- current += ch;
910
- depth++;
911
- } else if (ch === ")" || ch === "]") {
912
- current += ch;
913
- depth--;
914
- } else if (ch === " " && depth === 0) {
915
- if (current.trim()) {
916
- result.push(current.trim());
917
- current = "";
918
- }
919
- continue;
920
- }
921
- current += ch;
922
- }
923
- if (current.trim()) result.push(current.trim());
924
- return result.map((item) => {
925
- const t = item.trim();
926
- if (t === "nil") return null;
927
- if (t === "t") return true;
928
- if (t === "T") return true;
929
- const num = Number(t);
930
- if (!isNaN(num) && t !== "") return num;
931
- if (t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
932
- return t;
933
- });
934
- }
935
- function parseLispRecursive(str) {
936
- if (!str || typeof str !== "string") return null;
937
- str = str.trim();
938
- if (!str) return null;
939
- let pos = 0;
940
- function skipWs() {
941
- while (pos < str.length && /\s/.test(str[pos])) pos++;
942
- }
943
- function parseValue() {
944
- skipWs();
945
- if (pos >= str.length) return null;
946
- const ch = str[pos];
947
- if (ch === "(") return parseList();
948
- if (ch === '"') return parseString();
949
- if (ch === "'") {
950
- pos++;
951
- return parseValue();
952
- }
953
- return parseAtom();
538
+ function parseValue() {
539
+ skipWs();
540
+ if (pos >= str.length) return null;
541
+ const ch = str[pos];
542
+ if (ch === "(") return parseList();
543
+ if (ch === '"') return parseString();
544
+ if (ch === "'") {
545
+ pos++;
546
+ return parseValue();
547
+ }
548
+ return parseAtom();
954
549
  }
955
550
  function parseString() {
956
551
  pos++;
@@ -1131,6 +726,19 @@ function buildTextCode({ layer, bbox, offset, limit }) {
1131
726
  (@t:run))
1132
727
  `;
1133
728
  }
729
+ var RESOURCES = [
730
+ { uri: "atlisp://cad/info", name: "CAD Info", description: "CAD \u8FDE\u63A5\u4FE1\u606F", mimeType: "application/json", subscribable: true },
731
+ { uri: "atlisp://dwg/layers", name: "Layers", description: "\u56FE\u5C42\u5217\u8868", mimeType: "application/json", subscribable: true },
732
+ { uri: "atlisp://dwg/entities", name: "Entities", description: "\u56FE\u5143\u5217\u8868\uFF0C\u8FD4\u56DE\u603B\u6570\u548C\u524D3000\u4E2A\u56FE\u5143\uFF0C\u6587\u672C\u9644\u52A0 text \u5B57\u6BB5\uFF0C\u66F2\u7EBF\u9644\u52A0 points \u6570\u7EC4\uFF0CINSERT \u9644\u52A0 blockName\uFF0C\u652F\u6301 ?type=LINE&layer=0&bbox=0,0,100,100 \u8FC7\u6EE4", mimeType: "application/json", subscribable: true },
733
+ { uri: "atlisp://dwg/texts", name: "Texts", description: "\u6587\u672C\u5B9E\u4F53\u5185\u5BB9\u5217\u8868 (TEXT/MTEXT/ATTRIB/TCH_TEXT)\uFF0C\u652F\u6301 ?layer=0&bbox=0,0,100,100&limit=5000&offset=0", mimeType: "application/json", subscribable: true },
734
+ { uri: "atlisp://dwg/name", name: "DWG Name", description: "\u5F53\u524D DWG \u6587\u4EF6\u540D", mimeType: "application/json", subscribable: true },
735
+ { uri: "atlisp://dwg/path", name: "DWG Path", description: "\u6587\u4EF6\u5B8C\u6574\u8DEF\u5F84", mimeType: "application/json", subscribable: true },
736
+ { uri: "atlisp://cad/dwgs", name: "DWG List", description: "\u6240\u6709\u6253\u5F00\u7684 DWG \u6587\u4EF6\u5217\u8868", mimeType: "application/json", subscribable: true },
737
+ { uri: "atlisp://packages", name: "Packages", description: "\u5DF2\u5B89\u88C5\u5305\u5217\u8868", mimeType: "application/json", subscribable: true },
738
+ { uri: "atlisp://platforms", name: "Platforms", description: "\u652F\u6301\u7684\u5E73\u53F0\u4FE1\u606F", mimeType: "application/json", subscribable: false },
739
+ { uri: "atlisp://standards/drafting", name: "Drafting Standards", description: "CAD \u5236\u56FE\u89C4\u8303", mimeType: "application/json", subscribable: false },
740
+ { uri: "atlisp://standards/coding", name: "Coding Standards", description: "@lisp \u7F16\u7801\u89C4\u8303", mimeType: "application/json", subscribable: false }
741
+ ];
1134
742
  function parseQuery(uri) {
1135
743
  const qIdx = uri.indexOf("?");
1136
744
  if (qIdx === -1) return { base: uri, params: {} };
@@ -1157,7 +765,7 @@ async function readResource(uri) {
1157
765
  const { base, params } = parseQuery(uri);
1158
766
  switch (base) {
1159
767
  case "atlisp://cad/info":
1160
- return await getCadInfo2();
768
+ return await getCadInfo();
1161
769
  case "atlisp://dwg/layers":
1162
770
  return await getLayers(params.name);
1163
771
  case "atlisp://dwg/entities":
@@ -1182,7 +790,7 @@ async function readResource(uri) {
1182
790
  throw new Error(`\u8D44\u6E90\u4E0D\u5B58\u5728: ${uri}`);
1183
791
  }
1184
792
  }
1185
- async function getCadInfo2() {
793
+ async function getCadInfo() {
1186
794
  const cached = getCached("cad:info");
1187
795
  if (cached) return cached;
1188
796
  if (!cad.connected) {
@@ -1476,94 +1084,666 @@ async function getDwgList() {
1476
1084
  setCache(cacheKey, []);
1477
1085
  return [];
1478
1086
  }
1479
- let parsed = [];
1480
- try {
1481
- parsed = JSON.parse(result);
1482
- } catch (e) {
1483
- const listResult = parseLispList(result);
1484
- if (listResult) {
1485
- parsed = listResult;
1087
+ let parsed = [];
1088
+ try {
1089
+ parsed = JSON.parse(result);
1090
+ } catch (e) {
1091
+ const listResult = parseLispList(result);
1092
+ if (listResult) {
1093
+ parsed = listResult;
1094
+ }
1095
+ }
1096
+ if (!Array.isArray(parsed)) parsed = [];
1097
+ const dwgList = parsed.map((item) => {
1098
+ if (Array.isArray(item) && item.length >= 2) {
1099
+ return { name: String(item[0]), path: String(item[1]) };
1100
+ }
1101
+ return null;
1102
+ }).filter(Boolean);
1103
+ setCache(cacheKey, dwgList);
1104
+ return dwgList;
1105
+ } catch (e) {
1106
+ setCache(cacheKey, []);
1107
+ return [];
1108
+ }
1109
+ }
1110
+ function loadStandardsFile(filename) {
1111
+ const filePath = path3.join(STANDARDS_DIR, filename);
1112
+ try {
1113
+ const raw = fs2.readFileSync(filePath, "utf-8");
1114
+ return JSON.parse(raw);
1115
+ } catch (e) {
1116
+ log(`Error loading standards file ${filename}: ${e.message}`);
1117
+ return null;
1118
+ }
1119
+ }
1120
+ var standardsCache = /* @__PURE__ */ new Map();
1121
+ function getCachedStandards(key) {
1122
+ const entry = standardsCache.get(key);
1123
+ if (entry && Date.now() - entry.timestamp < 3e4) return entry.data;
1124
+ standardsCache.delete(key);
1125
+ return null;
1126
+ }
1127
+ function setCachedStandards(key, data) {
1128
+ standardsCache.set(key, { data, timestamp: Date.now() });
1129
+ }
1130
+ function getDraftingStandards() {
1131
+ const cached = getCachedStandards("drafting");
1132
+ if (cached) return cached;
1133
+ const data = loadStandardsFile("drafting.json");
1134
+ if (data) setCachedStandards("drafting", data);
1135
+ return data || { error: "\u5236\u56FE\u89C4\u8303\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25" };
1136
+ }
1137
+ function getCodingStandards() {
1138
+ const cached = getCachedStandards("coding");
1139
+ if (cached) return cached;
1140
+ const data = loadStandardsFile("coding.json");
1141
+ if (data) setCachedStandards("coding", data);
1142
+ return data || { error: "\u7F16\u7801\u89C4\u8303\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25" };
1143
+ }
1144
+
1145
+ // src/session-manager.js
1146
+ var Session = class {
1147
+ constructor(clientId) {
1148
+ this.clientId = clientId;
1149
+ this.subscriptions = /* @__PURE__ */ new Set();
1150
+ this.createdAt = Date.now();
1151
+ this.connected = false;
1152
+ }
1153
+ };
1154
+ var SessionManager = class {
1155
+ #sessions = /* @__PURE__ */ new Map();
1156
+ createSession(clientId) {
1157
+ let session = this.#sessions.get(clientId);
1158
+ if (!session) {
1159
+ session = new Session(clientId);
1160
+ this.#sessions.set(clientId, session);
1161
+ }
1162
+ return session;
1163
+ }
1164
+ getSession(clientId) {
1165
+ return this.#sessions.get(clientId) || null;
1166
+ }
1167
+ hasSession(clientId) {
1168
+ return this.#sessions.has(clientId);
1169
+ }
1170
+ removeSession(clientId) {
1171
+ return this.#sessions.delete(clientId);
1172
+ }
1173
+ subscribe(clientId, uri) {
1174
+ const session = this.#sessions.get(clientId);
1175
+ if (session) {
1176
+ session.subscriptions.add(uri);
1177
+ return true;
1178
+ }
1179
+ return false;
1180
+ }
1181
+ unsubscribe(clientId, uri) {
1182
+ const session = this.#sessions.get(clientId);
1183
+ if (session) {
1184
+ return session.subscriptions.delete(uri);
1185
+ }
1186
+ return false;
1187
+ }
1188
+ getSubscriptions(clientId) {
1189
+ const session = this.#sessions.get(clientId);
1190
+ return session ? Array.from(session.subscriptions) : [];
1191
+ }
1192
+ isSubscribed(clientId, uri) {
1193
+ const session = this.#sessions.get(clientId);
1194
+ return session ? session.subscriptions.has(uri) : false;
1195
+ }
1196
+ getAllSubscribedUris() {
1197
+ const all = /* @__PURE__ */ new Set();
1198
+ for (const session of this.#sessions.values()) {
1199
+ for (const uri of session.subscriptions) {
1200
+ all.add(uri);
1201
+ }
1202
+ }
1203
+ return all;
1204
+ }
1205
+ list() {
1206
+ return Array.from(this.#sessions.entries()).map(([id, s]) => ({
1207
+ clientId: id,
1208
+ subscriptions: Array.from(s.subscriptions),
1209
+ createdAt: s.createdAt,
1210
+ connected: s.connected
1211
+ }));
1212
+ }
1213
+ count() {
1214
+ return this.#sessions.size;
1215
+ }
1216
+ clear() {
1217
+ this.#sessions.clear();
1218
+ }
1219
+ };
1220
+
1221
+ // src/subscription-manager.js
1222
+ var sessionManager = new SessionManager();
1223
+ var _notifyFn = null;
1224
+ function setNotify(fn) {
1225
+ _notifyFn = fn;
1226
+ }
1227
+ function subscribe(uri, sessionId) {
1228
+ if (sessionId) {
1229
+ return sessionManager.subscribe(sessionId, uri);
1230
+ }
1231
+ return true;
1232
+ }
1233
+ function unsubscribe(uri, sessionId) {
1234
+ if (sessionId) {
1235
+ return sessionManager.unsubscribe(sessionId, uri);
1236
+ }
1237
+ return true;
1238
+ }
1239
+ function list(sessionId) {
1240
+ if (sessionId) {
1241
+ return sessionManager.getSubscriptions(sessionId);
1242
+ }
1243
+ return Array.from(sessionManager.getAllSubscribedUris());
1244
+ }
1245
+ function isSubscribed(uri, sessionId) {
1246
+ if (sessionId) {
1247
+ return sessionManager.isSubscribed(sessionId, uri);
1248
+ }
1249
+ return sessionManager.getAllSubscribedUris().has(uri);
1250
+ }
1251
+ function notify(uri, data) {
1252
+ if (_notifyFn) {
1253
+ _notifyFn(uri, data);
1254
+ }
1255
+ }
1256
+
1257
+ // src/sse-session.js
1258
+ import { randomUUID as randomUUID2 } from "crypto";
1259
+ var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
1260
+ var DEFAULT_RETRY_INTERVAL = 3e4;
1261
+ var MAX_SENT_MESSAGES = 1e3;
1262
+ var SSE_EVENTS = {
1263
+ MESSAGE: "message",
1264
+ ENDPOINT: "endpoint",
1265
+ PING: "ping",
1266
+ ERROR: "error",
1267
+ PROGRESS: "progress",
1268
+ CAPABILITIES: "capabilities"
1269
+ };
1270
+ var SseSession = class {
1271
+ #res;
1272
+ #clientId;
1273
+ #active = true;
1274
+ #heartbeatTimer = null;
1275
+ #lastEventId = 0;
1276
+ #heartbeatInterval;
1277
+ #retryInterval;
1278
+ #sessionData = {};
1279
+ #sentMessages = [];
1280
+ #backpressureBuffer = [];
1281
+ #drainHandler = null;
1282
+ constructor(res, options = {}) {
1283
+ this.#res = res;
1284
+ this.#clientId = options.clientId || randomUUID2();
1285
+ this.#heartbeatInterval = options.heartbeatInterval || DEFAULT_HEARTBEAT_INTERVAL;
1286
+ this.#retryInterval = options.retryInterval || DEFAULT_RETRY_INTERVAL;
1287
+ this.#sessionData = options.sessionData || {};
1288
+ if (options.resumeFromId && options.resumeFromId > 0) {
1289
+ this.#lastEventId = options.resumeFromId;
1290
+ }
1291
+ this._setupSseHeaders();
1292
+ this._sendRetry();
1293
+ this._setupDrain();
1294
+ }
1295
+ get clientId() {
1296
+ return this.#clientId;
1297
+ }
1298
+ get isActive() {
1299
+ return this.#active;
1300
+ }
1301
+ get lastEventId() {
1302
+ return this.#lastEventId;
1303
+ }
1304
+ get sessionData() {
1305
+ return this.#sessionData;
1306
+ }
1307
+ get sentMessages() {
1308
+ return [...this.#sentMessages];
1309
+ }
1310
+ getMessagesSince(eventId) {
1311
+ return this.#sentMessages.filter((m) => m.id > eventId);
1312
+ }
1313
+ setSessionData(key, value) {
1314
+ this.#sessionData[key] = value;
1315
+ }
1316
+ getSessionData(key) {
1317
+ return this.#sessionData[key];
1318
+ }
1319
+ get retryInterval() {
1320
+ return this.#retryInterval;
1321
+ }
1322
+ setRetryInterval(ms) {
1323
+ this.#retryInterval = ms;
1324
+ }
1325
+ _sendRetry() {
1326
+ this._write(`retry: ${this.#retryInterval}
1327
+
1328
+ `);
1329
+ }
1330
+ _setupSseHeaders() {
1331
+ this.#res.setHeader("Content-Type", "text/event-stream");
1332
+ this.#res.setHeader("Cache-Control", "no-cache");
1333
+ this.#res.setHeader("Connection", "keep-alive");
1334
+ this.#res.setHeader("Transfer-Encoding", "chunked");
1335
+ this.#res.setHeader("X-Accel-Buffering", "no");
1336
+ if (typeof this.#res.flush !== "function") {
1337
+ this.#res.flush = () => {
1338
+ };
1339
+ }
1340
+ }
1341
+ _formatEvent(event, data, id = null) {
1342
+ const lines = [];
1343
+ if (id !== null) {
1344
+ lines.push(`id: ${id}`);
1345
+ }
1346
+ lines.push(`event: ${event}`);
1347
+ if (data !== null && data !== void 0) {
1348
+ const jsonStr = JSON.stringify(data);
1349
+ lines.push(`data: ${jsonStr}`);
1350
+ } else {
1351
+ lines.push("data:");
1352
+ }
1353
+ return lines.join("\n") + "\n\n";
1354
+ }
1355
+ _setupDrain() {
1356
+ if (typeof this.#res.on !== "function") return;
1357
+ this.#drainHandler = () => {
1358
+ if (!this.#active) return;
1359
+ if (this.#backpressureBuffer.length > 0) {
1360
+ this._drainBuffer();
1361
+ }
1362
+ };
1363
+ this.#res.on("drain", this.#drainHandler);
1364
+ }
1365
+ _drainBuffer() {
1366
+ const batch = this.#backpressureBuffer.splice(0, 50);
1367
+ for (const item of batch) {
1368
+ const ok = this.#res.write(item);
1369
+ if (!ok) {
1370
+ this.#backpressureBuffer.unshift(...batch.slice(batch.indexOf(item)));
1371
+ return;
1372
+ }
1373
+ }
1374
+ this._flush();
1375
+ if (this.#backpressureBuffer.length > 0) {
1376
+ this._drainBuffer();
1377
+ }
1378
+ }
1379
+ _write(content) {
1380
+ if (!this.#active) return false;
1381
+ try {
1382
+ if (this.#backpressureBuffer.length > 0) {
1383
+ this.#backpressureBuffer.push(content);
1384
+ return true;
1385
+ }
1386
+ const result = this.#res.write(content);
1387
+ if (typeof this.#res.flush === "function") {
1388
+ this.#res.flush();
1389
+ }
1390
+ if (result === false) {
1391
+ this.#backpressureBuffer.push(content);
1392
+ return true;
1393
+ }
1394
+ return true;
1395
+ } catch (e) {
1396
+ this.#active = false;
1397
+ this.close();
1398
+ return false;
1399
+ }
1400
+ }
1401
+ _flush() {
1402
+ try {
1403
+ if (typeof this.#res.flush === "function") {
1404
+ this.#res.flush();
1405
+ }
1406
+ } catch (e) {
1407
+ }
1408
+ }
1409
+ flushBackpressure() {
1410
+ if (this.#backpressureBuffer.length > 0) {
1411
+ this._drainBuffer();
1412
+ }
1413
+ }
1414
+ sendEvent(event, data, id = null) {
1415
+ this.#lastEventId++;
1416
+ const eventId = id !== null ? id : this.#lastEventId;
1417
+ const formatted = this._formatEvent(event, data, eventId);
1418
+ this.#sentMessages.push({ event, data, id: eventId, timestamp: Date.now() });
1419
+ if (this.#sentMessages.length > MAX_SENT_MESSAGES) {
1420
+ this.#sentMessages.splice(0, this.#sentMessages.length - MAX_SENT_MESSAGES);
1421
+ }
1422
+ this._write(formatted);
1423
+ this._flush();
1424
+ return true;
1425
+ }
1426
+ sendMessage(data) {
1427
+ return this.sendEvent(SSE_EVENTS.MESSAGE, data);
1428
+ }
1429
+ sendEndpoint(path9) {
1430
+ return this.sendEvent(SSE_EVENTS.ENDPOINT, path9);
1431
+ }
1432
+ sendError(code, message, id = null) {
1433
+ return this.sendEvent(SSE_EVENTS.ERROR, { code, message }, id);
1434
+ }
1435
+ sendPing() {
1436
+ return this.sendEvent(SSE_EVENTS.PING, { timestamp: Date.now() });
1437
+ }
1438
+ sendProgress(current, total, message = "") {
1439
+ return this.sendEvent(SSE_EVENTS.PROGRESS, { current, total, message });
1440
+ }
1441
+ sendCapabilities(capabilities) {
1442
+ return this.sendEvent(SSE_EVENTS.CAPABILITIES, capabilities);
1443
+ }
1444
+ sendResponse(jsonrpcResponse, id = null) {
1445
+ const eventId = id !== null ? id : this.#lastEventId;
1446
+ return this.sendEvent(SSE_EVENTS.MESSAGE, jsonrpcResponse, eventId);
1447
+ }
1448
+ startHeartbeat(interval = null) {
1449
+ this.stopHeartbeat();
1450
+ const ms = interval || this.#heartbeatInterval;
1451
+ this.#heartbeatTimer = setInterval(() => {
1452
+ if (!this.#active) {
1453
+ this.stopHeartbeat();
1454
+ return;
1455
+ }
1456
+ if (!this.sendPing()) {
1457
+ this.stopHeartbeat();
1458
+ this.close();
1459
+ }
1460
+ }, ms);
1461
+ }
1462
+ stopHeartbeat() {
1463
+ if (this.#heartbeatTimer) {
1464
+ clearInterval(this.#heartbeatTimer);
1465
+ this.#heartbeatTimer = null;
1466
+ }
1467
+ }
1468
+ close() {
1469
+ this.#active = false;
1470
+ this.stopHeartbeat();
1471
+ if (this.#drainHandler) {
1472
+ this.#res.removeListener("drain", this.#drainHandler);
1473
+ this.#drainHandler = null;
1474
+ }
1475
+ this.#backpressureBuffer = [];
1476
+ try {
1477
+ this.#res.end();
1478
+ } catch (e) {
1479
+ }
1480
+ }
1481
+ };
1482
+
1483
+ // src/sse-session-manager.js
1484
+ import fs3 from "fs";
1485
+ import path4 from "path";
1486
+ import os2 from "os";
1487
+ var SESSION_STORE_DIR = path4.join(os2.homedir(), ".atlisp", "sessions");
1488
+ var SESSION_STORE_TTL = 24 * 60 * 60 * 1e3;
1489
+ function getSessionStorePath(clientId) {
1490
+ return path4.join(SESSION_STORE_DIR, `${clientId}.json`);
1491
+ }
1492
+ function persistSessionData(clientId, sessionData) {
1493
+ try {
1494
+ fs3.mkdirSync(SESSION_STORE_DIR, { recursive: true });
1495
+ const store = {
1496
+ clientId,
1497
+ sessionData,
1498
+ savedAt: Date.now()
1499
+ };
1500
+ fs3.writeFileSync(getSessionStorePath(clientId), JSON.stringify(store), "utf-8");
1501
+ } catch (e) {
1502
+ console.error("Failed to persist session:", e.message);
1503
+ }
1504
+ }
1505
+ function loadPersistedSessionData(clientId) {
1506
+ try {
1507
+ const filePath = getSessionStorePath(clientId);
1508
+ if (!fs3.existsSync(filePath)) return null;
1509
+ const raw = fs3.readFileSync(filePath, "utf-8");
1510
+ const store = JSON.parse(raw);
1511
+ if (Date.now() - store.savedAt > SESSION_STORE_TTL) {
1512
+ fs3.unlinkSync(filePath);
1513
+ return null;
1514
+ }
1515
+ return store.sessionData || {};
1516
+ } catch (e) {
1517
+ return null;
1518
+ }
1519
+ }
1520
+ var SseSessionManager = class {
1521
+ #sessions = /* @__PURE__ */ new Map();
1522
+ #eventHandlers = /* @__PURE__ */ new Map();
1523
+ constructor() {
1524
+ this.#sessions = /* @__PURE__ */ new Map();
1525
+ this.#eventHandlers = /* @__PURE__ */ new Map();
1526
+ }
1527
+ add(clientId, session) {
1528
+ if (!(session instanceof SseSession)) {
1529
+ throw new Error("session must be an SseSession instance");
1530
+ }
1531
+ const persisted = loadPersistedSessionData(clientId);
1532
+ if (persisted) {
1533
+ for (const [key, value] of Object.entries(persisted)) {
1534
+ session.setSessionData(key, value);
1535
+ }
1536
+ }
1537
+ this.#sessions.set(clientId, session);
1538
+ session.startHeartbeat();
1539
+ }
1540
+ remove(clientId) {
1541
+ const session = this.#sessions.get(clientId);
1542
+ if (session) {
1543
+ persistSessionData(clientId, session.sessionData);
1544
+ session.close();
1545
+ this.#sessions.delete(clientId);
1546
+ return true;
1547
+ }
1548
+ return false;
1549
+ }
1550
+ get(clientId) {
1551
+ return this.#sessions.get(clientId) || null;
1552
+ }
1553
+ has(clientId) {
1554
+ return this.#sessions.has(clientId);
1555
+ }
1556
+ list() {
1557
+ return Array.from(this.#sessions.keys()).map((id) => ({
1558
+ clientId: id,
1559
+ isActive: this.#sessions.get(id)?.isActive ?? false
1560
+ }));
1561
+ }
1562
+ listActive() {
1563
+ return this.list().filter((s) => s.isActive);
1564
+ }
1565
+ count() {
1566
+ return this.#sessions.size;
1567
+ }
1568
+ sendToClient(clientId, event, data, id = null) {
1569
+ const session = this.#sessions.get(clientId);
1570
+ if (!session || !session.isActive) {
1571
+ return false;
1572
+ }
1573
+ return session.sendEvent(event, data, id);
1574
+ }
1575
+ sendMessageToClient(clientId, data) {
1576
+ return this.sendToClient(clientId, SSE_EVENTS.MESSAGE, data);
1577
+ }
1578
+ sendErrorToClient(clientId, code, message, id = null) {
1579
+ return this.sendToClient(clientId, SSE_EVENTS.ERROR, { code, message }, id);
1580
+ }
1581
+ sendProgressToClient(clientId, current, total, message = "") {
1582
+ return this.sendToClient(clientId, SSE_EVENTS.PROGRESS, { current, total, message });
1583
+ }
1584
+ broadcast(event, data) {
1585
+ let successCount = 0;
1586
+ for (const [clientId, session] of this.#sessions) {
1587
+ if (session.isActive && session.sendEvent(event, data)) {
1588
+ successCount++;
1589
+ }
1590
+ }
1591
+ return successCount;
1592
+ }
1593
+ broadcastMessage(data) {
1594
+ return this.broadcast(SSE_EVENTS.MESSAGE, data);
1595
+ }
1596
+ broadcastError(code, message) {
1597
+ return this.broadcast(SSE_EVENTS.ERROR, { code, message });
1598
+ }
1599
+ on(event, handler) {
1600
+ if (!this.#eventHandlers.has(event)) {
1601
+ this.#eventHandlers.set(event, /* @__PURE__ */ new Set());
1602
+ }
1603
+ this.#eventHandlers.get(event).add(handler);
1604
+ }
1605
+ off(event, handler) {
1606
+ const handlers = this.#eventHandlers.get(event);
1607
+ if (handlers) {
1608
+ handlers.delete(handler);
1609
+ }
1610
+ }
1611
+ emit(event, data) {
1612
+ const handlers = this.#eventHandlers.get(event);
1613
+ if (handlers) {
1614
+ for (const handler of handlers) {
1615
+ try {
1616
+ handler(data);
1617
+ } catch (e) {
1618
+ }
1486
1619
  }
1487
1620
  }
1488
- if (!Array.isArray(parsed)) parsed = [];
1489
- const dwgList = parsed.map((item) => {
1490
- if (Array.isArray(item) && item.length >= 2) {
1491
- return { name: String(item[0]), path: String(item[1]) };
1621
+ }
1622
+ cleanupInactive() {
1623
+ for (const [clientId, session] of this.#sessions) {
1624
+ if (!session.isActive) {
1625
+ session.close();
1626
+ this.#sessions.delete(clientId);
1492
1627
  }
1628
+ }
1629
+ }
1630
+ closeAll() {
1631
+ for (const [clientId, session] of this.#sessions) {
1632
+ persistSessionData(clientId, session.sessionData);
1633
+ session.close();
1634
+ }
1635
+ this.#sessions.clear();
1636
+ }
1637
+ };
1638
+
1639
+ // src/routes.js
1640
+ import express from "express";
1641
+ import { randomUUID as randomUUID4 } from "crypto";
1642
+ import iconv from "iconv-lite";
1643
+ import rateLimit from "express-rate-limit";
1644
+
1645
+ // src/mcp-server-state.js
1646
+ import { AsyncLocalStorage } from "async_hooks";
1647
+
1648
+ // src/session-transport.js
1649
+ import { randomUUID as randomUUID3 } from "crypto";
1650
+ var SESSION_TIMEOUT = 30 * 60 * 1e3;
1651
+ var SessionTransport = class {
1652
+ constructor(sessionId) {
1653
+ this.sessionId = sessionId || randomUUID3();
1654
+ this.onclose = null;
1655
+ this.onerror = null;
1656
+ this.onmessage = null;
1657
+ this._pendingRequest = null;
1658
+ this._started = false;
1659
+ this._closed = false;
1660
+ this._cleanupTimer = null;
1661
+ }
1662
+ async start() {
1663
+ if (this._started) {
1664
+ throw new Error("Transport already started");
1665
+ }
1666
+ this._started = true;
1667
+ }
1668
+ async send(message) {
1669
+ if (this._closed) return;
1670
+ if (this._pendingRequest) {
1671
+ const { resolve, reject, timeout } = this._pendingRequest;
1672
+ clearTimeout(timeout);
1673
+ this._pendingRequest = null;
1674
+ resolve(message);
1675
+ }
1676
+ }
1677
+ async close() {
1678
+ if (this._closed) return;
1679
+ this._closed = true;
1680
+ if (this._pendingRequest) {
1681
+ const { reject, timeout } = this._pendingRequest;
1682
+ clearTimeout(timeout);
1683
+ this._pendingRequest = null;
1684
+ reject(new Error("Transport closed"));
1685
+ }
1686
+ this._clearCleanupTimer();
1687
+ this.onclose?.();
1688
+ }
1689
+ async handleRequest(message, timeoutMs = 6e4) {
1690
+ if (this._closed) {
1691
+ throw new Error("Transport closed");
1692
+ }
1693
+ if (!message || !message.method) {
1694
+ throw new Error("Invalid message");
1695
+ }
1696
+ if (!message.id) {
1697
+ this.onmessage?.(message);
1493
1698
  return null;
1494
- }).filter(Boolean);
1495
- setCache(cacheKey, dwgList);
1496
- return dwgList;
1497
- } catch (e) {
1498
- setCache(cacheKey, []);
1499
- return [];
1699
+ }
1700
+ return new Promise((resolve, reject) => {
1701
+ const timeout = setTimeout(() => {
1702
+ if (this._pendingRequest?.resolve === resolve) {
1703
+ this._pendingRequest = null;
1704
+ reject(new Error("Request timeout"));
1705
+ }
1706
+ }, timeoutMs);
1707
+ this._pendingRequest = { resolve, reject, timeout };
1708
+ this.onmessage?.(message);
1709
+ });
1500
1710
  }
1501
- }
1502
- function loadStandardsFile(filename) {
1503
- const filePath = path4.join(STANDARDS_DIR, filename);
1504
- try {
1505
- const raw = fs3.readFileSync(filePath, "utf-8");
1506
- return JSON.parse(raw);
1507
- } catch (e) {
1508
- log(`Error loading standards file ${filename}: ${e.message}`);
1509
- return null;
1711
+ resetCleanupTimer() {
1712
+ this._clearCleanupTimer();
1713
+ this._cleanupTimer = setTimeout(() => {
1714
+ this.close();
1715
+ }, SESSION_TIMEOUT);
1510
1716
  }
1511
- }
1512
- function getCachedStandards(key) {
1513
- const entry = standardsCache.get(key);
1514
- if (entry && Date.now() - entry.timestamp < 3e4) return entry.data;
1515
- standardsCache.delete(key);
1516
- return null;
1517
- }
1518
- function setCachedStandards(key, data) {
1519
- standardsCache.set(key, { data, timestamp: Date.now() });
1520
- }
1521
- function getDraftingStandards() {
1522
- const cached = getCachedStandards("drafting");
1523
- if (cached) return cached;
1524
- const data = loadStandardsFile("drafting.json");
1525
- if (data) setCachedStandards("drafting", data);
1526
- return data || { error: "\u5236\u56FE\u89C4\u8303\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25" };
1527
- }
1528
- function getCodingStandards() {
1529
- const cached = getCachedStandards("coding");
1530
- if (cached) return cached;
1531
- const data = loadStandardsFile("coding.json");
1532
- if (data) setCachedStandards("coding", data);
1533
- return data || { error: "\u7F16\u7801\u89C4\u8303\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25" };
1534
- }
1535
- var __dirname2, STANDARDS_DIR, CACHE_TTL2, resourceCache, RESOURCES, standardsCache;
1536
- var init_resource_handlers = __esm({
1537
- "src/handlers/resource-handlers.js"() {
1538
- init_cad();
1539
- init_constants();
1540
- init_logger();
1541
- init_config();
1542
- __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
1543
- STANDARDS_DIR = path4.join(__dirname2, "..", "standards");
1544
- CACHE_TTL2 = config_default.resourceCacheTtl;
1545
- resourceCache = /* @__PURE__ */ new Map();
1546
- RESOURCES = [
1547
- { uri: "atlisp://cad/info", name: "CAD Info", description: "CAD \u8FDE\u63A5\u4FE1\u606F", mimeType: "application/json", subscribable: true },
1548
- { uri: "atlisp://dwg/layers", name: "Layers", description: "\u56FE\u5C42\u5217\u8868", mimeType: "application/json", subscribable: true },
1549
- { uri: "atlisp://dwg/entities", name: "Entities", description: "\u56FE\u5143\u5217\u8868\uFF0C\u8FD4\u56DE\u603B\u6570\u548C\u524D3000\u4E2A\u56FE\u5143\uFF0C\u6587\u672C\u9644\u52A0 text \u5B57\u6BB5\uFF0C\u66F2\u7EBF\u9644\u52A0 points \u6570\u7EC4\uFF0CINSERT \u9644\u52A0 blockName\uFF0C\u652F\u6301 ?type=LINE&layer=0&bbox=0,0,100,100 \u8FC7\u6EE4", mimeType: "application/json", subscribable: true },
1550
- { uri: "atlisp://dwg/texts", name: "Texts", description: "\u6587\u672C\u5B9E\u4F53\u5185\u5BB9\u5217\u8868 (TEXT/MTEXT/ATTRIB/TCH_TEXT)\uFF0C\u652F\u6301 ?layer=0&bbox=0,0,100,100&limit=5000&offset=0", mimeType: "application/json", subscribable: true },
1551
- { uri: "atlisp://dwg/name", name: "DWG Name", description: "\u5F53\u524D DWG \u6587\u4EF6\u540D", mimeType: "application/json", subscribable: true },
1552
- { uri: "atlisp://dwg/path", name: "DWG Path", description: "\u6587\u4EF6\u5B8C\u6574\u8DEF\u5F84", mimeType: "application/json", subscribable: true },
1553
- { uri: "atlisp://cad/dwgs", name: "DWG List", description: "\u6240\u6709\u6253\u5F00\u7684 DWG \u6587\u4EF6\u5217\u8868", mimeType: "application/json", subscribable: true },
1554
- { uri: "atlisp://packages", name: "Packages", description: "\u5DF2\u5B89\u88C5\u5305\u5217\u8868", mimeType: "application/json", subscribable: true },
1555
- { uri: "atlisp://platforms", name: "Platforms", description: "\u652F\u6301\u7684\u5E73\u53F0\u4FE1\u606F", mimeType: "application/json", subscribable: false },
1556
- { uri: "atlisp://standards/drafting", name: "Drafting Standards", description: "CAD \u5236\u56FE\u89C4\u8303", mimeType: "application/json", subscribable: false },
1557
- { uri: "atlisp://standards/coding", name: "Coding Standards", description: "@lisp \u7F16\u7801\u89C4\u8303", mimeType: "application/json", subscribable: false }
1558
- ];
1559
- standardsCache = /* @__PURE__ */ new Map();
1717
+ _clearCleanupTimer() {
1718
+ if (this._cleanupTimer) {
1719
+ clearTimeout(this._cleanupTimer);
1720
+ this._cleanupTimer = null;
1721
+ }
1560
1722
  }
1561
- });
1723
+ };
1724
+
1725
+ // src/mcp-handlers.js
1726
+ import {
1727
+ ListToolsRequestSchema,
1728
+ CallToolRequestSchema,
1729
+ ListResourcesRequestSchema,
1730
+ ReadResourceRequestSchema,
1731
+ SubscribeRequestSchema,
1732
+ UnsubscribeRequestSchema,
1733
+ ListPromptsRequestSchema,
1734
+ GetPromptRequestSchema
1735
+ } from "@modelcontextprotocol/sdk/types.js";
1736
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1562
1737
 
1563
1738
  // src/handlers/prompt-handlers.js
1564
1739
  import fs4 from "fs";
1565
1740
  import path5 from "path";
1566
1741
  import { fileURLToPath as fileURLToPath3 } from "url";
1742
+ var __dirname3 = path5.dirname(fileURLToPath3(import.meta.url));
1743
+ var PROMPTS_DIR = path5.join(__dirname3, "..", "prompts");
1744
+ var externalPrompts = null;
1745
+ var externalPromptsAt = 0;
1746
+ var EXTERNAL_PROMPTS_TTL = 3e4;
1567
1747
  function loadExternalPrompts() {
1568
1748
  const now = Date.now();
1569
1749
  if (externalPrompts && now - externalPromptsAt < EXTERNAL_PROMPTS_TTL) {
@@ -1593,6 +1773,121 @@ function loadExternalPrompts() {
1593
1773
  externalPromptsAt = now;
1594
1774
  return externalPrompts;
1595
1775
  }
1776
+ var PROMPTS = [
1777
+ {
1778
+ name: "analyze-drawing",
1779
+ description: "\u5206\u6790\u5F53\u524D\u56FE\u7EB8\u7EDF\u8BA1\u4FE1\u606F",
1780
+ arguments: []
1781
+ },
1782
+ {
1783
+ name: "batch-draw-lines",
1784
+ description: "\u6279\u91CF\u7ED8\u5236\u7EBF\u6BB5",
1785
+ arguments: [
1786
+ { name: "count", description: "\u7EBF\u6BB5\u6570\u91CF", required: true },
1787
+ { name: "length", description: "\u7EBF\u6BB5\u957F\u5EA6", required: false }
1788
+ ]
1789
+ },
1790
+ {
1791
+ name: "generate-dimension",
1792
+ description: "\u751F\u6210\u5C3A\u5BF8\u6807\u6CE8",
1793
+ arguments: [
1794
+ { name: "style", description: "\u6807\u6CE8\u6837\u5F0F (horizontal/vertical/aligned)", required: false }
1795
+ ]
1796
+ },
1797
+ {
1798
+ name: "export-entities",
1799
+ description: "\u5BFC\u51FA\u56FE\u7EB8\u5B9E\u4F53\u7EDF\u8BA1",
1800
+ arguments: [
1801
+ { name: "format", description: "\u5BFC\u51FA\u683C\u5F0F (json/list)", required: false }
1802
+ ]
1803
+ },
1804
+ {
1805
+ name: "manage-layers",
1806
+ description: "\u56FE\u5C42\u7BA1\u7406\uFF1A\u521B\u5EFA\u3001\u51BB\u7ED3\u3001\u9501\u5B9A\u3001\u5220\u9664\u56FE\u5C42",
1807
+ arguments: [
1808
+ { name: "action", description: "\u64CD\u4F5C\u7C7B\u578B (list/freeze/lock/off/make/delete)", required: true },
1809
+ { name: "layers", description: "\u56FE\u5C42\u540D\uFF0C\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694", required: false },
1810
+ { name: "color", description: '\u56FE\u5C42\u989C\u8272 (ACI\u53F7\u6216RGB\u5982 "1,2,3")', required: false }
1811
+ ]
1812
+ },
1813
+ {
1814
+ name: "manage-blocks",
1815
+ description: "\u5757\u64CD\u4F5C\uFF1A\u63D2\u5165\u5757\u3001\u5217\u51FA\u5757\u3001\u83B7\u53D6/\u8BBE\u7F6E\u5C5E\u6027",
1816
+ arguments: [
1817
+ { name: "action", description: "\u64CD\u4F5C\u7C7B\u578B (list/insert/attributes/dynprops)", required: true },
1818
+ { name: "blockName", description: "\u5757\u540D", required: false },
1819
+ { name: "insertPoint", description: '\u63D2\u5165\u70B9 "x,y"', required: false },
1820
+ { name: "scale", description: "\u7F29\u653E\u6BD4\u4F8B", required: false }
1821
+ ]
1822
+ },
1823
+ {
1824
+ name: "curve-analysis",
1825
+ description: "\u66F2\u7EBF\u5206\u6790\uFF1A\u4EA4\u70B9\u3001\u6700\u8FD1\u70B9\u3001\u5B50\u6BB5\u3001\u7F13\u51B2\u533A",
1826
+ arguments: [
1827
+ { name: "action", description: "\u64CD\u4F5C (inters/subsegments/closestpoint/join)", required: true },
1828
+ { name: "distance", description: "\u8FDE\u63A5\u5BB9\u5DEE/\u504F\u79FB\u8DDD\u79BB", required: false }
1829
+ ]
1830
+ },
1831
+ {
1832
+ name: "geometry-calc",
1833
+ description: "\u51E0\u4F55\u8BA1\u7B97\uFF1A\u51F8\u5305\u3001\u70B9\u5230\u7EBF\u8DDD\u79BB\u3001\u9762\u5185\u6D4B\u8BD5\u3001\u53D8\u6362",
1834
+ arguments: [
1835
+ { name: "action", description: "\u64CD\u4F5C (convexhull/dist-point-line/in-curve-p/transform/centroid)", required: true },
1836
+ { name: "points", description: '\u70B9\u96C6 "x1,y1;x2,y2;..."', required: false }
1837
+ ]
1838
+ },
1839
+ {
1840
+ name: "text-process",
1841
+ description: "\u6587\u672C\u5904\u7406\uFF1AMTEXT\u89E3\u6790\u3001Markdown\u8F6CMTEXT\u3001\u53BB\u683C\u5F0F",
1842
+ arguments: [
1843
+ { name: "action", description: "\u64CD\u4F5C (parse-mtext/remove-fmt/from-markdown/mtext2text)", required: true },
1844
+ { name: "markdown", description: "Markdown\u6587\u672C\uFF08from-markdown\u64CD\u4F5C\u65F6\u7528\uFF09", required: false }
1845
+ ]
1846
+ },
1847
+ {
1848
+ name: "pickset-filter",
1849
+ description: "\u9009\u62E9\u96C6\u64CD\u4F5C\uFF1A\u8FC7\u6EE4\u3001\u8F6C\u6362\u3001\u6279\u91CF\u64CD\u4F5C",
1850
+ arguments: [
1851
+ { name: "action", description: "\u64CD\u4F5C (by-layer/by-type/by-box/erase/zoom)", required: true },
1852
+ { name: "dxfType", description: 'DXF\u7C7B\u578B "LINE, CIRCLE, LWPOLYLINE, INSERT, TEXT"', required: false },
1853
+ { name: "layerName", description: "\u56FE\u5C42\u540D", required: false }
1854
+ ]
1855
+ },
1856
+ {
1857
+ name: "color-convert",
1858
+ description: "\u989C\u8272\u8F6C\u6362\uFF1AACI/RGB/TrueColor/CSS\u4E92\u8F6C",
1859
+ arguments: [
1860
+ { name: "action", description: "\u64CD\u4F5C (aci2rgb/rgb2css/truecolor2rgb/rgb2truecolor)", required: true },
1861
+ { name: "value", description: "\u989C\u8272\u503C", required: true }
1862
+ ]
1863
+ },
1864
+ {
1865
+ name: "json-exchange",
1866
+ description: "JSON\u6570\u636E\u4EA4\u6362\uFF1A\u4ECE\u5B57\u7B26\u4E32\u89E3\u6790JSON alist\uFF0C\u4ECEalist\u751F\u6210JSON",
1867
+ arguments: [
1868
+ { name: "action", description: "\u64CD\u4F5C (encode/decode)", required: true },
1869
+ { name: "data", description: "JSON\u5B57\u7B26\u4E32\u6216Lisp alist\u6587\u672C", required: true }
1870
+ ]
1871
+ },
1872
+ {
1873
+ name: "excel-report",
1874
+ description: "Excel\u62A5\u8868\uFF1A\u5BFC\u51FACAD\u5B9E\u4F53\u6570\u636E\u5230Excel",
1875
+ arguments: [
1876
+ { name: "visible", description: "\u662F\u5426\u663E\u793AExcel\u7A97\u53E3 (true/false)", required: false },
1877
+ { name: "savePath", description: "\u4FDD\u5B58\u8DEF\u5F84\uFF0C\u4E0D\u586B\u5219\u4E0D\u4FDD\u5B58", required: false }
1878
+ ]
1879
+ },
1880
+ {
1881
+ name: "string-process",
1882
+ description: "\u5B57\u7B26\u4E32\u5904\u7406\uFF1A\u683C\u5F0F\u5316\u3001\u62C6\u5206\u3001\u66FF\u6362\u3001\u6B63\u5219",
1883
+ arguments: [
1884
+ { name: "action", description: "\u64CD\u4F5C (format/split/subst-all/regexp/trim/number2chinese)", required: true },
1885
+ { name: "input", description: "\u8F93\u5165\u5B57\u7B26\u4E32", required: true },
1886
+ { name: "arg1", description: "\u53C2\u65701\uFF08\u683C\u5F0F\u53C2\u6570/\u5206\u9694\u7B26/\u65E7\u4E32\uFF09", required: false },
1887
+ { name: "arg2", description: "\u53C2\u65702\uFF08\u65B0\u4E32/\u6B63\u5219\u6A21\u5F0F\uFF09", required: false }
1888
+ ]
1889
+ }
1890
+ ];
1596
1891
  function findExternalPrompt(name) {
1597
1892
  const ext = loadExternalPrompts();
1598
1893
  return ext.find((p) => p.name === name);
@@ -2209,825 +2504,766 @@ ${code}
2209
2504
  }]
2210
2505
  };
2211
2506
  }
2212
- var __dirname3, PROMPTS_DIR, externalPrompts, externalPromptsAt, EXTERNAL_PROMPTS_TTL, PROMPTS;
2213
- var init_prompt_handlers = __esm({
2214
- "src/handlers/prompt-handlers.js"() {
2215
- init_cad();
2216
- __dirname3 = path5.dirname(fileURLToPath3(import.meta.url));
2217
- PROMPTS_DIR = path5.join(__dirname3, "..", "prompts");
2218
- externalPrompts = null;
2219
- externalPromptsAt = 0;
2220
- EXTERNAL_PROMPTS_TTL = 3e4;
2221
- PROMPTS = [
2222
- {
2223
- name: "analyze-drawing",
2224
- description: "\u5206\u6790\u5F53\u524D\u56FE\u7EB8\u7EDF\u8BA1\u4FE1\u606F",
2225
- arguments: []
2226
- },
2227
- {
2228
- name: "batch-draw-lines",
2229
- description: "\u6279\u91CF\u7ED8\u5236\u7EBF\u6BB5",
2230
- arguments: [
2231
- { name: "count", description: "\u7EBF\u6BB5\u6570\u91CF", required: true },
2232
- { name: "length", description: "\u7EBF\u6BB5\u957F\u5EA6", required: false }
2233
- ]
2234
- },
2235
- {
2236
- name: "generate-dimension",
2237
- description: "\u751F\u6210\u5C3A\u5BF8\u6807\u6CE8",
2238
- arguments: [
2239
- { name: "style", description: "\u6807\u6CE8\u6837\u5F0F (horizontal/vertical/aligned)", required: false }
2240
- ]
2241
- },
2242
- {
2243
- name: "export-entities",
2244
- description: "\u5BFC\u51FA\u56FE\u7EB8\u5B9E\u4F53\u7EDF\u8BA1",
2245
- arguments: [
2246
- { name: "format", description: "\u5BFC\u51FA\u683C\u5F0F (json/list)", required: false }
2247
- ]
2248
- },
2249
- {
2250
- name: "manage-layers",
2251
- description: "\u56FE\u5C42\u7BA1\u7406\uFF1A\u521B\u5EFA\u3001\u51BB\u7ED3\u3001\u9501\u5B9A\u3001\u5220\u9664\u56FE\u5C42",
2252
- arguments: [
2253
- { name: "action", description: "\u64CD\u4F5C\u7C7B\u578B (list/freeze/lock/off/make/delete)", required: true },
2254
- { name: "layers", description: "\u56FE\u5C42\u540D\uFF0C\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694", required: false },
2255
- { name: "color", description: '\u56FE\u5C42\u989C\u8272 (ACI\u53F7\u6216RGB\u5982 "1,2,3")', required: false }
2256
- ]
2257
- },
2258
- {
2259
- name: "manage-blocks",
2260
- description: "\u5757\u64CD\u4F5C\uFF1A\u63D2\u5165\u5757\u3001\u5217\u51FA\u5757\u3001\u83B7\u53D6/\u8BBE\u7F6E\u5C5E\u6027",
2261
- arguments: [
2262
- { name: "action", description: "\u64CD\u4F5C\u7C7B\u578B (list/insert/attributes/dynprops)", required: true },
2263
- { name: "blockName", description: "\u5757\u540D", required: false },
2264
- { name: "insertPoint", description: '\u63D2\u5165\u70B9 "x,y"', required: false },
2265
- { name: "scale", description: "\u7F29\u653E\u6BD4\u4F8B", required: false }
2266
- ]
2267
- },
2268
- {
2269
- name: "curve-analysis",
2270
- description: "\u66F2\u7EBF\u5206\u6790\uFF1A\u4EA4\u70B9\u3001\u6700\u8FD1\u70B9\u3001\u5B50\u6BB5\u3001\u7F13\u51B2\u533A",
2271
- arguments: [
2272
- { name: "action", description: "\u64CD\u4F5C (inters/subsegments/closestpoint/join)", required: true },
2273
- { name: "distance", description: "\u8FDE\u63A5\u5BB9\u5DEE/\u504F\u79FB\u8DDD\u79BB", required: false }
2274
- ]
2275
- },
2276
- {
2277
- name: "geometry-calc",
2278
- description: "\u51E0\u4F55\u8BA1\u7B97\uFF1A\u51F8\u5305\u3001\u70B9\u5230\u7EBF\u8DDD\u79BB\u3001\u9762\u5185\u6D4B\u8BD5\u3001\u53D8\u6362",
2279
- arguments: [
2280
- { name: "action", description: "\u64CD\u4F5C (convexhull/dist-point-line/in-curve-p/transform/centroid)", required: true },
2281
- { name: "points", description: '\u70B9\u96C6 "x1,y1;x2,y2;..."', required: false }
2282
- ]
2283
- },
2284
- {
2285
- name: "text-process",
2286
- description: "\u6587\u672C\u5904\u7406\uFF1AMTEXT\u89E3\u6790\u3001Markdown\u8F6CMTEXT\u3001\u53BB\u683C\u5F0F",
2287
- arguments: [
2288
- { name: "action", description: "\u64CD\u4F5C (parse-mtext/remove-fmt/from-markdown/mtext2text)", required: true },
2289
- { name: "markdown", description: "Markdown\u6587\u672C\uFF08from-markdown\u64CD\u4F5C\u65F6\u7528\uFF09", required: false }
2290
- ]
2291
- },
2292
- {
2293
- name: "pickset-filter",
2294
- description: "\u9009\u62E9\u96C6\u64CD\u4F5C\uFF1A\u8FC7\u6EE4\u3001\u8F6C\u6362\u3001\u6279\u91CF\u64CD\u4F5C",
2295
- arguments: [
2296
- { name: "action", description: "\u64CD\u4F5C (by-layer/by-type/by-box/erase/zoom)", required: true },
2297
- { name: "dxfType", description: 'DXF\u7C7B\u578B "LINE, CIRCLE, LWPOLYLINE, INSERT, TEXT"', required: false },
2298
- { name: "layerName", description: "\u56FE\u5C42\u540D", required: false }
2299
- ]
2300
- },
2301
- {
2302
- name: "color-convert",
2303
- description: "\u989C\u8272\u8F6C\u6362\uFF1AACI/RGB/TrueColor/CSS\u4E92\u8F6C",
2304
- arguments: [
2305
- { name: "action", description: "\u64CD\u4F5C (aci2rgb/rgb2css/truecolor2rgb/rgb2truecolor)", required: true },
2306
- { name: "value", description: "\u989C\u8272\u503C", required: true }
2307
- ]
2308
- },
2309
- {
2310
- name: "json-exchange",
2311
- description: "JSON\u6570\u636E\u4EA4\u6362\uFF1A\u4ECE\u5B57\u7B26\u4E32\u89E3\u6790JSON alist\uFF0C\u4ECEalist\u751F\u6210JSON",
2312
- arguments: [
2313
- { name: "action", description: "\u64CD\u4F5C (encode/decode)", required: true },
2314
- { name: "data", description: "JSON\u5B57\u7B26\u4E32\u6216Lisp alist\u6587\u672C", required: true }
2315
- ]
2316
- },
2317
- {
2318
- name: "excel-report",
2319
- description: "Excel\u62A5\u8868\uFF1A\u5BFC\u51FACAD\u5B9E\u4F53\u6570\u636E\u5230Excel",
2320
- arguments: [
2321
- { name: "visible", description: "\u662F\u5426\u663E\u793AExcel\u7A97\u53E3 (true/false)", required: false },
2322
- { name: "savePath", description: "\u4FDD\u5B58\u8DEF\u5F84\uFF0C\u4E0D\u586B\u5219\u4E0D\u4FDD\u5B58", required: false }
2323
- ]
2324
- },
2325
- {
2326
- name: "string-process",
2327
- description: "\u5B57\u7B26\u4E32\u5904\u7406\uFF1A\u683C\u5F0F\u5316\u3001\u62C6\u5206\u3001\u66FF\u6362\u3001\u6B63\u5219",
2328
- arguments: [
2329
- { name: "action", description: "\u64CD\u4F5C (format/split/subst-all/regexp/trim/number2chinese)", required: true },
2330
- { name: "input", description: "\u8F93\u5165\u5B57\u7B26\u4E32", required: true },
2331
- { name: "arg1", description: "\u53C2\u65701\uFF08\u683C\u5F0F\u53C2\u6570/\u5206\u9694\u7B26/\u65E7\u4E32\uFF09", required: false },
2332
- { name: "arg2", description: "\u53C2\u65702\uFF08\u65B0\u4E32/\u6B63\u5219\u6A21\u5F0F\uFF09", required: false }
2333
- ]
2334
- }
2335
- ];
2507
+
2508
+ // src/mcp-handlers.js
2509
+ var SERVER_CAPABILITIES = {
2510
+ tools: {},
2511
+ resources: { subscribe: true, listChanged: true },
2512
+ prompts: {}
2513
+ };
2514
+ function createMcpServer(handlers = {}) {
2515
+ const { tools: tools2 = [], handleToolCall: handleToolCall2 = () => ({ content: [] }) } = handlers;
2516
+ const server = new Server(
2517
+ { name: SERVER_NAME, version: SERVER_VERSION },
2518
+ { capabilities: SERVER_CAPABILITIES }
2519
+ );
2520
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools2 }));
2521
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2522
+ const { name, arguments: args } = request.params;
2523
+ return await handleToolCall2(name, args || {});
2524
+ });
2525
+ server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
2526
+ const ctx = mcpContext.getStore();
2527
+ const sessionId = ctx?.sessionId;
2528
+ const subscribedUris = sessionId ? list(sessionId) : list();
2529
+ return {
2530
+ resources: await listResources(subscribedUris)
2531
+ };
2532
+ });
2533
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2534
+ const { uri } = request.params;
2535
+ try {
2536
+ const data = await readResource(uri);
2537
+ return {
2538
+ contents: [{
2539
+ uri,
2540
+ mimeType: "application/json",
2541
+ text: JSON.stringify(data)
2542
+ }]
2543
+ };
2544
+ } catch (e) {
2545
+ return {
2546
+ contents: [],
2547
+ isError: true,
2548
+ error: { code: -32603, message: e.message }
2549
+ };
2550
+ }
2551
+ });
2552
+ server.setRequestHandler(SubscribeRequestSchema, async (request) => {
2553
+ const { uri } = request.params;
2554
+ const ctx = mcpContext.getStore();
2555
+ subscribe(uri, ctx?.sessionId);
2556
+ return { success: true };
2557
+ });
2558
+ server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
2559
+ const { uri } = request.params;
2560
+ const ctx = mcpContext.getStore();
2561
+ unsubscribe(uri, ctx?.sessionId);
2562
+ return { success: true };
2563
+ });
2564
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
2565
+ prompts: await listPrompts()
2566
+ }));
2567
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
2568
+ const { name, arguments: args } = request.params;
2569
+ return await getPrompt(name, args || {});
2570
+ });
2571
+ return server;
2572
+ }
2573
+
2574
+ // src/handlers/cad-handlers.js
2575
+ async function connectCad(platform = null) {
2576
+ log(`connect_cad called${platform ? ` (target: ${platform})` : ""}, cad.connected before: ${cad.connected}`);
2577
+ try {
2578
+ log("calling cad.connect()...");
2579
+ const connected = await cad.connect(platform);
2580
+ log("cad.connect() returned: " + connected + ", cad.connected: " + cad.connected);
2581
+ if (connected) {
2582
+ return { content: [{ type: "text", text: `\u5DF2\u8FDE\u63A5\u5230 ${cad.getPlatform()} ${cad.getVersion()}` }] };
2583
+ }
2584
+ } catch (e) {
2585
+ log("connect_cad error: " + e.message);
2586
+ }
2587
+ return { content: [{ type: "text", text: "\u672A\u80FD\u8FDE\u63A5\u6216\u542F\u52A8 CAD\uFF0C\u8BF7\u786E\u4FDD CAD \u5DF2\u5B89\u88C5" }], isError: true };
2588
+ }
2589
+ async function evalLisp(code, withResult = false, encoding = null) {
2590
+ if (!cad.connected) {
2591
+ await cad.connect();
2592
+ }
2593
+ if (!cad.connected) {
2594
+ return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
2595
+ }
2596
+ const trimmed = (code || "").trim();
2597
+ if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
2598
+ try {
2599
+ if (withResult) {
2600
+ const result = await cad.sendCommandWithResult(trimmed, encoding);
2601
+ if (result !== null) {
2602
+ return { content: [{ type: "text", text: result }] };
2603
+ }
2604
+ return { content: [{ type: "text", text: "\u6267\u884C\u5931\u8D25\u6216\u65E0\u8FD4\u56DE\u503C" }], isError: true };
2605
+ } else {
2606
+ await cad.sendCommand(trimmed + "\n");
2607
+ return { content: [{ type: "text", text: `\u5DF2\u53D1\u9001\u547D\u4EE4` }] };
2608
+ }
2609
+ } catch (e) {
2610
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
2611
+ }
2612
+ }
2613
+ async function getCadInfo2() {
2614
+ if (!cad.connected) {
2615
+ await cad.connect();
2616
+ }
2617
+ if (!cad.connected) return { content: [{ type: "text", text: "CAD \u672A\u8FDE\u63A5" }] };
2618
+ const info = await cad.getInfo();
2619
+ return { content: [{ type: "text", text: info }] };
2620
+ }
2621
+ async function atCommand(command) {
2622
+ if (!cad.connected) return { content: [{ type: "text", text: "\u8BF7\u5148\u8FDE\u63A5 CAD" }], isError: true };
2623
+ const trimmed = (command || "").trim();
2624
+ if (!trimmed) return { content: [{ type: "text", text: "\u547D\u4EE4\u4E3A\u7A7A\uFF0C\u672A\u53D1\u9001" }] };
2625
+ await cad.sendCommand(trimmed + "\n");
2626
+ return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001\u547D\u4EE4" }] };
2627
+ }
2628
+ async function newDocument() {
2629
+ if (!cad.connected) {
2630
+ await cad.connect();
2631
+ }
2632
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
2633
+ const result = await cad.newDoc();
2634
+ if (result) {
2635
+ return { content: [{ type: "text", text: "\u5DF2\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863" }] };
2636
+ }
2637
+ return { content: [{ type: "text", text: "\u65B0\u5EFA\u6587\u6863\u5931\u8D25" }], isError: true };
2638
+ }
2639
+ async function bringToFront() {
2640
+ if (!cad.connected) {
2641
+ await cad.connect();
2642
+ }
2643
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
2644
+ const result = await cad.bringToFront();
2645
+ if (result) {
2646
+ return { content: [{ type: "text", text: "\u5DF2\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0" }] };
2647
+ }
2648
+ return { content: [{ type: "text", text: "\u5207\u6362\u524D\u53F0\u5931\u8D25" }], isError: true };
2649
+ }
2650
+ async function installAtlisp() {
2651
+ if (!cad.connected) {
2652
+ await cad.connect();
2653
+ }
2654
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
2655
+ 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))))`;
2656
+ await cad.sendCommand(installCode + "\n");
2657
+ return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001 @lisp \u5B89\u88C5\u4EE3\u7801\u5230 CAD" }] };
2658
+ }
2659
+ async function listFunctionsInCad() {
2660
+ if (!cad.connected) {
2661
+ await cad.connect();
2662
+ }
2663
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
2664
+ try {
2665
+ const result = await cad.sendCommandWithResult("(atoms-family 0)", null);
2666
+ if (result) return { content: [{ type: "text", text: result }] };
2667
+ return { content: [{ type: "text", text: "CAD \u51FD\u6570\u5217\u8868\u4E3A\u7A7A" }] };
2668
+ } catch (e) {
2669
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
2670
+ }
2671
+ }
2672
+ async function getSystemStatus() {
2673
+ const status = {
2674
+ mcp: { version: process.env.npm_package_version || "unknown" },
2675
+ worker: { alive: false },
2676
+ cad: { connected: false, platform: null, version: null, busy: false, hasDoc: false },
2677
+ lisp: { available: false, testResult: null, error: null },
2678
+ packages: null
2679
+ };
2680
+ try {
2681
+ status.worker.alive = await cad.ping();
2682
+ } catch {
2683
+ status.worker.alive = false;
2336
2684
  }
2337
- });
2338
-
2339
- // src/session-manager.js
2340
- var Session, SessionManager;
2341
- var init_session_manager = __esm({
2342
- "src/session-manager.js"() {
2343
- Session = class {
2344
- constructor(clientId) {
2345
- this.clientId = clientId;
2346
- this.subscriptions = /* @__PURE__ */ new Set();
2347
- this.createdAt = Date.now();
2348
- this.connected = false;
2349
- }
2350
- };
2351
- SessionManager = class {
2352
- #sessions = /* @__PURE__ */ new Map();
2353
- createSession(clientId) {
2354
- let session = this.#sessions.get(clientId);
2355
- if (!session) {
2356
- session = new Session(clientId);
2357
- this.#sessions.set(clientId, session);
2358
- }
2359
- return session;
2360
- }
2361
- getSession(clientId) {
2362
- return this.#sessions.get(clientId) || null;
2363
- }
2364
- hasSession(clientId) {
2365
- return this.#sessions.has(clientId);
2366
- }
2367
- removeSession(clientId) {
2368
- return this.#sessions.delete(clientId);
2369
- }
2370
- subscribe(clientId, uri) {
2371
- const session = this.#sessions.get(clientId);
2372
- if (session) {
2373
- session.subscriptions.add(uri);
2374
- return true;
2375
- }
2376
- return false;
2377
- }
2378
- unsubscribe(clientId, uri) {
2379
- const session = this.#sessions.get(clientId);
2380
- if (session) {
2381
- return session.subscriptions.delete(uri);
2382
- }
2383
- return false;
2384
- }
2385
- getSubscriptions(clientId) {
2386
- const session = this.#sessions.get(clientId);
2387
- return session ? Array.from(session.subscriptions) : [];
2388
- }
2389
- isSubscribed(clientId, uri) {
2390
- const session = this.#sessions.get(clientId);
2391
- return session ? session.subscriptions.has(uri) : false;
2392
- }
2393
- getAllSubscribedUris() {
2394
- const all = /* @__PURE__ */ new Set();
2395
- for (const session of this.#sessions.values()) {
2396
- for (const uri of session.subscriptions) {
2397
- all.add(uri);
2398
- }
2399
- }
2400
- return all;
2401
- }
2402
- list() {
2403
- return Array.from(this.#sessions.entries()).map(([id, s]) => ({
2404
- clientId: id,
2405
- subscriptions: Array.from(s.subscriptions),
2406
- createdAt: s.createdAt,
2407
- connected: s.connected
2408
- }));
2409
- }
2410
- count() {
2411
- return this.#sessions.size;
2685
+ status.cad.connected = cad.connected;
2686
+ if (cad.connected) {
2687
+ status.cad.platform = cad.getPlatform();
2688
+ status.cad.version = cad.getVersion();
2689
+ try {
2690
+ status.cad.busy = await cad.isBusy();
2691
+ } catch {
2692
+ }
2693
+ try {
2694
+ const doc = await cad.hasDoc();
2695
+ status.cad.hasDoc = doc.hasDoc;
2696
+ } catch {
2697
+ }
2698
+ try {
2699
+ const r = await cad.sendCommandWithResult("(+ 1 2)");
2700
+ if (r !== null && r !== void 0) {
2701
+ status.lisp.available = true;
2702
+ status.lisp.testResult = String(r).trim();
2412
2703
  }
2413
- clear() {
2414
- this.#sessions.clear();
2704
+ } catch (e) {
2705
+ status.lisp.error = e.message;
2706
+ }
2707
+ try {
2708
+ const pkgResult = await cad.sendCommandWithResult("(@::package-list-in-local)");
2709
+ if (pkgResult && pkgResult !== "nil") {
2710
+ status.packages = pkgResult;
2415
2711
  }
2416
- };
2712
+ } catch {
2713
+ }
2417
2714
  }
2418
- });
2419
-
2420
- // src/subscription-manager.js
2421
- function setNotify(fn) {
2422
- _notifyFn = fn;
2715
+ return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
2423
2716
  }
2424
- function subscribe(uri, sessionId) {
2425
- if (sessionId) {
2426
- return sessionManager.subscribe(sessionId, uri);
2717
+ async function initAtlisp() {
2718
+ if (!cad.connected) {
2719
+ await cad.connect();
2427
2720
  }
2428
- return true;
2429
- }
2430
- function unsubscribe(uri, sessionId) {
2431
- if (sessionId) {
2432
- return sessionManager.unsubscribe(sessionId, uri);
2721
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
2722
+ const code = `(if (null @::load-module)
2723
+ (progn
2724
+ (vl-load-com)
2725
+ (setq s strcat h"http"o(vlax-create-object (s"win"h".win"h"request.5.1"))
2726
+ v vlax-invoke e eval r read)(v o'open "get" (s h"://""atlisp.""cn/cloud"):vlax-true)
2727
+ (v o'send)
2728
+ (v o'WaitforResponse 1000)
2729
+ (e(r(vlax-get o'ResponseText)))))`;
2730
+ try {
2731
+ await cad.sendCommand(code + "\n");
2732
+ return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001 @lisp \u51FD\u6570\u5E93\u52A0\u8F7D\u4EE3\u7801\u5230 CAD" }] };
2733
+ } catch (e) {
2734
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
2433
2735
  }
2434
- return true;
2435
2736
  }
2436
- function list(sessionId) {
2437
- if (sessionId) {
2438
- return sessionManager.getSubscriptions(sessionId);
2737
+
2738
+ // src/handlers/package-handlers.js
2739
+ var PACKAGES_LIST_URL = process.env.PACKAGES_LIST_URL || "http://s3.atlisp.cn/packages-list?fmt=json";
2740
+ var cachedPackages = null;
2741
+ var cachedAt = null;
2742
+ var CACHE_TTL2 = 10 * 60 * 1e3;
2743
+ async function fetchPackages() {
2744
+ const now = Date.now();
2745
+ if (cachedPackages && cachedAt && now - cachedAt < CACHE_TTL2) {
2746
+ return cachedPackages;
2439
2747
  }
2440
- return Array.from(sessionManager.getAllSubscribedUris());
2748
+ try {
2749
+ const response = await fetch(PACKAGES_LIST_URL, { headers: { "User-Agent": "atlisp-mcp" } });
2750
+ if (response.ok) {
2751
+ const data = await response.json();
2752
+ cachedPackages = data;
2753
+ cachedAt = now;
2754
+ return data;
2755
+ }
2756
+ } catch (e) {
2757
+ }
2758
+ return cachedPackages || MOCK_PACKAGES;
2441
2759
  }
2442
- function isSubscribed(uri, sessionId) {
2443
- if (sessionId) {
2444
- return sessionManager.isSubscribed(sessionId, uri);
2760
+ function flattenPackages(data) {
2761
+ if (Array.isArray(data)) return data;
2762
+ if (data && Array.isArray(data.packages)) return data.packages;
2763
+ return [];
2764
+ }
2765
+ async function listPackages() {
2766
+ if (!cad.connected) await cad.connect();
2767
+ if (!cad.connected) {
2768
+ const data2 = await fetchPackages();
2769
+ const items2 = flattenPackages(data2);
2770
+ const text2 = items2.length ? items2.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n") : "\u65E0\u53EF\u7528\u5305";
2771
+ return { content: [{ type: "text", text: text2 }] };
2445
2772
  }
2446
- return sessionManager.getAllSubscribedUris().has(uri);
2773
+ const result = await cad.sendCommandWithResult("(@::package-list-in-local)");
2774
+ if (result && result !== "nil") return { content: [{ type: "text", text: result }] };
2775
+ const data = await fetchPackages();
2776
+ const items = flattenPackages(data);
2777
+ const text = items.length ? `\u4ECE\u6CE8\u518C\u8868\u83B7\u53D6\u7684 @lisp \u5305:
2778
+ ${items.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n")}` : MOCK_PACKAGES.map((p) => `${p.name} v${p.version} - ${p.description}`).join("\n");
2779
+ return { content: [{ type: "text", text: `\u5DF2\u5B89\u88C5\u7684 @lisp \u5305:
2780
+ ${text}` }] };
2447
2781
  }
2448
- function notify(uri, data) {
2449
- if (_notifyFn) {
2450
- _notifyFn(uri, data);
2782
+ async function searchPackages(query) {
2783
+ const data = await fetchPackages();
2784
+ if (!data) {
2785
+ return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6\u5305\u5217\u8868" }], isError: true };
2786
+ }
2787
+ const items = flattenPackages(data);
2788
+ const results = [];
2789
+ if (query) {
2790
+ const q = query.toLowerCase();
2791
+ for (const p of items) {
2792
+ const name = (p.name || "").toLowerCase();
2793
+ const desc = (p.description || "").toLowerCase();
2794
+ if (name.includes(q) || desc.includes(q)) results.push(p);
2795
+ }
2796
+ } else {
2797
+ for (const p of items) results.push(p);
2798
+ }
2799
+ if (results.length === 0) {
2800
+ return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5305\u542B "${query}" \u7684\u5305` }] };
2451
2801
  }
2802
+ return { content: [{ type: "text", text: `\u641C\u7D22\u7ED3\u679C (${results.length}):
2803
+ ${results.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n")}` }] };
2452
2804
  }
2453
- var sessionManager, _notifyFn;
2454
- var init_subscription_manager = __esm({
2455
- "src/subscription-manager.js"() {
2456
- init_session_manager();
2457
- sessionManager = new SessionManager();
2458
- _notifyFn = null;
2805
+ async function installPackage(packageName) {
2806
+ const data = await fetchPackages();
2807
+ const items = flattenPackages(data);
2808
+ const pkg = items.find((p) => p.name === packageName);
2809
+ if (!pkg) {
2810
+ return { content: [{ type: "text", text: `\u9519\u8BEF: \u5305 "${packageName}" \u4E0D\u5B58\u5728` }], isError: true };
2459
2811
  }
2460
- });
2461
-
2462
- // src/sse-session.js
2463
- import { randomUUID as randomUUID2 } from "crypto";
2464
- var DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_RETRY_INTERVAL, MAX_SENT_MESSAGES, SSE_EVENTS, SseSession;
2465
- var init_sse_session = __esm({
2466
- "src/sse-session.js"() {
2467
- DEFAULT_HEARTBEAT_INTERVAL = 3e4;
2468
- DEFAULT_RETRY_INTERVAL = 3e4;
2469
- MAX_SENT_MESSAGES = 1e3;
2470
- SSE_EVENTS = {
2471
- MESSAGE: "message",
2472
- ENDPOINT: "endpoint",
2473
- PING: "ping",
2474
- ERROR: "error",
2475
- PROGRESS: "progress",
2476
- CAPABILITIES: "capabilities"
2477
- };
2478
- SseSession = class {
2479
- #res;
2480
- #clientId;
2481
- #active = true;
2482
- #heartbeatTimer = null;
2483
- #lastEventId = 0;
2484
- #heartbeatInterval;
2485
- #retryInterval;
2486
- #sessionData = {};
2487
- #sentMessages = [];
2488
- #backpressureBuffer = [];
2489
- #drainHandler = null;
2490
- constructor(res, options = {}) {
2491
- this.#res = res;
2492
- this.#clientId = options.clientId || randomUUID2();
2493
- this.#heartbeatInterval = options.heartbeatInterval || DEFAULT_HEARTBEAT_INTERVAL;
2494
- this.#retryInterval = options.retryInterval || DEFAULT_RETRY_INTERVAL;
2495
- this.#sessionData = options.sessionData || {};
2496
- if (options.resumeFromId && options.resumeFromId > 0) {
2497
- this.#lastEventId = options.resumeFromId;
2498
- }
2499
- this._setupSseHeaders();
2500
- this._sendRetry();
2501
- this._setupDrain();
2502
- }
2503
- get clientId() {
2504
- return this.#clientId;
2505
- }
2506
- get isActive() {
2507
- return this.#active;
2508
- }
2509
- get lastEventId() {
2510
- return this.#lastEventId;
2511
- }
2512
- get sessionData() {
2513
- return this.#sessionData;
2514
- }
2515
- get sentMessages() {
2516
- return [...this.#sentMessages];
2517
- }
2518
- getMessagesSince(eventId) {
2519
- return this.#sentMessages.filter((m) => m.id > eventId);
2520
- }
2521
- setSessionData(key, value) {
2522
- this.#sessionData[key] = value;
2523
- }
2524
- getSessionData(key) {
2525
- return this.#sessionData[key];
2526
- }
2527
- get retryInterval() {
2528
- return this.#retryInterval;
2529
- }
2530
- setRetryInterval(ms) {
2531
- this.#retryInterval = ms;
2532
- }
2533
- _sendRetry() {
2534
- this._write(`retry: ${this.#retryInterval}
2535
-
2812
+ if (cad.connected) {
2813
+ await cad.sendCommand(`(@::load-module '${packageName})
2536
2814
  `);
2537
- }
2538
- _setupSseHeaders() {
2539
- this.#res.setHeader("Content-Type", "text/event-stream");
2540
- this.#res.setHeader("Cache-Control", "no-cache");
2541
- this.#res.setHeader("Connection", "keep-alive");
2542
- this.#res.setHeader("Transfer-Encoding", "chunked");
2543
- this.#res.setHeader("X-Accel-Buffering", "no");
2544
- if (typeof this.#res.flush !== "function") {
2545
- this.#res.flush = () => {
2546
- };
2547
- }
2548
- }
2549
- _formatEvent(event, data, id = null) {
2550
- const lines = [];
2551
- if (id !== null) {
2552
- lines.push(`id: ${id}`);
2553
- }
2554
- lines.push(`event: ${event}`);
2555
- if (data !== null && data !== void 0) {
2556
- const jsonStr = JSON.stringify(data);
2557
- lines.push(`data: ${jsonStr}`);
2558
- } else {
2559
- lines.push("data:");
2560
- }
2561
- return lines.join("\n") + "\n\n";
2562
- }
2563
- _setupDrain() {
2564
- if (typeof this.#res.on !== "function") return;
2565
- this.#drainHandler = () => {
2566
- if (!this.#active) return;
2567
- if (this.#backpressureBuffer.length > 0) {
2568
- this._drainBuffer();
2569
- }
2570
- };
2571
- this.#res.on("drain", this.#drainHandler);
2572
- }
2573
- _drainBuffer() {
2574
- const batch = this.#backpressureBuffer.splice(0, 50);
2575
- for (const item of batch) {
2576
- const ok = this.#res.write(item);
2577
- if (!ok) {
2578
- this.#backpressureBuffer.unshift(...batch.slice(batch.indexOf(item)));
2579
- return;
2580
- }
2581
- }
2582
- this._flush();
2583
- if (this.#backpressureBuffer.length > 0) {
2584
- this._drainBuffer();
2585
- }
2586
- }
2587
- _write(content) {
2588
- if (!this.#active) return false;
2589
- try {
2590
- if (this.#backpressureBuffer.length > 0) {
2591
- this.#backpressureBuffer.push(content);
2592
- return true;
2593
- }
2594
- const result = this.#res.write(content);
2595
- if (typeof this.#res.flush === "function") {
2596
- this.#res.flush();
2597
- }
2598
- if (result === false) {
2599
- this.#backpressureBuffer.push(content);
2600
- return true;
2601
- }
2602
- return true;
2603
- } catch (e) {
2604
- this.#active = false;
2605
- this.close();
2606
- return false;
2607
- }
2608
- }
2609
- _flush() {
2610
- try {
2611
- if (typeof this.#res.flush === "function") {
2612
- this.#res.flush();
2613
- }
2614
- } catch (e) {
2615
- }
2616
- }
2617
- flushBackpressure() {
2618
- if (this.#backpressureBuffer.length > 0) {
2619
- this._drainBuffer();
2620
- }
2621
- }
2622
- sendEvent(event, data, id = null) {
2623
- this.#lastEventId++;
2624
- const eventId = id !== null ? id : this.#lastEventId;
2625
- const formatted = this._formatEvent(event, data, eventId);
2626
- this.#sentMessages.push({ event, data, id: eventId, timestamp: Date.now() });
2627
- if (this.#sentMessages.length > MAX_SENT_MESSAGES) {
2628
- this.#sentMessages.splice(0, this.#sentMessages.length - MAX_SENT_MESSAGES);
2629
- }
2630
- this._write(formatted);
2631
- this._flush();
2632
- return true;
2633
- }
2634
- sendMessage(data) {
2635
- return this.sendEvent(SSE_EVENTS.MESSAGE, data);
2636
- }
2637
- sendEndpoint(path8) {
2638
- return this.sendEvent(SSE_EVENTS.ENDPOINT, path8);
2639
- }
2640
- sendError(code, message, id = null) {
2641
- return this.sendEvent(SSE_EVENTS.ERROR, { code, message }, id);
2642
- }
2643
- sendPing() {
2644
- return this.sendEvent(SSE_EVENTS.PING, { timestamp: Date.now() });
2645
- }
2646
- sendProgress(current, total, message = "") {
2647
- return this.sendEvent(SSE_EVENTS.PROGRESS, { current, total, message });
2648
- }
2649
- sendCapabilities(capabilities) {
2650
- return this.sendEvent(SSE_EVENTS.CAPABILITIES, capabilities);
2651
- }
2652
- sendResponse(jsonrpcResponse, id = null) {
2653
- const eventId = id !== null ? id : this.#lastEventId;
2654
- return this.sendEvent(SSE_EVENTS.MESSAGE, jsonrpcResponse, eventId);
2655
- }
2656
- startHeartbeat(interval = null) {
2657
- this.stopHeartbeat();
2658
- const ms = interval || this.#heartbeatInterval;
2659
- this.#heartbeatTimer = setInterval(() => {
2660
- if (!this.#active) {
2661
- this.stopHeartbeat();
2662
- return;
2663
- }
2664
- if (!this.sendPing()) {
2665
- this.stopHeartbeat();
2666
- this.close();
2667
- }
2668
- }, ms);
2669
- }
2670
- stopHeartbeat() {
2671
- if (this.#heartbeatTimer) {
2672
- clearInterval(this.#heartbeatTimer);
2673
- this.#heartbeatTimer = null;
2674
- }
2675
- }
2676
- close() {
2677
- this.#active = false;
2678
- this.stopHeartbeat();
2679
- if (this.#drainHandler) {
2680
- this.#res.removeListener("drain", this.#drainHandler);
2681
- this.#drainHandler = null;
2682
- }
2683
- this.#backpressureBuffer = [];
2684
- try {
2685
- this.#res.end();
2686
- } catch (e) {
2687
- }
2688
- }
2689
- };
2690
2815
  }
2691
- });
2816
+ return { content: [{ type: "text", text: `\u5305 "${packageName}" \u5B89\u88C5\u547D\u4EE4\u5DF2\u53D1\u9001\uFF01` }] };
2817
+ }
2692
2818
 
2693
- // src/sse-session-manager.js
2819
+ // src/handlers/function-handlers.js
2694
2820
  import fs5 from "fs";
2695
2821
  import path6 from "path";
2696
2822
  import os3 from "os";
2697
- function getSessionStorePath(clientId) {
2698
- return path6.join(SESSION_STORE_DIR, `${clientId}.json`);
2823
+ var FUNCTIONS_DIR = process.env.FUNCTIONS_DIR || "";
2824
+ var FUNCTIONS_URL = process.env.FUNCTIONS_URL || "http://s3.atlisp.cn/json/functions.json";
2825
+ var CACHE_DIR = path6.join(os3.homedir(), ".atlisp", "cache");
2826
+ var FUNCLIB_CACHE_FILE = path6.join(CACHE_DIR, "functions.json");
2827
+ var FUNCLIB_CACHE_TTL = 24 * 60 * 60 * 1e3;
2828
+ function readCacheFromDisk() {
2829
+ try {
2830
+ const stat = fs5.statSync(FUNCLIB_CACHE_FILE);
2831
+ if (Date.now() - stat.mtimeMs < FUNCLIB_CACHE_TTL) {
2832
+ const raw = fs5.readFileSync(FUNCLIB_CACHE_FILE, "utf-8");
2833
+ return JSON.parse(raw);
2834
+ }
2835
+ } catch {
2836
+ }
2837
+ return null;
2699
2838
  }
2700
- function persistSessionData(clientId, sessionData) {
2839
+ function writeCacheToDisk(data) {
2701
2840
  try {
2702
- fs5.mkdirSync(SESSION_STORE_DIR, { recursive: true });
2703
- const store = {
2704
- clientId,
2705
- sessionData,
2706
- savedAt: Date.now()
2707
- };
2708
- fs5.writeFileSync(getSessionStorePath(clientId), JSON.stringify(store), "utf-8");
2841
+ fs5.mkdirSync(CACHE_DIR, { recursive: true });
2842
+ fs5.writeFileSync(FUNCLIB_CACHE_FILE, JSON.stringify(data), "utf-8");
2709
2843
  } catch (e) {
2710
- console.error("Failed to persist session:", e.message);
2844
+ log(`function-handlers writeCache error: ${e.message}`);
2711
2845
  }
2712
2846
  }
2713
- function loadPersistedSessionData(clientId) {
2847
+ async function fetchFunctions() {
2848
+ const diskCache = readCacheFromDisk();
2849
+ if (diskCache) return diskCache;
2714
2850
  try {
2715
- const filePath = getSessionStorePath(clientId);
2716
- if (!fs5.existsSync(filePath)) return null;
2717
- const raw = fs5.readFileSync(filePath, "utf-8");
2718
- const store = JSON.parse(raw);
2719
- if (Date.now() - store.savedAt > SESSION_STORE_TTL) {
2720
- fs5.unlinkSync(filePath);
2721
- return null;
2851
+ const response = await fetch(FUNCTIONS_URL, { headers: { "User-Agent": "atlisp-mcp" } });
2852
+ if (response.ok) {
2853
+ const data = await response.json();
2854
+ writeCacheToDisk(data);
2855
+ return data;
2722
2856
  }
2723
- return store.sessionData || {};
2724
2857
  } catch (e) {
2725
- return null;
2858
+ log(`function-handlers fetch error: ${e.message}`);
2726
2859
  }
2860
+ return diskCache;
2727
2861
  }
2728
- var SESSION_STORE_DIR, SESSION_STORE_TTL, SseSessionManager;
2729
- var init_sse_session_manager = __esm({
2730
- "src/sse-session-manager.js"() {
2731
- init_sse_session();
2732
- SESSION_STORE_DIR = path6.join(os3.homedir(), ".atlisp", "sessions");
2733
- SESSION_STORE_TTL = 24 * 60 * 60 * 1e3;
2734
- SseSessionManager = class {
2735
- #sessions = /* @__PURE__ */ new Map();
2736
- #eventHandlers = /* @__PURE__ */ new Map();
2737
- constructor() {
2738
- this.#sessions = /* @__PURE__ */ new Map();
2739
- this.#eventHandlers = /* @__PURE__ */ new Map();
2740
- }
2741
- add(clientId, session) {
2742
- if (!(session instanceof SseSession)) {
2743
- throw new Error("session must be an SseSession instance");
2744
- }
2745
- const persisted = loadPersistedSessionData(clientId);
2746
- if (persisted) {
2747
- for (const [key, value] of Object.entries(persisted)) {
2748
- session.setSessionData(key, value);
2749
- }
2750
- }
2751
- this.#sessions.set(clientId, session);
2752
- session.startHeartbeat();
2753
- }
2754
- remove(clientId) {
2755
- const session = this.#sessions.get(clientId);
2756
- if (session) {
2757
- persistSessionData(clientId, session.sessionData);
2758
- session.close();
2759
- this.#sessions.delete(clientId);
2760
- return true;
2761
- }
2762
- return false;
2763
- }
2764
- get(clientId) {
2765
- return this.#sessions.get(clientId) || null;
2766
- }
2767
- has(clientId) {
2768
- return this.#sessions.has(clientId);
2769
- }
2770
- list() {
2771
- return Array.from(this.#sessions.keys()).map((id) => ({
2772
- clientId: id,
2773
- isActive: this.#sessions.get(id)?.isActive ?? false
2774
- }));
2775
- }
2776
- listActive() {
2777
- return this.list().filter((s) => s.isActive);
2778
- }
2779
- count() {
2780
- return this.#sessions.size;
2781
- }
2782
- sendToClient(clientId, event, data, id = null) {
2783
- const session = this.#sessions.get(clientId);
2784
- if (!session || !session.isActive) {
2785
- return false;
2786
- }
2787
- return session.sendEvent(event, data, id);
2788
- }
2789
- sendMessageToClient(clientId, data) {
2790
- return this.sendToClient(clientId, SSE_EVENTS.MESSAGE, data);
2791
- }
2792
- sendErrorToClient(clientId, code, message, id = null) {
2793
- return this.sendToClient(clientId, SSE_EVENTS.ERROR, { code, message }, id);
2794
- }
2795
- sendProgressToClient(clientId, current, total, message = "") {
2796
- return this.sendToClient(clientId, SSE_EVENTS.PROGRESS, { current, total, message });
2797
- }
2798
- broadcast(event, data) {
2799
- let successCount = 0;
2800
- for (const [clientId, session] of this.#sessions) {
2801
- if (session.isActive && session.sendEvent(event, data)) {
2802
- successCount++;
2803
- }
2804
- }
2805
- return successCount;
2806
- }
2807
- broadcastMessage(data) {
2808
- return this.broadcast(SSE_EVENTS.MESSAGE, data);
2809
- }
2810
- broadcastError(code, message) {
2811
- return this.broadcast(SSE_EVENTS.ERROR, { code, message });
2812
- }
2813
- on(event, handler) {
2814
- if (!this.#eventHandlers.has(event)) {
2815
- this.#eventHandlers.set(event, /* @__PURE__ */ new Set());
2862
+ async function getFunctionUsage(funcName, packageName) {
2863
+ try {
2864
+ let results = [];
2865
+ const data = await fetchFunctions();
2866
+ if (data) {
2867
+ const funcs = Object.values(data.all_functions || {});
2868
+ const func = funcs.find((f2) => f2.name === funcName || f2.name === `${packageName}:${funcName}`);
2869
+ if (func) results.push(func);
2870
+ }
2871
+ if (results.length === 0 && FUNCTIONS_DIR) {
2872
+ try {
2873
+ const files = fs5.readdirSync(FUNCTIONS_DIR).filter((f2) => f2.endsWith(".json"));
2874
+ for (const file of files) {
2875
+ const raw = fs5.readFileSync(path6.join(FUNCTIONS_DIR, file), "utf-8");
2876
+ const data2 = JSON.parse(raw);
2877
+ const func = data2.functions?.find((f2) => f2.name === funcName);
2878
+ if (func) results.push(func);
2816
2879
  }
2817
- this.#eventHandlers.get(event).add(handler);
2880
+ } catch {
2818
2881
  }
2819
- off(event, handler) {
2820
- const handlers = this.#eventHandlers.get(event);
2821
- if (handlers) {
2822
- handlers.delete(handler);
2823
- }
2882
+ }
2883
+ if (results.length === 0) {
2884
+ return { content: [{ type: "text", text: `\u672A\u627E\u5230\u51FD\u6570: ${funcName}` }], isError: true };
2885
+ }
2886
+ const f = results[0];
2887
+ const text = `\u51FD\u6570: ${f.name}
2888
+ \u63CF\u8FF0: ${f.description || "-"}
2889
+ \u53C2\u6570: ${f.params?.join(", ") || "-"}
2890
+ \u8FD4\u56DE\u503C: ${f.return || "-"}
2891
+ \u793A\u4F8B: ${f.usage || "-"}`;
2892
+ return { content: [{ type: "text", text }] };
2893
+ } catch (e) {
2894
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
2895
+ }
2896
+ }
2897
+ async function listSymbols(packageName) {
2898
+ try {
2899
+ let allFuncs = [];
2900
+ const data = await fetchFunctions();
2901
+ if (data) {
2902
+ const funcs = Object.values(data.all_functions || {});
2903
+ if (packageName) {
2904
+ allFuncs = funcs.filter((f) => f.name.startsWith(`${packageName}:`));
2905
+ } else {
2906
+ allFuncs = funcs.map((f) => `${f.name}: ${f.description || "-"}`);
2824
2907
  }
2825
- emit(event, data) {
2826
- const handlers = this.#eventHandlers.get(event);
2827
- if (handlers) {
2828
- for (const handler of handlers) {
2829
- try {
2830
- handler(data);
2831
- } catch (e) {
2908
+ }
2909
+ if (allFuncs.length === 0 && FUNCTIONS_DIR) {
2910
+ try {
2911
+ const files = fs5.readdirSync(FUNCTIONS_DIR).filter((f) => f.endsWith(".json"));
2912
+ for (const file of files) {
2913
+ const raw = fs5.readFileSync(path6.join(FUNCTIONS_DIR, file), "utf-8");
2914
+ const data2 = JSON.parse(raw);
2915
+ const funcs = Object.values(data2.all_functions || {});
2916
+ if (packageName) {
2917
+ allFuncs = funcs.filter((f) => f.name.startsWith(`${packageName}:`));
2918
+ } else {
2919
+ for (const f of funcs) {
2920
+ allFuncs.push(`${f.name}: ${f.description || "-"}`);
2832
2921
  }
2833
2922
  }
2834
2923
  }
2924
+ } catch (err) {
2925
+ log(`listSymbols readDir error: ${err.message}`);
2835
2926
  }
2836
- cleanupInactive() {
2837
- for (const [clientId, session] of this.#sessions) {
2838
- if (!session.isActive) {
2839
- session.close();
2840
- this.#sessions.delete(clientId);
2841
- }
2842
- }
2843
- }
2844
- closeAll() {
2845
- for (const [clientId, session] of this.#sessions) {
2846
- persistSessionData(clientId, session.sessionData);
2847
- session.close();
2848
- }
2849
- this.#sessions.clear();
2850
- }
2851
- };
2927
+ }
2928
+ if (allFuncs.length === 0) {
2929
+ return await listFunctionsInCad();
2930
+ }
2931
+ if (packageName) {
2932
+ return { content: [{ type: "text", text: `${packageName} \u5305\u51FD\u6570 (${allFuncs.length}):
2933
+ ${allFuncs.map((f) => f.name).join("\n")}` }] };
2934
+ }
2935
+ return { content: [{ type: "text", text: allFuncs.join("\n") }] };
2936
+ } catch (e) {
2937
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
2852
2938
  }
2853
- });
2939
+ }
2854
2940
 
2855
- // src/session-transport.js
2856
- import { randomUUID as randomUUID3 } from "crypto";
2857
- var SESSION_TIMEOUT, SessionTransport;
2858
- var init_session_transport = __esm({
2859
- "src/session-transport.js"() {
2860
- SESSION_TIMEOUT = 30 * 60 * 1e3;
2861
- SessionTransport = class {
2862
- constructor(sessionId) {
2863
- this.sessionId = sessionId || randomUUID3();
2864
- this.onclose = null;
2865
- this.onerror = null;
2866
- this.onmessage = null;
2867
- this._pendingRequest = null;
2868
- this._started = false;
2869
- this._closed = false;
2870
- this._cleanupTimer = null;
2871
- }
2872
- async start() {
2873
- if (this._started) {
2874
- throw new Error("Transport already started");
2875
- }
2876
- this._started = true;
2877
- }
2878
- async send(message) {
2879
- if (this._closed) return;
2880
- if (this._pendingRequest) {
2881
- const { resolve, reject, timeout } = this._pendingRequest;
2882
- clearTimeout(timeout);
2883
- this._pendingRequest = null;
2884
- resolve(message);
2885
- }
2886
- }
2887
- async close() {
2888
- if (this._closed) return;
2889
- this._closed = true;
2890
- if (this._pendingRequest) {
2891
- const { reject, timeout } = this._pendingRequest;
2892
- clearTimeout(timeout);
2893
- this._pendingRequest = null;
2894
- reject(new Error("Transport closed"));
2895
- }
2896
- this._clearCleanupTimer();
2897
- this.onclose?.();
2898
- }
2899
- async handleRequest(message, timeoutMs = 6e4) {
2900
- if (this._closed) {
2901
- throw new Error("Transport closed");
2902
- }
2903
- if (!message || !message.method) {
2904
- throw new Error("Invalid message");
2905
- }
2906
- if (!message.id) {
2907
- this.onmessage?.(message);
2908
- return null;
2909
- }
2910
- return new Promise((resolve, reject) => {
2911
- const timeout = setTimeout(() => {
2912
- if (this._pendingRequest?.resolve === resolve) {
2913
- this._pendingRequest = null;
2914
- reject(new Error("Request timeout"));
2915
- }
2916
- }, timeoutMs);
2917
- this._pendingRequest = { resolve, reject, timeout };
2918
- this.onmessage?.(message);
2919
- });
2920
- }
2921
- resetCleanupTimer() {
2922
- this._clearCleanupTimer();
2923
- this._cleanupTimer = setTimeout(() => {
2924
- this.close();
2925
- }, SESSION_TIMEOUT);
2926
- }
2927
- _clearCleanupTimer() {
2928
- if (this._cleanupTimer) {
2929
- clearTimeout(this._cleanupTimer);
2930
- this._cleanupTimer = null;
2931
- }
2932
- }
2933
- };
2941
+ // src/function-lib.js
2942
+ import path7 from "path";
2943
+ import fs6 from "fs";
2944
+ import os4 from "os";
2945
+ var CACHE_DIR2 = path7.join(os4.homedir(), ".atlisp", "cache");
2946
+ var FUNCLIB_CACHE_FILE2 = path7.join(CACHE_DIR2, "functions.json");
2947
+ var CACHE_TTL3 = config_default.functionLibCacheTtl || 5 * 60 * 1e3;
2948
+ var FUNCTION_LIB_URL = config_default.functionLibUrl || "http://s3.atlisp.cn/json/functions.json";
2949
+ var cachedFunctionLib = null;
2950
+ var cachedAt2 = null;
2951
+ async function readCacheFromDisk2() {
2952
+ try {
2953
+ const stat = fs6.statSync(FUNCLIB_CACHE_FILE2);
2954
+ if (Date.now() - stat.mtimeMs < CACHE_TTL3) {
2955
+ const raw = fs6.readFileSync(FUNCLIB_CACHE_FILE2, "utf-8");
2956
+ return JSON.parse(raw);
2957
+ }
2958
+ } catch {
2934
2959
  }
2935
- });
2960
+ return null;
2961
+ }
2962
+ function writeCacheToDisk2(data) {
2963
+ try {
2964
+ fs6.mkdirSync(CACHE_DIR2, { recursive: true });
2965
+ fs6.writeFileSync(FUNCLIB_CACHE_FILE2, JSON.stringify(data), "utf-8");
2966
+ } catch (e) {
2967
+ error(`writeCacheToDisk error: ${e.message}`);
2968
+ }
2969
+ }
2970
+ async function loadAtlibFunctionLib() {
2971
+ const now = Date.now();
2972
+ if (cachedFunctionLib && cachedAt2 && now - cachedAt2 < CACHE_TTL3) {
2973
+ return cachedFunctionLib;
2974
+ }
2975
+ const diskCache = await readCacheFromDisk2();
2976
+ if (diskCache) {
2977
+ cachedFunctionLib = diskCache;
2978
+ cachedAt2 = now;
2979
+ return diskCache;
2980
+ }
2981
+ try {
2982
+ const response = await fetch(FUNCTION_LIB_URL, { headers: { "User-Agent": "atlisp-mcp" } });
2983
+ if (response.ok) {
2984
+ const data = await response.json();
2985
+ cachedFunctionLib = data;
2986
+ cachedAt2 = now;
2987
+ writeCacheToDisk2(data);
2988
+ return data;
2989
+ }
2990
+ } catch (e) {
2991
+ error(`loadAtlibFunctionLib error: ${e.message}`);
2992
+ }
2993
+ return cachedFunctionLib;
2994
+ }
2936
2995
 
2937
- // src/mcp-handlers.js
2938
- import {
2939
- ListToolsRequestSchema,
2940
- CallToolRequestSchema,
2941
- ListResourcesRequestSchema,
2942
- ReadResourceRequestSchema,
2943
- SubscribeRequestSchema,
2944
- UnsubscribeRequestSchema,
2945
- ListPromptsRequestSchema,
2946
- GetPromptRequestSchema
2947
- } from "@modelcontextprotocol/sdk/types.js";
2948
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2949
- function createMcpServer(handlers = {}) {
2950
- const { tools: tools2 = [], handleToolCall: handleToolCall2 = () => ({ content: [] }) } = handlers;
2951
- const server = new Server(
2952
- { name: SERVER_NAME, version: SERVER_VERSION },
2953
- { capabilities: SERVER_CAPABILITIES }
2954
- );
2955
- server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools2 }));
2956
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
2957
- const { name, arguments: args } = request.params;
2958
- return await handleToolCall2(name, args || {});
2959
- });
2960
- server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
2961
- const ctx = mcpContext.getStore();
2962
- const sessionId = ctx?.sessionId;
2963
- const subscribedUris = sessionId ? list(sessionId) : list();
2964
- return {
2965
- resources: await listResources(subscribedUris)
2966
- };
2967
- });
2968
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2969
- const { uri } = request.params;
2970
- try {
2971
- const data = await readResource(uri);
2972
- return {
2973
- contents: [{
2974
- uri,
2975
- mimeType: "application/json",
2976
- text: JSON.stringify(data)
2977
- }]
2978
- };
2979
- } catch (e) {
2980
- return {
2981
- contents: [],
2982
- isError: true,
2983
- error: { code: -32603, message: e.message }
2984
- };
2996
+ // src/mcp-tools.js
2997
+ var toolInputSchema = {
2998
+ connect_cad: { platform: "string?" },
2999
+ eval_lisp: { code: "string" },
3000
+ eval_lisp_with_result: { code: "string", encoding: "string?" },
3001
+ get_cad_info: {},
3002
+ list_packages: {},
3003
+ search_packages: { query: "string" },
3004
+ install_package: { packageName: "string" },
3005
+ get_platform_info: {},
3006
+ at_command: { command: "string" },
3007
+ new_document: {},
3008
+ bring_to_front: {},
3009
+ install_atlisp: {},
3010
+ init_atlisp: {},
3011
+ get_function_usage: { name: "string", package: "string?" },
3012
+ list_symbols: { package: "string?" },
3013
+ get_system_status: {},
3014
+ import_funlib: { format: "string?" },
3015
+ list_prompts: {},
3016
+ get_prompt: { name: "string", args: "object?" }
3017
+ };
3018
+ function validateToolArgs(name, args) {
3019
+ const schema = toolInputSchema[name];
3020
+ if (!schema) {
3021
+ throw new Error(`Unknown tool: ${name}`);
3022
+ }
3023
+ for (const [key, type] of Object.entries(schema)) {
3024
+ const isOptional = type.endsWith("?");
3025
+ const baseType = type.replace("?", "");
3026
+ if (!args[key]) {
3027
+ if (isOptional) continue;
3028
+ throw new Error(`Missing required argument: ${key}`);
3029
+ }
3030
+ const actualType = typeof args[key];
3031
+ if (baseType === "string" && actualType !== "string") {
3032
+ throw new Error(`Invalid type for argument ${key}: expected string, got ${actualType}`);
3033
+ }
3034
+ if (baseType === "object" && (actualType !== "object" || actualType === null)) {
3035
+ throw new Error(`Invalid type for argument ${key}: expected object, got ${actualType}`);
3036
+ }
3037
+ if (baseType === "number" && actualType !== "number") {
3038
+ throw new Error(`Invalid type for argument ${key}: expected number, got ${actualType}`);
2985
3039
  }
2986
- });
2987
- server.setRequestHandler(SubscribeRequestSchema, async (request) => {
2988
- const { uri } = request.params;
2989
- const ctx = mcpContext.getStore();
2990
- subscribe(uri, ctx?.sessionId);
2991
- return { success: true };
2992
- });
2993
- server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
2994
- const { uri } = request.params;
2995
- const ctx = mcpContext.getStore();
2996
- unsubscribe(uri, ctx?.sessionId);
2997
- return { success: true };
2998
- });
2999
- server.setRequestHandler(ListPromptsRequestSchema, async () => ({
3000
- prompts: await listPrompts()
3001
- }));
3002
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
3003
- const { name, arguments: args } = request.params;
3004
- return await getPrompt(name, args || {});
3005
- });
3006
- return server;
3040
+ }
3041
+ return true;
3007
3042
  }
3008
- var SERVER_CAPABILITIES;
3009
- var init_mcp_handlers = __esm({
3010
- "src/mcp-handlers.js"() {
3011
- init_resource_handlers();
3012
- init_prompt_handlers();
3013
- init_constants();
3014
- init_subscription_manager();
3015
- init_mcp_server_state();
3016
- SERVER_CAPABILITIES = {
3017
- tools: {},
3018
- resources: { subscribe: true, listChanged: true },
3019
- prompts: {}
3020
- };
3043
+ var TOOL_HANDLERS = {
3044
+ connect_cad: (a) => connectCad(a?.platform),
3045
+ eval_lisp: (a) => evalLisp(a.code),
3046
+ eval_lisp_with_result: (a) => evalLisp(a.code, true, a.encoding),
3047
+ get_cad_info: () => getCadInfo2(),
3048
+ list_packages: () => listPackages(),
3049
+ search_packages: (a) => searchPackages(a.query),
3050
+ install_package: (a) => installPackage(a.packageName),
3051
+ get_platform_info: () => ({ content: [{ type: "text", text: `\u652F\u6301\u7684 CAD \u5E73\u53F0:
3052
+ ${CAD_PLATFORMS.join("\n")}
3053
+ \u5F53\u524D\u8FDE\u63A5: ${cad.connected ? "\u5DF2\u8FDE\u63A5" : "\u672A\u8FDE\u63A5"}` }] }),
3054
+ at_command: (a) => atCommand(a.command),
3055
+ new_document: () => newDocument(),
3056
+ bring_to_front: () => bringToFront(),
3057
+ install_atlisp: () => installAtlisp(),
3058
+ init_atlisp: () => initAtlisp(),
3059
+ get_function_usage: (a) => getFunctionUsage(a.name, a.package),
3060
+ list_symbols: (a) => listSymbols(a.package),
3061
+ get_system_status: () => getSystemStatus(),
3062
+ list_prompts: async () => {
3063
+ const prompts = await listPrompts();
3064
+ return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
3065
+ },
3066
+ get_prompt: async (a) => {
3067
+ const prompt = await getPrompt(a.name, a.args || {});
3068
+ return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
3069
+ },
3070
+ import_funlib: async (a) => {
3071
+ const data = await loadAtlibFunctionLib();
3072
+ if (!data) return { content: [{ type: "text", text: "\u52A0\u8F7D\u51FD\u6570\u5E93\u5931\u8D25" }], isError: true };
3073
+ if (a.format === "list") {
3074
+ const items = Object.values(data.all_functions || {}).map((f) => `${f.name}: ${f.description || ""}`);
3075
+ return { content: [{ type: "text", text: items.join("\n") }] };
3076
+ }
3077
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
3021
3078
  }
3022
- });
3079
+ };
3080
+ var tools = [
3081
+ {
3082
+ name: "connect_cad",
3083
+ description: "\u8FDE\u63A5\u5230 CAD (AutoCAD/ZWCAD/GStarCAD/BricsCAD)",
3084
+ inputSchema: {
3085
+ type: "object",
3086
+ properties: {
3087
+ platform: { type: "string", description: "\u6307\u5B9A CAD \u5E73\u53F0: AutoCAD, ZWCAD, GStarCAD, BricsCAD\uFF08\u53EF\u9009\uFF0C\u81EA\u52A8\u68C0\u6D4B\uFF09" }
3088
+ }
3089
+ }
3090
+ },
3091
+ {
3092
+ name: "eval_lisp",
3093
+ description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\uFF08\u4E0D\u8FD4\u56DE\u7ED3\u679C\uFF09",
3094
+ inputSchema: {
3095
+ type: "object",
3096
+ properties: { code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" } },
3097
+ required: ["code"]
3098
+ }
3099
+ },
3100
+ {
3101
+ name: "eval_lisp_with_result",
3102
+ description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\u5E76\u8FD4\u56DE\u7ED3\u679C",
3103
+ inputSchema: {
3104
+ type: "object",
3105
+ properties: {
3106
+ code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" },
3107
+ encoding: { type: "string", description: "\u7ED3\u679C\u7F16\u7801\uFF0C\u5982 utf-8, gbk, gb2312, gb18030\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u81EA\u52A8\u68C0\u6D4B\uFF09" }
3108
+ },
3109
+ required: ["code"]
3110
+ }
3111
+ },
3112
+ {
3113
+ name: "get_cad_info",
3114
+ description: "\u83B7\u53D6\u5F53\u524D CAD \u4FE1\u606F",
3115
+ inputSchema: { type: "object", properties: {} }
3116
+ },
3117
+ {
3118
+ name: "list_packages",
3119
+ description: "\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 @lisp \u5305",
3120
+ inputSchema: { type: "object", properties: {} }
3121
+ },
3122
+ {
3123
+ name: "search_packages",
3124
+ description: "\u641C\u7D22 @lisp \u5305",
3125
+ inputSchema: {
3126
+ type: "object",
3127
+ properties: { query: { type: "string", description: "\u641C\u7D22\u5173\u952E\u8BCD" } },
3128
+ required: ["query"]
3129
+ }
3130
+ },
3131
+ {
3132
+ name: "install_package",
3133
+ description: "\u5B89\u88C5 @lisp \u5305\u5230 CAD",
3134
+ inputSchema: {
3135
+ type: "object",
3136
+ properties: { packageName: { type: "string", description: "\u5305\u540D\u79F0" } },
3137
+ required: ["packageName"]
3138
+ }
3139
+ },
3140
+ {
3141
+ name: "get_platform_info",
3142
+ description: "\u83B7\u53D6 CAD \u5E73\u53F0\u4FE1\u606F",
3143
+ inputSchema: { type: "object", properties: {} }
3144
+ },
3145
+ {
3146
+ name: "at_command",
3147
+ description: "\u6267\u884C @lisp \u547D\u4EE4",
3148
+ inputSchema: {
3149
+ type: "object",
3150
+ properties: { command: { type: "string", description: "@lisp \u547D\u4EE4" } },
3151
+ required: ["command"]
3152
+ }
3153
+ },
3154
+ {
3155
+ name: "new_document",
3156
+ description: "\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863",
3157
+ inputSchema: { type: "object", properties: {} }
3158
+ },
3159
+ {
3160
+ name: "bring_to_front",
3161
+ description: "\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0\uFF08\u4ECE\u540E\u53F0\u8FD0\u884C\u72B6\u6001\u5524\u9192\uFF09",
3162
+ inputSchema: { type: "object", properties: {} }
3163
+ },
3164
+ {
3165
+ name: "install_atlisp",
3166
+ description: "\u5728 CAD \u4E2D\u5B89\u88C5 @lisp",
3167
+ inputSchema: { type: "object", properties: {} }
3168
+ },
3169
+ {
3170
+ name: "init_atlisp",
3171
+ description: "\u521D\u59CB\u5316 CAD \u4E2D\u7684 @lisp \u73AF\u5883\uFF0C\u52A0\u8F7D\u51FD\u6570\u5E93\u548C\u521D\u59CB\u53D8\u91CF",
3172
+ inputSchema: { type: "object", properties: {} }
3173
+ },
3174
+ {
3175
+ name: "get_function_usage",
3176
+ description: "\u83B7\u53D6 @lisp \u51FD\u6570\u7528\u6CD5",
3177
+ inputSchema: {
3178
+ type: "object",
3179
+ properties: {
3180
+ name: { type: "string", description: "\u51FD\u6570\u540D\uFF0C\u5982 string:length" },
3181
+ package: { type: "string", description: "\u5305\u540D\uFF0C\u5982 string\uFF08\u53EF\u9009\uFF0C\u4E0D\u4F20\u5219\u81EA\u52A8\u641C\u7D22\uFF09" }
3182
+ },
3183
+ required: ["name"]
3184
+ }
3185
+ },
3186
+ {
3187
+ name: "list_symbols",
3188
+ description: "\u5217\u51FA @lisp \u6240\u6709\u7B26\u53F7\uFF0C\u7528\u4E8E\u68C0\u67E5\u51FD\u6570\u548C\u53D8\u91CF",
3189
+ inputSchema: {
3190
+ type: "object",
3191
+ properties: {
3192
+ package: { type: "string", description: "\u5305\u540D\uFF08\u53EF\u9009\uFF09" }
3193
+ }
3194
+ }
3195
+ },
3196
+ {
3197
+ name: "import_funlib",
3198
+ description: "\u4ECE\u7F51\u7EDC\u5BFC\u5165 @lisp \u51FD\u6570\u5E93\u6570\u636E\u5230 AI Agent",
3199
+ inputSchema: {
3200
+ type: "object",
3201
+ properties: {
3202
+ 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" }
3203
+ }
3204
+ }
3205
+ },
3206
+ {
3207
+ name: "get_system_status",
3208
+ description: "\u83B7\u53D6\u5B8C\u6574\u7CFB\u7EDF\u8BCA\u65AD\u4FE1\u606F\uFF1AMCP \u72B6\u6001\u3001CAD \u8FDE\u63A5\u3001Lisp \u6267\u884C\u3001\u5305\u5217\u8868",
3209
+ inputSchema: { type: "object", properties: {} }
3210
+ },
3211
+ {
3212
+ name: "list_prompts",
3213
+ description: "\u5217\u51FA\u6240\u6709\u53EF\u7528\u7684 @lisp \u63D0\u793A\u6A21\u677F",
3214
+ inputSchema: { type: "object", properties: {} }
3215
+ },
3216
+ {
3217
+ name: "get_prompt",
3218
+ description: "\u83B7\u53D6\u6307\u5B9A\u63D0\u793A\u6A21\u677F\u7684\u5185\u5BB9",
3219
+ inputSchema: {
3220
+ type: "object",
3221
+ properties: {
3222
+ name: { type: "string", description: "\u63D0\u793A\u6A21\u677F\u540D\u79F0" },
3223
+ args: { type: "object", description: "\u6A21\u677F\u53C2\u6570\uFF08\u53EF\u9009\uFF09" }
3224
+ },
3225
+ required: ["name"]
3226
+ }
3227
+ }
3228
+ ];
3229
+ function notifyResourceChanges(toolName) {
3230
+ const resourceMap = {
3231
+ connect_cad: ["atlisp://cad/info", "atlisp://dwg/layers", "atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/dwgs", "atlisp://packages"],
3232
+ new_document: ["atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/dwgs", "atlisp://dwg/layers", "atlisp://dwg/entities", "atlisp://dwg/texts"],
3233
+ install_package: ["atlisp://packages"],
3234
+ install_atlisp: ["atlisp://packages", "atlisp://cad/info"],
3235
+ init_atlisp: ["atlisp://packages", "atlisp://cad/info"]
3236
+ };
3237
+ const uris = resourceMap[toolName];
3238
+ if (uris) {
3239
+ for (const uri of uris) {
3240
+ clearCache(uri);
3241
+ if (isSubscribed(uri)) {
3242
+ notify(uri, null);
3243
+ }
3244
+ }
3245
+ }
3246
+ }
3247
+ async function handleToolCall(name, args) {
3248
+ const handler = TOOL_HANDLERS[name];
3249
+ if (!handler) return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
3250
+ try {
3251
+ validateToolArgs(name, args || {});
3252
+ const result = await handler(args || {});
3253
+ notifyResourceChanges(name);
3254
+ return result;
3255
+ } catch (e) {
3256
+ error(`Tool call error [${name}]: ${e.message}`, { tool: name, args });
3257
+ return { content: [{ type: "text", text: `\u5DE5\u5177\u6267\u884C\u9519\u8BEF: ${e.message}` }], isError: true };
3258
+ }
3259
+ }
3023
3260
 
3024
3261
  // src/mcp-server-state.js
3025
- import { AsyncLocalStorage } from "async_hooks";
3262
+ var mcpContext = new AsyncLocalStorage();
3263
+ var sessionServers = /* @__PURE__ */ new Map();
3264
+ var sessionTransports = /* @__PURE__ */ new Map();
3265
+ var mcpHandlers = { tools, handleToolCall };
3026
3266
  async function getOrCreateSessionServer(sessionId) {
3027
- if (!mcpHandlers) {
3028
- const { tools: tools2, handleToolCall: handleToolCall2 } = await init_atlisp_mcp().then(() => atlisp_mcp_exports);
3029
- mcpHandlers = { tools: tools2, handleToolCall: handleToolCall2 };
3030
- }
3031
3267
  let entry = sessionServers.get(sessionId);
3032
3268
  if (!entry) {
3033
3269
  const server = createMcpServer(mcpHandlers);
@@ -3061,24 +3297,18 @@ function broadcastToAllSessions(uri) {
3061
3297
  });
3062
3298
  }
3063
3299
  }
3064
- var mcpContext, sessionServers, sessionTransports, mcpHandlers;
3065
- var init_mcp_server_state = __esm({
3066
- "src/mcp-server-state.js"() {
3067
- init_session_transport();
3068
- init_mcp_handlers();
3069
- init_logger();
3070
- mcpContext = new AsyncLocalStorage();
3071
- sessionServers = /* @__PURE__ */ new Map();
3072
- sessionTransports = /* @__PURE__ */ new Map();
3073
- mcpHandlers = null;
3074
- }
3075
- });
3076
3300
 
3077
3301
  // src/routes.js
3078
- import express from "express";
3079
- import { randomUUID as randomUUID4 } from "crypto";
3080
- import iconv from "iconv-lite";
3081
- import rateLimit from "express-rate-limit";
3302
+ var sseSessionManager = new SseSessionManager();
3303
+ var { apiKey, enableCors, corsOrigin, rateLimitWindow, rateLimitMax } = config_default;
3304
+ var mcpLimiter = rateLimit({
3305
+ windowMs: rateLimitWindow,
3306
+ max: rateLimitMax,
3307
+ standardHeaders: true,
3308
+ legacyHeaders: false,
3309
+ message: { error: "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5" }
3310
+ });
3311
+ var JSON_CONTENT_TYPES = ["application/json", "application/json-rpc", "application/vnd.api+json"];
3082
3312
  function createJsonBodyParser() {
3083
3313
  return async (req, res, next) => {
3084
3314
  const ct = (req.headers["content-type"] || "").toLowerCase();
@@ -3246,132 +3476,55 @@ async function handleMessage(req, res) {
3246
3476
  res.status(400).json({ error: "Invalid request" });
3247
3477
  }
3248
3478
  }
3249
- var sseSessionManager, apiKey, enableCors, corsOrigin, rateLimitWindow, rateLimitMax, mcpLimiter, JSON_CONTENT_TYPES;
3250
- var init_routes = __esm({
3251
- "src/routes.js"() {
3252
- init_config();
3253
- init_logger();
3254
- init_cad();
3255
- init_subscription_manager();
3256
- init_sse_session_manager();
3257
- init_mcp_server_state();
3258
- init_sse_session();
3259
- sseSessionManager = new SseSessionManager();
3260
- ({ apiKey, enableCors, corsOrigin, rateLimitWindow, rateLimitMax } = config_default);
3261
- mcpLimiter = rateLimit({
3262
- windowMs: rateLimitWindow,
3263
- max: rateLimitMax,
3264
- standardHeaders: true,
3265
- legacyHeaders: false,
3266
- message: { error: "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5" }
3267
- });
3268
- JSON_CONTENT_TYPES = ["application/json", "application/json-rpc", "application/vnd.api+json"];
3269
- }
3270
- });
3271
3479
 
3272
3480
  // src/atlisp-mcp.js
3273
- var atlisp_mcp_exports = {};
3274
- __export(atlisp_mcp_exports, {
3275
- CAD_PLATFORMS: () => CAD_PLATFORMS,
3276
- FILE_EXTENSIONS: () => FILE_EXTENSIONS,
3277
- handleToolCall: () => handleToolCall,
3278
- loadAtlibFunctionLib: () => loadAtlibFunctionLib,
3279
- startServer: () => startServer,
3280
- tools: () => tools
3281
- });
3282
- import path7 from "path";
3283
- import fs6 from "fs";
3284
- import { fileURLToPath as fileURLToPath4 } from "url";
3285
- import { createRequire } from "module";
3286
- import os4 from "os";
3287
- import express2 from "express";
3288
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3289
- async function readCacheFromDisk2() {
3290
- try {
3291
- const stat = fs6.statSync(FUNCLIB_CACHE_FILE2);
3292
- if (Date.now() - stat.mtimeMs < CACHE_TTL3) {
3293
- const raw = fs6.readFileSync(FUNCLIB_CACHE_FILE2, "utf-8");
3294
- return JSON.parse(raw);
3295
- }
3296
- } catch {
3297
- }
3298
- return null;
3299
- }
3300
- function writeCacheToDisk2(data) {
3301
- try {
3302
- fs6.mkdirSync(CACHE_DIR2, { recursive: true });
3303
- fs6.writeFileSync(FUNCLIB_CACHE_FILE2, JSON.stringify(data), "utf-8");
3304
- } catch (e) {
3305
- error(`writeCacheToDisk error: ${e.message}`);
3306
- }
3307
- }
3308
- async function loadAtlibFunctionLib() {
3309
- const now = Date.now();
3310
- if (cachedFunctionLib && cachedAt2 && now - cachedAt2 < CACHE_TTL3) {
3311
- return cachedFunctionLib;
3312
- }
3313
- const diskCache = await readCacheFromDisk2();
3314
- if (diskCache) {
3315
- cachedFunctionLib = diskCache;
3316
- cachedAt2 = now;
3317
- return diskCache;
3318
- }
3319
- try {
3320
- const response = await fetch(FUNCTION_LIB_URL, { headers: { "User-Agent": "atlisp-mcp" } });
3321
- if (response.ok) {
3322
- const data = await response.json();
3323
- cachedFunctionLib = data;
3324
- cachedAt2 = now;
3325
- writeCacheToDisk2(data);
3326
- return data;
3327
- }
3328
- } catch (e) {
3329
- error(`loadAtlibFunctionLib error: ${e.message}`);
3330
- }
3331
- return cachedFunctionLib;
3332
- }
3333
- function validateToolArgs(name, args) {
3334
- const schema = toolInputSchema[name];
3335
- if (!schema) return true;
3336
- for (const [key, type] of Object.entries(schema)) {
3337
- if (type.endsWith("?")) continue;
3338
- if (!args[key]) {
3339
- throw new Error(`Missing required argument: ${key}`);
3340
- }
3341
- }
3342
- return true;
3343
- }
3344
- async function handleToolCall(name, args) {
3345
- const handler = TOOL_HANDLERS[name];
3346
- if (!handler) return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
3347
- try {
3348
- validateToolArgs(name, args || {});
3349
- const result = await handler(args || {});
3350
- notifyResourceChanges(name);
3351
- return result;
3352
- } catch (e) {
3353
- error(`Tool call error [${name}]: ${e.message}`, { tool: name, args });
3354
- return { content: [{ type: "text", text: `\u5DE5\u5177\u6267\u884C\u9519\u8BEF: ${e.message}` }], isError: true };
3355
- }
3356
- }
3357
- function notifyResourceChanges(toolName) {
3358
- const resourceMap = {
3359
- connect_cad: ["atlisp://cad/info", "atlisp://dwg/layers", "atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/dwgs", "atlisp://packages"],
3360
- new_document: ["atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/dwgs", "atlisp://dwg/layers", "atlisp://dwg/entities", "atlisp://dwg/texts"],
3361
- install_package: ["atlisp://packages"],
3362
- install_atlisp: ["atlisp://packages", "atlisp://cad/info"],
3363
- init_atlisp: ["atlisp://packages", "atlisp://cad/info"]
3364
- };
3365
- const uris = resourceMap[toolName];
3366
- if (uris) {
3367
- for (const uri of uris) {
3368
- clearCache(uri);
3369
- if (isSubscribed(uri)) {
3370
- notify(uri, null);
3371
- }
3481
+ var __dirname4 = path8.dirname(fileURLToPath4(import.meta.url));
3482
+ var require2 = createRequire(import.meta.url);
3483
+ var { version } = require2(path8.join(__dirname4, "..", "package.json"));
3484
+ var sseSessionManager2 = new SseSessionManager();
3485
+ var isMcpScript = process.argv[1]?.includes("atlisp-mcp");
3486
+ if (isMcpScript) {
3487
+ const args = process.argv.slice(2);
3488
+ for (let i = 0; i < args.length; i++) {
3489
+ if (args[i] === "--version" || args[i] === "-v") {
3490
+ console.log(`atlisp-mcp v${version}`);
3491
+ process.exit(0);
3492
+ } else if (args[i] === "--help" || args[i] === "-h") {
3493
+ console.log(`
3494
+ @lisp MCP Server v${version}
3495
+
3496
+ Usage: atlisp-mcp [options]
3497
+
3498
+ Options:
3499
+ -v, --version Show version number
3500
+ --transport <type> Transport type: http or stdio (default: http)
3501
+ --port <port> HTTP port (default: 8110)
3502
+ --host <host> HTTP host (default: 0.0.0.0)
3503
+ --stdio Shorthand for --transport stdio
3504
+ -h, --help Show this help message
3505
+
3506
+ Examples:
3507
+ atlisp-mcp # Start HTTP server on port 8110
3508
+ atlisp-mcp --port 3000 # Start HTTP server on port 3000
3509
+ atlisp-mcp --stdio # Start in stdio mode for MCP clients
3510
+ atlisp-mcp --transport stdio # Same as --stdio
3511
+ `);
3512
+ process.exit(0);
3513
+ } else if (args[i] === "--transport" && args[i + 1]) {
3514
+ process.env.TRANSPORT = args[i + 1];
3515
+ i++;
3516
+ } else if (args[i] === "--stdio") {
3517
+ process.env.TRANSPORT = "stdio";
3518
+ } else if (args[i] === "--port" && args[i + 1]) {
3519
+ process.env.PORT = args[i + 1];
3520
+ i++;
3521
+ } else if (args[i] === "--host" && args[i + 1]) {
3522
+ process.env.HOST = args[i + 1];
3523
+ i++;
3372
3524
  }
3373
3525
  }
3374
3526
  }
3527
+ if (config_default.apiKey) log("API Key authentication enabled");
3375
3528
  async function initCadConnection() {
3376
3529
  log("Attempting to connect to CAD on startup...");
3377
3530
  try {
@@ -3432,282 +3585,9 @@ async function startServer() {
3432
3585
  process.on("SIGTERM", () => shutdown("SIGTERM"));
3433
3586
  }
3434
3587
  }
3435
- var __dirname4, require2, version, sseSessionManager2, isMcpScript, CACHE_DIR2, FUNCLIB_CACHE_FILE2, CACHE_TTL3, FUNCTION_LIB_URL, cachedFunctionLib, cachedAt2, toolInputSchema, TOOL_HANDLERS, tools;
3436
- var init_atlisp_mcp = __esm({
3437
- async "src/atlisp-mcp.js"() {
3438
- init_cad();
3439
- init_constants();
3440
- init_logger();
3441
- init_cad_handlers();
3442
- init_package_handlers();
3443
- init_function_handlers();
3444
- init_resource_handlers();
3445
- init_prompt_handlers();
3446
- init_subscription_manager();
3447
- init_subscription_manager();
3448
- init_sse_session_manager();
3449
- init_sse_session();
3450
- init_config();
3451
- init_routes();
3452
- init_mcp_server_state();
3453
- init_constants();
3454
- __dirname4 = path7.dirname(fileURLToPath4(import.meta.url));
3455
- require2 = createRequire(import.meta.url);
3456
- ({ version } = require2(path7.join(__dirname4, "..", "package.json")));
3457
- sseSessionManager2 = new SseSessionManager();
3458
- isMcpScript = process.argv[1]?.includes("atlisp-mcp");
3459
- if (isMcpScript) {
3460
- const args = process.argv.slice(2);
3461
- for (let i = 0; i < args.length; i++) {
3462
- if (args[i] === "--version" || args[i] === "-v") {
3463
- console.log(`atlisp-mcp v${version}`);
3464
- process.exit(0);
3465
- } else if (args[i] === "--help" || args[i] === "-h") {
3466
- console.log(`
3467
- @lisp MCP Server v${version}
3468
-
3469
- Usage: atlisp-mcp [options]
3470
-
3471
- Options:
3472
- -v, --version Show version number
3473
- --transport <type> Transport type: http or stdio (default: http)
3474
- --port <port> HTTP port (default: 8110)
3475
- --host <host> HTTP host (default: 0.0.0.0)
3476
- --stdio Shorthand for --transport stdio
3477
- -h, --help Show this help message
3478
-
3479
- Examples:
3480
- atlisp-mcp # Start HTTP server on port 8110
3481
- atlisp-mcp --port 3000 # Start HTTP server on port 3000
3482
- atlisp-mcp --stdio # Start in stdio mode for MCP clients
3483
- atlisp-mcp --transport stdio # Same as --stdio
3484
- `);
3485
- process.exit(0);
3486
- } else if (args[i] === "--transport" && args[i + 1]) {
3487
- process.env.TRANSPORT = args[i + 1];
3488
- i++;
3489
- } else if (args[i] === "--stdio") {
3490
- process.env.TRANSPORT = "stdio";
3491
- } else if (args[i] === "--port" && args[i + 1]) {
3492
- process.env.PORT = args[i + 1];
3493
- i++;
3494
- } else if (args[i] === "--host" && args[i + 1]) {
3495
- process.env.HOST = args[i + 1];
3496
- i++;
3497
- }
3498
- }
3499
- }
3500
- if (config_default.apiKey) log("API Key authentication enabled");
3501
- CACHE_DIR2 = path7.join(os4.homedir(), ".atlisp", "cache");
3502
- FUNCLIB_CACHE_FILE2 = path7.join(CACHE_DIR2, "functions.json");
3503
- CACHE_TTL3 = config_default.functionLibCacheTtl || 5 * 60 * 1e3;
3504
- FUNCTION_LIB_URL = config_default.functionLibUrl || "http://s3.atlisp.cn/json/functions.json";
3505
- cachedFunctionLib = null;
3506
- cachedAt2 = null;
3507
- toolInputSchema = {
3508
- connect_cad: { platform: "string?" },
3509
- eval_lisp: { code: "string" },
3510
- eval_lisp_with_result: { code: "string", encoding: "string?" },
3511
- search_packages: { query: "string" },
3512
- install_package: { packageName: "string" },
3513
- at_command: { command: "string" },
3514
- get_function_usage: { name: "string", package: "string?" },
3515
- list_symbols: { package: "string?" },
3516
- import_funlib: { format: "string?" },
3517
- get_prompt: { name: "string", args: "object?" }
3518
- };
3519
- TOOL_HANDLERS = {
3520
- connect_cad: (a) => connectCad(a?.platform),
3521
- eval_lisp: (a) => evalLisp(a.code),
3522
- eval_lisp_with_result: (a) => evalLisp(a.code, true, a.encoding),
3523
- get_cad_info: () => getCadInfo(),
3524
- list_packages: () => listPackages(),
3525
- search_packages: (a) => searchPackages(a.query),
3526
- install_package: (a) => installPackage(a.packageName),
3527
- get_platform_info: () => ({ content: [{ type: "text", text: `\u652F\u6301\u7684 CAD \u5E73\u53F0:
3528
- ${CAD_PLATFORMS.join("\n")}
3529
- \u5F53\u524D\u8FDE\u63A5: ${cad.connected ? "\u5DF2\u8FDE\u63A5" : "\u672A\u8FDE\u63A5"}` }] }),
3530
- at_command: (a) => atCommand(a.command),
3531
- new_document: () => newDocument(),
3532
- bring_to_front: () => bringToFront(),
3533
- install_atlisp: () => installAtlisp(),
3534
- init_atlisp: () => initAtlisp(),
3535
- get_function_usage: (a) => getFunctionUsage(a.name, a.package),
3536
- list_symbols: (a) => listSymbols(a.package),
3537
- get_system_status: () => getSystemStatus(),
3538
- list_prompts: async () => {
3539
- const prompts = await listPrompts();
3540
- return { content: [{ type: "text", text: JSON.stringify(prompts) }] };
3541
- },
3542
- get_prompt: async (a) => {
3543
- const prompt = await getPrompt(a.name, a.args || {});
3544
- return { content: prompt.messages.map((m) => ({ type: "text", text: m.content.text || m.content })) };
3545
- },
3546
- import_funlib: async (a) => {
3547
- const data = await loadAtlibFunctionLib();
3548
- if (!data) return { content: [{ type: "text", text: "\u52A0\u8F7D\u51FD\u6570\u5E93\u5931\u8D25" }], isError: true };
3549
- if (a.format === "list") {
3550
- const items = Object.values(data.all_functions || {}).map((f) => `${f.name}: ${f.description || ""}`);
3551
- return { content: [{ type: "text", text: items.join("\n") }] };
3552
- }
3553
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
3554
- }
3555
- };
3556
- tools = [
3557
- {
3558
- name: "connect_cad",
3559
- description: "\u8FDE\u63A5\u5230 CAD (AutoCAD/ZWCAD/GStarCAD/BricsCAD)",
3560
- inputSchema: {
3561
- type: "object",
3562
- properties: {
3563
- platform: { type: "string", description: "\u6307\u5B9A CAD \u5E73\u53F0: AutoCAD, ZWCAD, GStarCAD, BricsCAD\uFF08\u53EF\u9009\uFF0C\u81EA\u52A8\u68C0\u6D4B\uFF09" }
3564
- }
3565
- }
3566
- },
3567
- {
3568
- name: "eval_lisp",
3569
- description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\uFF08\u4E0D\u8FD4\u56DE\u7ED3\u679C\uFF09",
3570
- inputSchema: {
3571
- type: "object",
3572
- properties: { code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" } },
3573
- required: ["code"]
3574
- }
3575
- },
3576
- {
3577
- name: "eval_lisp_with_result",
3578
- description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\u5E76\u8FD4\u56DE\u7ED3\u679C",
3579
- inputSchema: {
3580
- type: "object",
3581
- properties: {
3582
- code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" },
3583
- encoding: { type: "string", description: "\u7ED3\u679C\u7F16\u7801\uFF0C\u5982 utf-8, gbk, gb2312, gb18030\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u81EA\u52A8\u68C0\u6D4B\uFF09" }
3584
- },
3585
- required: ["code"]
3586
- }
3587
- },
3588
- {
3589
- name: "get_cad_info",
3590
- description: "\u83B7\u53D6\u5F53\u524D CAD \u4FE1\u606F",
3591
- inputSchema: { type: "object", properties: {} }
3592
- },
3593
- {
3594
- name: "list_packages",
3595
- description: "\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 @lisp \u5305",
3596
- inputSchema: { type: "object", properties: {} }
3597
- },
3598
- {
3599
- name: "search_packages",
3600
- description: "\u641C\u7D22 @lisp \u5305",
3601
- inputSchema: {
3602
- type: "object",
3603
- properties: { query: { type: "string", description: "\u641C\u7D22\u5173\u952E\u8BCD" } },
3604
- required: ["query"]
3605
- }
3606
- },
3607
- {
3608
- name: "install_package",
3609
- description: "\u5B89\u88C5 @lisp \u5305\u5230 CAD",
3610
- inputSchema: {
3611
- type: "object",
3612
- properties: { packageName: { type: "string", description: "\u5305\u540D\u79F0" } },
3613
- required: ["packageName"]
3614
- }
3615
- },
3616
- {
3617
- name: "get_platform_info",
3618
- description: "\u83B7\u53D6 CAD \u5E73\u53F0\u4FE1\u606F",
3619
- inputSchema: { type: "object", properties: {} }
3620
- },
3621
- {
3622
- name: "at_command",
3623
- description: "\u6267\u884C @lisp \u547D\u4EE4",
3624
- inputSchema: {
3625
- type: "object",
3626
- properties: { command: { type: "string", description: "@lisp \u547D\u4EE4" } },
3627
- required: ["command"]
3628
- }
3629
- },
3630
- {
3631
- name: "new_document",
3632
- description: "\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863",
3633
- inputSchema: { type: "object", properties: {} }
3634
- },
3635
- {
3636
- name: "bring_to_front",
3637
- description: "\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0\uFF08\u4ECE\u540E\u53F0\u8FD0\u884C\u72B6\u6001\u5524\u9192\uFF09",
3638
- inputSchema: { type: "object", properties: {} }
3639
- },
3640
- {
3641
- name: "install_atlisp",
3642
- description: "\u5728 CAD \u4E2D\u5B89\u88C5 @lisp",
3643
- inputSchema: { type: "object", properties: {} }
3644
- },
3645
- {
3646
- name: "init_atlisp",
3647
- description: "\u521D\u59CB\u5316 CAD \u4E2D\u7684 @lisp \u73AF\u5883\uFF0C\u52A0\u8F7D\u51FD\u6570\u5E93\u548C\u521D\u59CB\u53D8\u91CF",
3648
- inputSchema: { type: "object", properties: {} }
3649
- },
3650
- {
3651
- name: "get_function_usage",
3652
- description: "\u83B7\u53D6 @lisp \u51FD\u6570\u7528\u6CD5",
3653
- inputSchema: {
3654
- type: "object",
3655
- properties: {
3656
- name: { type: "string", description: "\u51FD\u6570\u540D\uFF0C\u5982 string:length" },
3657
- package: { type: "string", description: "\u5305\u540D\uFF0C\u5982 string\uFF08\u53EF\u9009\uFF0C\u4E0D\u4F20\u5219\u81EA\u52A8\u641C\u7D22\uFF09" }
3658
- },
3659
- required: ["name"]
3660
- }
3661
- },
3662
- {
3663
- name: "list_symbols",
3664
- description: "\u5217\u51FA @lisp \u6240\u6709\u7B26\u53F7\uFF0C\u7528\u4E8E\u68C0\u67E5\u51FD\u6570\u548C\u53D8\u91CF",
3665
- inputSchema: {
3666
- type: "object",
3667
- properties: {
3668
- package: { type: "string", description: "\u5305\u540D\uFF08\u53EF\u9009\uFF09" }
3669
- }
3670
- }
3671
- },
3672
- {
3673
- name: "import_funlib",
3674
- description: "\u4ECE\u7F51\u7EDC\u5BFC\u5165 @lisp \u51FD\u6570\u5E93\u6570\u636E\u5230 AI Agent",
3675
- inputSchema: {
3676
- type: "object",
3677
- properties: {
3678
- 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" }
3679
- }
3680
- }
3681
- },
3682
- {
3683
- name: "get_system_status",
3684
- description: "\u83B7\u53D6\u5B8C\u6574\u7CFB\u7EDF\u8BCA\u65AD\u4FE1\u606F\uFF1AMCP \u72B6\u6001\u3001CAD \u8FDE\u63A5\u3001Lisp \u6267\u884C\u3001\u5305\u5217\u8868",
3685
- inputSchema: { type: "object", properties: {} }
3686
- },
3687
- {
3688
- name: "list_prompts",
3689
- description: "\u5217\u51FA\u6240\u6709\u53EF\u7528\u7684 @lisp \u63D0\u793A\u6A21\u677F",
3690
- inputSchema: { type: "object", properties: {} }
3691
- },
3692
- {
3693
- name: "get_prompt",
3694
- description: "\u83B7\u53D6\u6307\u5B9A\u63D0\u793A\u6A21\u677F\u7684\u5185\u5BB9",
3695
- inputSchema: {
3696
- type: "object",
3697
- properties: {
3698
- name: { type: "string", description: "\u63D0\u793A\u6A21\u677F\u540D\u79F0" },
3699
- args: { type: "object", description: "\u6A21\u677F\u53C2\u6570\uFF08\u53EF\u9009\uFF09" }
3700
- },
3701
- required: ["name"]
3702
- }
3703
- }
3704
- ];
3705
- if (!process.env.VITEST) {
3706
- await startServer();
3707
- }
3708
- }
3709
- });
3710
- await init_atlisp_mcp();
3588
+ if (!process.env.VITEST) {
3589
+ await startServer();
3590
+ }
3711
3591
  export {
3712
3592
  CAD_PLATFORMS,
3713
3593
  FILE_EXTENSIONS,