@atlisp/mcp 1.3.0 → 1.3.1

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.
@@ -0,0 +1,2314 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/atlisp-mcp.js
4
+ import path4 from "path";
5
+ import { fileURLToPath as fileURLToPath2 } from "url";
6
+ import { createRequire } from "module";
7
+ import express from "express";
8
+ import rawBody from "raw-body";
9
+ import iconv from "iconv-lite";
10
+ import rateLimit from "express-rate-limit";
11
+ import { randomUUID as randomUUID4 } from "crypto";
12
+ import { AsyncLocalStorage } from "async_hooks";
13
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
14
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
+
16
+ // src/session-transport.js
17
+ import { randomUUID } from "crypto";
18
+ var SESSION_TIMEOUT = 30 * 60 * 1e3;
19
+ var SessionTransport = class {
20
+ constructor(sessionId) {
21
+ this.sessionId = sessionId || randomUUID();
22
+ this.onclose = null;
23
+ this.onerror = null;
24
+ this.onmessage = null;
25
+ this._pendingRequest = null;
26
+ this._started = false;
27
+ this._closed = false;
28
+ this._cleanupTimer = null;
29
+ }
30
+ async start() {
31
+ if (this._started) {
32
+ throw new Error("Transport already started");
33
+ }
34
+ this._started = true;
35
+ }
36
+ async send(message) {
37
+ if (this._closed) return;
38
+ if (this._pendingRequest) {
39
+ const { resolve, reject, timeout } = this._pendingRequest;
40
+ clearTimeout(timeout);
41
+ this._pendingRequest = null;
42
+ resolve(message);
43
+ }
44
+ }
45
+ async close() {
46
+ if (this._closed) return;
47
+ this._closed = true;
48
+ if (this._pendingRequest) {
49
+ const { reject, timeout } = this._pendingRequest;
50
+ clearTimeout(timeout);
51
+ this._pendingRequest = null;
52
+ reject(new Error("Transport closed"));
53
+ }
54
+ this._clearCleanupTimer();
55
+ this.onclose?.();
56
+ }
57
+ async handleRequest(message, timeoutMs = 6e4) {
58
+ if (this._closed) {
59
+ throw new Error("Transport closed");
60
+ }
61
+ if (!message || !message.method) {
62
+ throw new Error("Invalid message");
63
+ }
64
+ if (!message.id) {
65
+ this.onmessage?.(message);
66
+ return null;
67
+ }
68
+ return new Promise((resolve, reject) => {
69
+ const timeout = setTimeout(() => {
70
+ if (this._pendingRequest?.resolve === resolve) {
71
+ this._pendingRequest = null;
72
+ reject(new Error("Request timeout"));
73
+ }
74
+ }, timeoutMs);
75
+ this._pendingRequest = { resolve, reject, timeout };
76
+ this.onmessage?.(message);
77
+ });
78
+ }
79
+ resetCleanupTimer() {
80
+ this._clearCleanupTimer();
81
+ this._cleanupTimer = setTimeout(() => {
82
+ this.close();
83
+ }, SESSION_TIMEOUT);
84
+ }
85
+ _clearCleanupTimer() {
86
+ if (this._cleanupTimer) {
87
+ clearTimeout(this._cleanupTimer);
88
+ this._cleanupTimer = null;
89
+ }
90
+ }
91
+ };
92
+
93
+ // src/atlisp-mcp.js
94
+ import {
95
+ ListToolsRequestSchema,
96
+ CallToolRequestSchema,
97
+ ListResourcesRequestSchema,
98
+ ReadResourceRequestSchema,
99
+ SubscribeRequestSchema,
100
+ UnsubscribeRequestSchema,
101
+ ListPromptsRequestSchema,
102
+ GetPromptRequestSchema
103
+ } from "@modelcontextprotocol/sdk/types.js";
104
+
105
+ // src/cad.js
106
+ import { spawn } from "child_process";
107
+ import path from "path";
108
+ import { fileURLToPath } from "url";
109
+ import { randomUUID as randomUUID2 } from "crypto";
110
+
111
+ // src/constants.js
112
+ var CAD_PLATFORMS = ["AutoCAD", "ZWCAD", "GStarCAD", "BricsCAD"];
113
+ var FILE_EXTENSIONS = {
114
+ "AutoCAD": [".fas", ".vlx"],
115
+ "ZWCAD": [".zelx", ".vls"],
116
+ "GStarCAD": [".fas", ".vlx"],
117
+ "BricsCAD": [".des"]
118
+ };
119
+ var SERVER_NAME = "atlisp-mcp-server";
120
+ var SERVER_VERSION = "1.1.2";
121
+ var MOCK_PACKAGES = [
122
+ { name: "base", description: "\u57FA\u7840\u51FD\u6570\u5E93", version: "1.0.0" },
123
+ { name: "at-pm", description: "\u5DE5\u7A0B\u9879\u76EE\u7BA1\u7406", version: "2.1.0" },
124
+ { name: "network", description: "\u7F51\u7EDC\u529F\u80FD\u6A21\u5757", version: "1.5.0" },
125
+ { name: "userman", description: "\u7528\u6237\u7BA1\u7406", version: "1.2.0" },
126
+ { name: "pkgman", description: "\u5305\u7BA1\u7406\u5668", version: "2.0.0" },
127
+ { name: "sidebar", description: "\u4FA7\u8FB9\u680F\u5DE5\u5177", version: "1.0.0" },
128
+ { name: "aibot", description: "AI \u52A9\u624B", version: "0.9.0" },
129
+ { name: "tips", description: "\u6BCF\u65E5\u63D0\u793A", version: "1.0.0" },
130
+ { name: "function", description: "\u51FD\u6570\u5E93", version: "3.0.0" }
131
+ ];
132
+
133
+ // src/config.js
134
+ function parseArgs() {
135
+ const args = process.argv.slice(2);
136
+ const result = { transport: "http", port: "8110", host: "0.0.0.0" };
137
+ for (let i = 0; i < args.length; i++) {
138
+ if (args[i] === "--transport" && args[i + 1]) {
139
+ result.transport = args[i + 1];
140
+ i++;
141
+ } else if (args[i] === "--stdio") {
142
+ result.transport = "stdio";
143
+ } else if (args[i] === "--port" && args[i + 1]) {
144
+ result.port = args[i + 1];
145
+ i++;
146
+ } else if (args[i] === "--host" && args[i + 1]) {
147
+ result.host = args[i + 1];
148
+ i++;
149
+ }
150
+ }
151
+ return result;
152
+ }
153
+ var cliArgs = parseArgs();
154
+ var config = {
155
+ port: parseInt(process.env.PORT || cliArgs.port, 10),
156
+ host: process.env.HOST || cliArgs.host,
157
+ transport: process.env.TRANSPORT || cliArgs.transport,
158
+ apiKey: process.env.MCP_API_KEY || "",
159
+ enableSse: process.env.ENABLE_SSE !== "false",
160
+ messageTimeout: parseInt(process.env.MESSAGE_TIMEOUT || "60000", 10),
161
+ heartbeatInterval: parseInt(process.env.HEARTBEAT_INTERVAL || "30000", 10),
162
+ busyRetries: parseInt(process.env.BUSY_RETRIES || "10", 10),
163
+ busyDelay: parseInt(process.env.BUSY_DELAY || "500", 10),
164
+ enableCors: process.env.ENABLE_CORS !== "false",
165
+ corsOrigin: process.env.CORS_ORIGIN || "*",
166
+ rateLimitWindow: parseInt(process.env.RATE_LIMIT_WINDOW || "60000", 10),
167
+ rateLimitMax: parseInt(process.env.RATE_LIMIT_MAX || "100", 10),
168
+ resourceCacheTtl: parseInt(process.env.RESOURCE_CACHE_TTL || "5000", 10),
169
+ debug: process.env.DEBUG === "true",
170
+ debugFile: process.env.DEBUG_FILE || ""
171
+ };
172
+ var config_default = config;
173
+
174
+ // src/cad.js
175
+ var MESSAGE_TIMEOUT = config_default.messageTimeout;
176
+ var HEARTBEAT_INTERVAL = config_default.heartbeatInterval;
177
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
178
+ var workerPath = path.join(__dirname, "cad-worker.js");
179
+ var worker = null;
180
+ var _platform = null;
181
+ var heartbeatTimer = null;
182
+ var pendingRequests = /* @__PURE__ */ new Map();
183
+ var stdoutHandlerSetup = false;
184
+ var MAX_BUFFER_SIZE = 1024 * 1024;
185
+ function resetWorker() {
186
+ for (const [id, { reject, timeout }] of pendingRequests) {
187
+ clearTimeout(timeout);
188
+ reject(new Error("Worker reset"));
189
+ }
190
+ pendingRequests.clear();
191
+ stdoutHandlerSetup = false;
192
+ if (worker) {
193
+ worker.kill();
194
+ worker = null;
195
+ }
196
+ if (heartbeatTimer) {
197
+ clearInterval(heartbeatTimer);
198
+ heartbeatTimer = null;
199
+ }
200
+ }
201
+ function setupStdoutHandler(w) {
202
+ let buffer = "";
203
+ stdoutHandlerSetup = true;
204
+ w.stdout.on("data", (data) => {
205
+ try {
206
+ buffer += data.toString();
207
+ if (buffer.length > MAX_BUFFER_SIZE) {
208
+ buffer = buffer.slice(-MAX_BUFFER_SIZE);
209
+ }
210
+ const lines = buffer.split("\n");
211
+ buffer = lines.pop() || "";
212
+ for (const line of lines) {
213
+ if (!line.trim()) continue;
214
+ try {
215
+ const result = JSON.parse(line);
216
+ const rid = result.requestId;
217
+ if (rid && pendingRequests.has(rid)) {
218
+ const { resolve, reject, timeout } = pendingRequests.get(rid);
219
+ clearTimeout(timeout);
220
+ pendingRequests.delete(rid);
221
+ if (result.error) {
222
+ reject(new Error(result.error));
223
+ } else {
224
+ resolve(result);
225
+ }
226
+ }
227
+ } catch (parseErr) {
228
+ console.error("JSON parse error in worker output:", parseErr.message);
229
+ }
230
+ }
231
+ } catch (e) {
232
+ console.error("Unexpected error in worker stdout handler:", e.message);
233
+ }
234
+ });
235
+ }
236
+ function getWorker() {
237
+ if (!worker || !worker.connected) {
238
+ if (worker) worker.kill();
239
+ worker = spawn("node", [workerPath], {
240
+ stdio: ["pipe", "pipe", "pipe"]
241
+ });
242
+ worker.connected = true;
243
+ worker.stderr.on("data", (d) => console.error("worker stderr:", d.toString()));
244
+ const w = worker;
245
+ w.on("exit", (code) => {
246
+ console.error("worker exited:", code);
247
+ if (w !== worker) return;
248
+ w.connected = false;
249
+ for (const [id, { reject, timeout }] of pendingRequests) {
250
+ clearTimeout(timeout);
251
+ reject(new Error(`Worker exited with code ${code}`));
252
+ }
253
+ pendingRequests.clear();
254
+ stdoutHandlerSetup = false;
255
+ if (heartbeatTimer) {
256
+ clearInterval(heartbeatTimer);
257
+ heartbeatTimer = null;
258
+ }
259
+ });
260
+ setupStdoutHandler(w);
261
+ startHeartbeat();
262
+ }
263
+ return worker;
264
+ }
265
+ function startHeartbeat() {
266
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
267
+ heartbeatTimer = setInterval(async () => {
268
+ try {
269
+ const result = await sendMessage({ type: "ping" });
270
+ if (!result?.pong) {
271
+ console.error("heartbeat failed, worker may be dead");
272
+ resetWorker();
273
+ }
274
+ } catch (e) {
275
+ console.error("heartbeat error:", e.message);
276
+ resetWorker();
277
+ }
278
+ }, HEARTBEAT_INTERVAL);
279
+ }
280
+ function sendMessage(msg) {
281
+ const w = getWorker();
282
+ const requestId = randomUUID2();
283
+ return new Promise((resolve, reject) => {
284
+ const timeout = setTimeout(() => {
285
+ const pending = pendingRequests.get(requestId);
286
+ if (pending) {
287
+ pendingRequests.delete(requestId);
288
+ reject(new Error("Timeout waiting for worker"));
289
+ }
290
+ }, MESSAGE_TIMEOUT);
291
+ pendingRequests.set(requestId, { resolve, reject, timeout });
292
+ try {
293
+ w.stdin.write(JSON.stringify({ ...msg, requestId }) + "\n");
294
+ } catch (e) {
295
+ clearTimeout(timeout);
296
+ pendingRequests.delete(requestId);
297
+ reject(new Error(`Failed to write to worker: ${e.message}`));
298
+ }
299
+ });
300
+ }
301
+ var CadConnection = class {
302
+ constructor() {
303
+ this.connected = false;
304
+ this.version = null;
305
+ this.product = null;
306
+ }
307
+ isAvailable() {
308
+ return process.platform === "win32";
309
+ }
310
+ async connect() {
311
+ if (!this.isAvailable()) return false;
312
+ try {
313
+ const result = await sendMessage({ type: "connect" });
314
+ if (result && result.success) {
315
+ this.version = result.version;
316
+ this.product = result.platform;
317
+ _platform = result.platform;
318
+ this.connected = true;
319
+ return true;
320
+ }
321
+ } catch (e) {
322
+ console.error("CAD connect error:", e.message);
323
+ }
324
+ return false;
325
+ }
326
+ async sendCommand(code) {
327
+ if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
328
+ const result = await sendMessage({ type: "send", code, platform: _platform });
329
+ if (result.success) return true;
330
+ throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
331
+ }
332
+ async sendCommandWithResult(code, encoding = null) {
333
+ if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
334
+ const result = await sendMessage({ type: "sendResult", code, platform: _platform, encoding: encoding || "" });
335
+ if (result.success) {
336
+ return result.result || "";
337
+ }
338
+ throw new Error(result.error || "\u53D1\u9001\u5931\u8D25");
339
+ }
340
+ getVersion() {
341
+ return this.version;
342
+ }
343
+ getPlatform() {
344
+ return this.product;
345
+ }
346
+ async isBusy() {
347
+ if (!this.connected) return false;
348
+ try {
349
+ const result = await sendMessage({ type: "isBusy", platform: _platform });
350
+ return result && result.isBusy;
351
+ } catch (e) {
352
+ return false;
353
+ }
354
+ }
355
+ async hasDoc() {
356
+ if (!this.connected) return { hasDoc: false, docCount: 0 };
357
+ try {
358
+ const result = await sendMessage({ type: "hasdoc", platform: _platform });
359
+ return result || { hasDoc: false, docCount: 0 };
360
+ } catch (e) {
361
+ return { hasDoc: false, docCount: 0 };
362
+ }
363
+ }
364
+ async newDoc() {
365
+ if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
366
+ try {
367
+ const result = await sendMessage({ type: "newdoc", platform: _platform });
368
+ return result && result.success;
369
+ } catch (e) {
370
+ return false;
371
+ }
372
+ }
373
+ async bringToFront() {
374
+ if (!this.connected) throw new Error("\u672A\u8FDE\u63A5 CAD");
375
+ try {
376
+ const result = await sendMessage({ type: "bringToFront", platform: _platform });
377
+ return result && result.success;
378
+ } catch (e) {
379
+ return false;
380
+ }
381
+ }
382
+ async getInfo() {
383
+ if (!this.connected) return "CAD \u672A\u8FDE\u63A5";
384
+ return `Platform: ${this.product}, Version: ${this.version}, Status: Connected`;
385
+ }
386
+ async disconnect() {
387
+ this.connected = false;
388
+ this.version = null;
389
+ this.product = null;
390
+ _platform = null;
391
+ resetWorker();
392
+ }
393
+ async ping() {
394
+ try {
395
+ const result = await sendMessage({ type: "ping" });
396
+ return result && result.pong;
397
+ } catch (e) {
398
+ return false;
399
+ }
400
+ }
401
+ _reset() {
402
+ this.connected = false;
403
+ this.version = null;
404
+ this.product = null;
405
+ _platform = null;
406
+ resetWorker();
407
+ }
408
+ };
409
+ var cad = new CadConnection();
410
+
411
+ // src/logger.js
412
+ import fs from "fs";
413
+ import path2 from "path";
414
+ import os from "os";
415
+ var DEBUG_FILE = process.env.DEBUG_FILE || path2.join(os.tmpdir(), "mcp-server-debug.log");
416
+ var stream = null;
417
+ function getStream() {
418
+ if (!stream) {
419
+ stream = fs.createWriteStream(DEBUG_FILE, { flags: "a" });
420
+ }
421
+ return stream;
422
+ }
423
+ function log(msg) {
424
+ const entry = `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
425
+ `;
426
+ getStream().write(entry);
427
+ }
428
+ function closeLog() {
429
+ if (stream) {
430
+ stream.end();
431
+ stream = null;
432
+ }
433
+ }
434
+
435
+ // src/handlers/cad-handlers.js
436
+ async function connectCad() {
437
+ log("connect_cad called, cad.connected before: " + cad.connected);
438
+ try {
439
+ log("calling cad.connect()...");
440
+ const connected = await cad.connect();
441
+ log("cad.connect() returned: " + connected + ", cad.connected: " + cad.connected);
442
+ if (connected) {
443
+ return { content: [{ type: "text", text: `\u5DF2\u8FDE\u63A5\u5230 ${cad.getPlatform()} ${cad.getVersion()}` }] };
444
+ }
445
+ } catch (e) {
446
+ log("connect_cad error: " + e.message);
447
+ }
448
+ return { content: [{ type: "text", text: "\u672A\u80FD\u8FDE\u63A5\u6216\u542F\u52A8 CAD\uFF0C\u8BF7\u786E\u4FDD CAD \u5DF2\u5B89\u88C5" }], isError: true };
449
+ }
450
+ async function evalLisp(code, withResult = false, encoding = null) {
451
+ if (!cad.connected) {
452
+ await cad.connect();
453
+ }
454
+ if (!cad.connected) {
455
+ return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
456
+ }
457
+ try {
458
+ if (withResult) {
459
+ const result = await cad.sendCommandWithResult(code, encoding);
460
+ if (result !== null) {
461
+ return { content: [{ type: "text", text: result }] };
462
+ }
463
+ return { content: [{ type: "text", text: "\u6267\u884C\u5931\u8D25\u6216\u65E0\u8FD4\u56DE\u503C" }], isError: true };
464
+ } else {
465
+ await cad.sendCommand("\x1B\x1B" + code + "\n");
466
+ return { content: [{ type: "text", text: `\u5DF2\u53D1\u9001\u547D\u4EE4: ${code}` }] };
467
+ }
468
+ } catch (e) {
469
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
470
+ }
471
+ }
472
+ async function getCadInfo() {
473
+ if (!cad.connected) {
474
+ await cad.connect();
475
+ }
476
+ if (!cad.connected) return { content: [{ type: "text", text: "CAD \u672A\u8FDE\u63A5" }] };
477
+ const info = await cad.getInfo();
478
+ return { content: [{ type: "text", text: info }] };
479
+ }
480
+ async function atCommand(command) {
481
+ if (!cad.connected) return { content: [{ type: "text", text: "\u8BF7\u5148\u8FDE\u63A5 CAD" }], isError: true };
482
+ await cad.sendCommand("\x1B\x1B" + command + "\n");
483
+ return { content: [{ type: "text", text: `\u5DF2\u53D1\u9001\u547D\u4EE4: ${command}` }] };
484
+ }
485
+ async function newDocument() {
486
+ if (!cad.connected) {
487
+ await cad.connect();
488
+ }
489
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
490
+ const result = await cad.newDoc();
491
+ if (result) {
492
+ return { content: [{ type: "text", text: "\u5DF2\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863" }] };
493
+ }
494
+ return { content: [{ type: "text", text: "\u65B0\u5EFA\u6587\u6863\u5931\u8D25" }], isError: true };
495
+ }
496
+ async function bringToFront() {
497
+ if (!cad.connected) {
498
+ await cad.connect();
499
+ }
500
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
501
+ const result = await cad.bringToFront();
502
+ if (result) {
503
+ return { content: [{ type: "text", text: "\u5DF2\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0" }] };
504
+ }
505
+ return { content: [{ type: "text", text: "\u5207\u6362\u524D\u53F0\u5931\u8D25" }], isError: true };
506
+ }
507
+ async function installAtlisp() {
508
+ if (!cad.connected) {
509
+ await cad.connect();
510
+ }
511
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
512
+ 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))))`;
513
+ await cad.sendCommand("\x1B\x1B" + installCode + "\n");
514
+ return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001 @lisp \u5B89\u88C5\u4EE3\u7801\u5230 CAD" }] };
515
+ }
516
+ async function listFunctionsInCad() {
517
+ if (!cad.connected) {
518
+ await cad.connect();
519
+ }
520
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
521
+ try {
522
+ const result = await cad.sendCommandWithResult("(atoms-family 0)", null);
523
+ if (result) return { content: [{ type: "text", text: result }] };
524
+ return { content: [{ type: "text", text: "CAD \u51FD\u6570\u5217\u8868\u4E3A\u7A7A" }] };
525
+ } catch (e) {
526
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
527
+ }
528
+ }
529
+ async function initAtlisp() {
530
+ if (!cad.connected) {
531
+ await cad.connect();
532
+ }
533
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
534
+ const code = `(if (null @::load-module)
535
+ (progn
536
+ (vl-load-com)
537
+ (setq s strcat h"http"o(vlax-create-object (s"win"h".win"h"request.5.1"))
538
+ v vlax-invoke e eval r read)(v o'open "get" (s h"://""atlisp.""cn/cloud"):vlax-true)
539
+ (v o'send)
540
+ (v o'WaitforResponse 1000)
541
+ (e(r(vlax-get o'ResponseText)))))`;
542
+ try {
543
+ await cad.sendCommand("\x1B\x1B" + code + "\n");
544
+ return { content: [{ type: "text", text: "\u5DF2\u53D1\u9001 @lisp \u51FD\u6570\u5E93\u52A0\u8F7D\u4EE3\u7801\u5230 CAD" }] };
545
+ } catch (e) {
546
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
547
+ }
548
+ }
549
+
550
+ // src/handlers/package-handlers.js
551
+ var PACKAGES_LIST_URL = process.env.PACKAGES_LIST_URL || "http://s3.atlisp.cn/packages-list?fmt=json";
552
+ var cachedPackages = null;
553
+ var cachedAt = null;
554
+ var CACHE_TTL = 10 * 60 * 1e3;
555
+ async function fetchPackages() {
556
+ const now = Date.now();
557
+ if (cachedPackages && cachedAt && now - cachedAt < CACHE_TTL) {
558
+ return cachedPackages;
559
+ }
560
+ try {
561
+ const response = await fetch(PACKAGES_LIST_URL, { headers: { "User-Agent": "atlisp-mcp" } });
562
+ if (response.ok) {
563
+ const data = await response.json();
564
+ cachedPackages = data;
565
+ cachedAt = now;
566
+ return data;
567
+ }
568
+ } catch (e) {
569
+ }
570
+ return null;
571
+ }
572
+ async function listPackages() {
573
+ if (!cad.connected) await cad.connect();
574
+ if (!cad.connected) return { content: [{ type: "text", text: "\u672A\u8FDE\u63A5 CAD" }], isError: true };
575
+ const result = await cad.sendCommandWithResult("(@::package-list-in-local)");
576
+ if (result && result !== "nil") return { content: [{ type: "text", text: result }] };
577
+ const packages = MOCK_PACKAGES.map((p) => `${p.name} v${p.version} - ${p.description}`);
578
+ return { content: [{ type: "text", text: `\u5DF2\u5B89\u88C5\u7684 @lisp \u5305:
579
+ ${packages.join("\n")}` }] };
580
+ }
581
+ async function searchPackages(query) {
582
+ const data = await fetchPackages();
583
+ if (!data) {
584
+ return { content: [{ type: "text", text: "\u65E0\u6CD5\u83B7\u53D6\u5305\u5217\u8868" }], isError: true };
585
+ }
586
+ const results = [];
587
+ const items = Array.isArray(data) ? data : data.packages || [];
588
+ if (query) {
589
+ const q = query.toLowerCase();
590
+ for (const p of items) {
591
+ const name = (p.name || "").toLowerCase();
592
+ const desc = (p.description || "").toLowerCase();
593
+ if (name.includes(q) || desc.includes(q)) results.push(p);
594
+ }
595
+ } else {
596
+ for (const p of items) results.push(p);
597
+ }
598
+ if (results.length === 0) {
599
+ return { content: [{ type: "text", text: `\u672A\u627E\u5230\u5305\u542B "${query}" \u7684\u5305` }] };
600
+ }
601
+ return { content: [{ type: "text", text: `\u641C\u7D22\u7ED3\u679C (${results.length}):
602
+ ${results.map((p) => `${p.name} v${p.version || ""} - ${p.description || ""}`).join("\n")}` }] };
603
+ }
604
+ async function installPackage(packageName) {
605
+ const pkg = MOCK_PACKAGES.find((p) => p.name === packageName);
606
+ if (!pkg) {
607
+ return { content: [{ type: "text", text: `\u9519\u8BEF: \u5305 "${packageName}" \u4E0D\u5B58\u5728` }], isError: true };
608
+ }
609
+ if (cad.connected) {
610
+ await cad.sendCommand(`(@::load-module '${packageName})
611
+ `);
612
+ }
613
+ return { content: [{ type: "text", text: `\u5305 "${packageName}" \u5B89\u88C5\u547D\u4EE4\u5DF2\u53D1\u9001\uFF01` }] };
614
+ }
615
+
616
+ // src/handlers/function-handlers.js
617
+ import fs2 from "fs/promises";
618
+ import path3 from "path";
619
+ var FUNCTIONS_DIR = process.env.FUNCTIONS_DIR || "";
620
+ var FUNCTIONS_URL = process.env.FUNCTIONS_URL || "http://s3.atlisp.cn/json/functions.json";
621
+ async function fileExists(filePath) {
622
+ try {
623
+ await fs2.access(filePath);
624
+ return true;
625
+ } catch {
626
+ return false;
627
+ }
628
+ }
629
+ async function getFunctionUsage(funcName, packageName) {
630
+ try {
631
+ let results = [];
632
+ try {
633
+ const response = await fetch(FUNCTIONS_URL, { headers: { "User-Agent": "atlisp-mcp" } });
634
+ if (response.ok) {
635
+ const data = await response.json();
636
+ const funcs = data.functions || [];
637
+ const func = funcs.find((f2) => f2.name === funcName || f2.name === `${packageName}:${funcName}`);
638
+ if (func) results.push(func);
639
+ }
640
+ } catch (e) {
641
+ log(`function-handlers fetch error: ${e.message}`);
642
+ }
643
+ if (results.length === 0 && FUNCTIONS_DIR && await fileExists(FUNCTIONS_DIR)) {
644
+ const files = (await fs2.readdir(FUNCTIONS_DIR)).filter((f2) => f2.endsWith(".json"));
645
+ for (const file of files) {
646
+ const raw = await fs2.readFile(path3.join(FUNCTIONS_DIR, file), "utf-8");
647
+ const data = JSON.parse(raw);
648
+ const func = data.functions?.find((f2) => f2.name === funcName);
649
+ if (func) results.push(func);
650
+ }
651
+ }
652
+ if (results.length === 0) {
653
+ return { content: [{ type: "text", text: `\u672A\u627E\u5230\u51FD\u6570: ${funcName}` }], isError: true };
654
+ }
655
+ const f = results[0];
656
+ const text = `\u51FD\u6570: ${f.name}
657
+ \u63CF\u8FF0: ${f.description || "-"}
658
+ \u53C2\u6570: ${f.params?.join(", ") || "-"}
659
+ \u8FD4\u56DE\u503C: ${f.return || "-"}
660
+ \u793A\u4F8B: ${f.usage || "-"}`;
661
+ return { content: [{ type: "text", text }] };
662
+ } catch (e) {
663
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
664
+ }
665
+ }
666
+ async function listFunctions(packageName) {
667
+ try {
668
+ let allFuncs = [];
669
+ try {
670
+ const response = await fetch(FUNCTIONS_URL, { headers: { "User-Agent": "atlisp-mcp" } });
671
+ if (response.ok) {
672
+ const data = await response.json();
673
+ const funcs = data.functions || [];
674
+ if (packageName) {
675
+ allFuncs = funcs.filter((f) => f.name.startsWith(`${packageName}:`));
676
+ } else {
677
+ allFuncs = funcs.map((f) => `${f.name}: ${f.description || "-"}`);
678
+ }
679
+ }
680
+ } catch (e) {
681
+ log(`function-handlers list fetch error: ${e.message}`);
682
+ }
683
+ if (allFuncs.length === 0 && FUNCTIONS_DIR && await fileExists(FUNCTIONS_DIR)) {
684
+ const files = (await fs2.readdir(FUNCTIONS_DIR)).filter((f) => f.endsWith(".json"));
685
+ for (const file of files) {
686
+ try {
687
+ const raw = await fs2.readFile(path3.join(FUNCTIONS_DIR, file), "utf-8");
688
+ const data = JSON.parse(raw);
689
+ const funcs = data.functions || [];
690
+ if (packageName) {
691
+ allFuncs = funcs.filter((f) => f.name.startsWith(`${packageName}:`));
692
+ } else {
693
+ for (const f of funcs) {
694
+ allFuncs.push(`${f.name}: ${f.description || "-"}`);
695
+ }
696
+ }
697
+ } catch (err) {
698
+ log(`read file ${file} error: ${err.message}`);
699
+ }
700
+ }
701
+ }
702
+ if (allFuncs.length === 0) {
703
+ return await listFunctionsInCad();
704
+ }
705
+ if (packageName) {
706
+ return { content: [{ type: "text", text: `${packageName} \u5305\u51FD\u6570 (${allFuncs.length}):
707
+ ${allFuncs.map((f) => f.name).join("\n")}` }] };
708
+ }
709
+ return { content: [{ type: "text", text: allFuncs.join("\n") }] };
710
+ } catch (e) {
711
+ return { content: [{ type: "text", text: `\u9519\u8BEF: ${e.message}` }], isError: true };
712
+ }
713
+ }
714
+
715
+ // src/handlers/resource-handlers.js
716
+ var CACHE_TTL2 = config_default.resourceCacheTtl;
717
+ var resourceCache = /* @__PURE__ */ new Map();
718
+ function getCached(key) {
719
+ const entry = resourceCache.get(key);
720
+ if (entry && Date.now() - entry.timestamp < CACHE_TTL2) {
721
+ return entry.data;
722
+ }
723
+ resourceCache.delete(key);
724
+ return null;
725
+ }
726
+ function setCache(key, data) {
727
+ resourceCache.set(key, { data, timestamp: Date.now() });
728
+ }
729
+ function parseLispList(str) {
730
+ if (!str || typeof str !== "string") return null;
731
+ const trimmed = str.trim();
732
+ if (!trimmed.startsWith("(") || !trimmed.endsWith(")")) return null;
733
+ const content = trimmed.slice(1, -1).trim();
734
+ if (!content) return [];
735
+ const result = [];
736
+ let depth = 0;
737
+ let current = "";
738
+ let inString = false;
739
+ let escaped = false;
740
+ for (let i = 0; i < content.length; i++) {
741
+ const ch = content[i];
742
+ if (escaped) {
743
+ current += ch;
744
+ escaped = false;
745
+ continue;
746
+ }
747
+ if (ch === "\\") {
748
+ current += ch;
749
+ escaped = true;
750
+ continue;
751
+ }
752
+ if (ch === '"') {
753
+ current += ch;
754
+ inString = !inString;
755
+ continue;
756
+ }
757
+ if (inString) {
758
+ current += ch;
759
+ continue;
760
+ }
761
+ if (ch === "(" || ch === "[") {
762
+ current += ch;
763
+ depth++;
764
+ } else if (ch === ")" || ch === "]") {
765
+ current += ch;
766
+ depth--;
767
+ } else if (ch === " " && depth === 0) {
768
+ if (current.trim()) {
769
+ result.push(current.trim());
770
+ current = "";
771
+ }
772
+ continue;
773
+ }
774
+ current += ch;
775
+ }
776
+ if (current.trim()) result.push(current.trim());
777
+ return result.map((item) => {
778
+ const t = item.trim();
779
+ if (t === "nil") return null;
780
+ if (t === "t") return true;
781
+ if (t === "T") return true;
782
+ const num = Number(t);
783
+ if (!isNaN(num) && t !== "") return num;
784
+ if (t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
785
+ return t;
786
+ });
787
+ }
788
+ var RESOURCES = [
789
+ { uri: "atlisp://cad/info", name: "CAD Info", description: "CAD \u8FDE\u63A5\u4FE1\u606F", mimeType: "application/json", subscribable: true },
790
+ { uri: "atlisp://cad/layers", name: "Layers", description: "\u56FE\u5C42\u5217\u8868", mimeType: "application/json", subscribable: true },
791
+ { uri: "atlisp://cad/entities", name: "Entities", description: "\u5B9E\u4F53\u7EDF\u8BA1", mimeType: "application/json", subscribable: true },
792
+ { uri: "atlisp://dwg/name", name: "DWG Name", description: "\u5F53\u524D DWG \u6587\u4EF6\u540D", mimeType: "application/json", subscribable: true },
793
+ { uri: "atlisp://dwg/path", name: "DWG Path", description: "\u6587\u4EF6\u5B8C\u6574\u8DEF\u5F84", mimeType: "application/json", subscribable: true },
794
+ { uri: "atlisp://packages", name: "Packages", description: "\u5DF2\u5B89\u88C5\u5305\u5217\u8868", mimeType: "application/json", subscribable: true },
795
+ { uri: "atlisp://platforms", name: "Platforms", description: "\u652F\u6301\u7684\u5E73\u53F0\u4FE1\u606F", mimeType: "application/json", subscribable: false }
796
+ ];
797
+ function parseQuery(uri) {
798
+ const qIdx = uri.indexOf("?");
799
+ if (qIdx === -1) return { base: uri, params: {} };
800
+ const base = uri.substring(0, qIdx);
801
+ const params = {};
802
+ uri.substring(qIdx + 1).split("&").forEach((p) => {
803
+ const eqIdx = p.indexOf("=");
804
+ const k = eqIdx === -1 ? p : p.substring(0, eqIdx);
805
+ const v = eqIdx === -1 ? "" : p.substring(eqIdx + 1);
806
+ if (k) params[k] = decodeURIComponent(v);
807
+ });
808
+ return { base, params };
809
+ }
810
+ async function listResources(subscribedUris = []) {
811
+ return RESOURCES.map((r) => ({
812
+ uri: r.uri,
813
+ name: r.name,
814
+ description: r.description,
815
+ mimeType: r.mimeType,
816
+ subscribed: subscribedUris.includes(r.uri)
817
+ }));
818
+ }
819
+ async function readResource(uri) {
820
+ const { base, params } = parseQuery(uri);
821
+ switch (base) {
822
+ case "atlisp://cad/info":
823
+ return await getCadInfo2();
824
+ case "atlisp://cad/layers":
825
+ return await getLayers(params.name);
826
+ case "atlisp://cad/entities":
827
+ return await getEntities(params.type);
828
+ case "atlisp://dwg/name":
829
+ return await getDwgName();
830
+ case "atlisp://dwg/path":
831
+ return await getDwgPath();
832
+ case "atlisp://packages":
833
+ return await getPackages(params.name);
834
+ case "atlisp://platforms":
835
+ return getPlatforms();
836
+ default:
837
+ throw new Error(`\u8D44\u6E90\u4E0D\u5B58\u5728: ${uri}`);
838
+ }
839
+ }
840
+ async function getCadInfo2() {
841
+ const cached = getCached("cad:info");
842
+ if (cached) return cached;
843
+ if (!cad.connected) {
844
+ await cad.connect();
845
+ }
846
+ if (!cad.connected) {
847
+ const result2 = { connected: false, platform: null, version: null, busy: false };
848
+ setCache("cad:info", result2);
849
+ return result2;
850
+ }
851
+ let busy = false;
852
+ try {
853
+ busy = await cad.isBusy();
854
+ } catch (e) {
855
+ log(`Error checking CAD busy status: ${e.message}`);
856
+ }
857
+ const result = {
858
+ connected: true,
859
+ platform: cad.getPlatform(),
860
+ version: cad.getVersion(),
861
+ busy
862
+ };
863
+ setCache("cad:info", result);
864
+ return result;
865
+ }
866
+ async function getLayers(filterName = null) {
867
+ const cacheKey = filterName ? `layers:${filterName}` : "layers:all";
868
+ const cached = getCached(cacheKey);
869
+ if (cached) return cached;
870
+ if (!cad.connected) await cad.connect();
871
+ if (!cad.connected) return [];
872
+ try {
873
+ const code = filterName ? `(tblnext "LAYER" T)` : `(progn (setq res nil)(while (setq e (tblnext "LAYER" (null res)))(setq res (cons (list (cdr (assoc 2 e)) (cdr (assoc 62 e)) (cdr (assoc 70 e))) res))) (reverse res))`;
874
+ const result = await cad.sendCommandWithResult(code);
875
+ if (!result || result === "" || result === "nil") {
876
+ setCache(cacheKey, []);
877
+ return [];
878
+ }
879
+ let layers = [];
880
+ try {
881
+ layers = JSON.parse(result);
882
+ } catch (e) {
883
+ const parsed = parseLispList(result);
884
+ if (parsed) {
885
+ layers = parsed;
886
+ } else {
887
+ layers = [];
888
+ }
889
+ }
890
+ if (!Array.isArray(layers)) {
891
+ if (typeof layers === "number") {
892
+ layers = [[result, layers, 0]];
893
+ } else if (typeof layers === "string") {
894
+ layers = [[layers, 7, 0]];
895
+ }
896
+ }
897
+ const mapped = layers.map((l) => {
898
+ const name = Array.isArray(l) ? l[0] : l;
899
+ const color = Array.isArray(l) && l[1] !== void 0 ? l[1] : 7;
900
+ const flags = Array.isArray(l) && l[2] !== void 0 ? l[2] : 0;
901
+ return {
902
+ name: String(name),
903
+ color,
904
+ locked: (flags & 4) !== 0,
905
+ frozen: (flags & 1) !== 0
906
+ };
907
+ });
908
+ setCache(cacheKey, mapped);
909
+ return mapped;
910
+ } catch (e) {
911
+ setCache(cacheKey, []);
912
+ return [];
913
+ }
914
+ }
915
+ async function getEntities(filterType = null) {
916
+ const cacheKey = filterType ? `entities:${filterType}` : "entities:all";
917
+ const cached = getCached(cacheKey);
918
+ if (cached) return cached;
919
+ if (!cad.connected) await cad.connect();
920
+ if (!cad.connected) {
921
+ const result = { total: 0, byType: {} };
922
+ setCache(cacheKey, result);
923
+ return result;
924
+ }
925
+ try {
926
+ let code;
927
+ if (filterType) {
928
+ code = `(sslength (ssget "_X" (list (cons 0 "${filterType}"))))`;
929
+ const count = await cad.sendCommandWithResult(code);
930
+ const result = { type: filterType, count: parseInt(count) || 0 };
931
+ setCache(cacheKey, result);
932
+ return result;
933
+ } else {
934
+ code = `(progn(setq ss(ssget "_X"))(if ss(progn(setq elst(mapcar (quote(lambda(x)(cdr(assoc 0 (entget x))))) (pickset:to-list ss)))(stat:stat elst))))`;
935
+ const resultStr = await cad.sendCommandWithResult(code);
936
+ if (!resultStr || resultStr === "" || resultStr === "nil") {
937
+ const result2 = { total: 0, byType: {} };
938
+ setCache(cacheKey, result2);
939
+ return result2;
940
+ }
941
+ let statResult = [];
942
+ try {
943
+ statResult = JSON.parse(resultStr);
944
+ } catch (e) {
945
+ const parsed = parseLispList(resultStr);
946
+ if (parsed) {
947
+ statResult = parsed;
948
+ }
949
+ }
950
+ if (!Array.isArray(statResult)) {
951
+ const result2 = { total: 0, byType: {} };
952
+ setCache(cacheKey, result2);
953
+ return result2;
954
+ }
955
+ const byType = {};
956
+ statResult.forEach((item) => {
957
+ if (Array.isArray(item) && item.length >= 2) {
958
+ byType[item[0]] = item[1];
959
+ }
960
+ });
961
+ const total = Object.values(byType).reduce((sum, n) => sum + n, 0);
962
+ const result = { total, byType };
963
+ setCache(cacheKey, result);
964
+ return result;
965
+ }
966
+ } catch (e) {
967
+ const result = { total: 0, byType: {} };
968
+ setCache(cacheKey, result);
969
+ return result;
970
+ }
971
+ }
972
+ async function getDwgName() {
973
+ if (!cad.connected) await cad.connect();
974
+ if (!cad.connected) return { name: null };
975
+ try {
976
+ const code = `(vl-princ-to-string (getvar "dwgname"))`;
977
+ const name = await cad.sendCommandWithResult(code);
978
+ return { name: name || null };
979
+ } catch (e) {
980
+ return { name: null };
981
+ }
982
+ }
983
+ async function getDwgPath() {
984
+ if (!cad.connected) await cad.connect();
985
+ if (!cad.connected) return { path: null, name: null };
986
+ try {
987
+ const code = `(progn
988
+ (setq prefix (vl-princ-to-string (getvar "dwgprefix")))
989
+ (setq name (vl-princ-to-string (getvar "dwgname")))
990
+ (list (strcat prefix name) name)
991
+ )`;
992
+ const result = await cad.sendCommandWithResult(code);
993
+ if (!result || result === "nil" || result === "") {
994
+ return { path: null, name: null };
995
+ }
996
+ let parsed = null;
997
+ try {
998
+ parsed = JSON.parse(result);
999
+ } catch {
1000
+ }
1001
+ if (!parsed) {
1002
+ const listResult = parseLispList(result);
1003
+ if (listResult && listResult.length >= 2) parsed = listResult;
1004
+ }
1005
+ if (parsed && Array.isArray(parsed) && parsed.length >= 2) {
1006
+ return { path: String(parsed[0]), name: String(parsed[1]) };
1007
+ }
1008
+ return { path: result, name: result };
1009
+ } catch (e) {
1010
+ return { path: null, name: null };
1011
+ }
1012
+ }
1013
+ async function getPackages(filterName = null) {
1014
+ const packages = MOCK_PACKAGES.map((p) => ({
1015
+ name: p.name,
1016
+ version: p.version,
1017
+ description: p.description,
1018
+ loaded: true
1019
+ }));
1020
+ if (filterName) {
1021
+ const pkg = packages.find((p) => p.name === filterName);
1022
+ return pkg ? [pkg] : [];
1023
+ }
1024
+ return packages;
1025
+ }
1026
+ function getPlatforms() {
1027
+ return CAD_PLATFORMS.map((name) => ({
1028
+ name,
1029
+ extensions: FILE_EXTENSIONS[name] || []
1030
+ }));
1031
+ }
1032
+
1033
+ // src/handlers/prompt-handlers.js
1034
+ var PROMPTS = [
1035
+ {
1036
+ name: "draw-residential",
1037
+ description: "\u751F\u6210\u519C\u6751\u6C11\u5C45\u5E73\u9762\u5E03\u7F6E\u56FE",
1038
+ arguments: [
1039
+ { name: "width", description: "\u5EFA\u7B51\u5BBD\u5EA6 (mm)", required: true },
1040
+ { name: "depth", description: "\u5EFA\u7B51\u6DF1\u5EA6 (mm)", required: true },
1041
+ { name: "rooms", description: "\u623F\u95F4\u6570\u91CF", required: false }
1042
+ ]
1043
+ },
1044
+ {
1045
+ name: "draw-floor-plan",
1046
+ description: "\u751F\u6210\u5EFA\u7B51\u697C\u5C42\u5E73\u9762\u56FE",
1047
+ arguments: [
1048
+ { name: "floor", description: "\u697C\u5C42\u540D\u79F0", required: true },
1049
+ { name: "scale", description: "\u56FE\u7EB8\u6BD4\u4F8B", required: false }
1050
+ ]
1051
+ },
1052
+ {
1053
+ name: "analyze-drawing",
1054
+ description: "\u5206\u6790\u5F53\u524D\u56FE\u7EB8\u7EDF\u8BA1\u4FE1\u606F",
1055
+ arguments: []
1056
+ },
1057
+ {
1058
+ name: "batch-draw-lines",
1059
+ description: "\u6279\u91CF\u7ED8\u5236\u7EBF\u6BB5",
1060
+ arguments: [
1061
+ { name: "count", description: "\u7EBF\u6BB5\u6570\u91CF", required: true },
1062
+ { name: "length", description: "\u7EBF\u6BB5\u957F\u5EA6", required: false }
1063
+ ]
1064
+ },
1065
+ {
1066
+ name: "generate-dimension",
1067
+ description: "\u751F\u6210\u5C3A\u5BF8\u6807\u6CE8",
1068
+ arguments: [
1069
+ { name: "style", description: "\u6807\u6CE8\u6837\u5F0F (horizontal/vertical/aligned)", required: false }
1070
+ ]
1071
+ },
1072
+ {
1073
+ name: "export-entities",
1074
+ description: "\u5BFC\u51FA\u56FE\u7EB8\u5B9E\u4F53\u7EDF\u8BA1",
1075
+ arguments: [
1076
+ { name: "format", description: "\u5BFC\u51FA\u683C\u5F0F (json/list)", required: false }
1077
+ ]
1078
+ }
1079
+ ];
1080
+ async function listPrompts() {
1081
+ return PROMPTS;
1082
+ }
1083
+ async function getPrompt(name, arguments_ = {}) {
1084
+ switch (name) {
1085
+ case "draw-residential":
1086
+ return generateResidentialPrompt(arguments_);
1087
+ case "draw-floor-plan":
1088
+ return generateFloorPlanPrompt(arguments_);
1089
+ case "analyze-drawing":
1090
+ return generateAnalyzePrompt();
1091
+ case "batch-draw-lines":
1092
+ return generateBatchLinesPrompt(arguments_);
1093
+ case "generate-dimension":
1094
+ return generateDimensionPrompt(arguments_);
1095
+ case "export-entities":
1096
+ return generateExportPrompt(arguments_);
1097
+ default:
1098
+ throw new Error(`Unknown prompt: ${name}`);
1099
+ }
1100
+ }
1101
+ async function generateResidentialPrompt(args) {
1102
+ const { width = 15e3, depth = 12e3, rooms = 6 } = args;
1103
+ const code = `(defun c:draw-residential (/ old-layer p)
1104
+ (setq old-layer (getvar "clayer"))
1105
+ (setvar "clayer" "0")
1106
+ (setq p (list 0 0))
1107
+ (command "_.rectangle" p (list ${width} ${depth}))
1108
+ ${generateRoomLayout(width, depth, rooms)}
1109
+ (setvar "clayer" old-layer)
1110
+ (princ)
1111
+ )
1112
+ (c:draw-residential)`;
1113
+ return {
1114
+ messages: [{
1115
+ role: "user",
1116
+ content: {
1117
+ type: "text",
1118
+ text: `\u8BF7\u5728 CAD \u4E2D\u7ED8\u5236\u519C\u6751\u6C11\u5C45\u5E73\u9762\u56FE\uFF0C\u5C3A\u5BF8 ${width}x${depth}mm\uFF0C${rooms} \u4E2A\u623F\u95F4\u3002
1119
+
1120
+ \u751F\u6210\u7684 AutoLISP \u4EE3\u7801\uFF1A
1121
+ \`\`\`lisp
1122
+ ${code}
1123
+ \`\`\`
1124
+
1125
+ \u76F4\u63A5\u6267\u884C\u6B64\u4EE3\u7801\u5373\u53EF\u751F\u6210\u56FE\u7EB8\u3002`
1126
+ }
1127
+ }]
1128
+ };
1129
+ }
1130
+ function generateRoomLayout(width, depth, rooms) {
1131
+ if (rooms === 6) {
1132
+ return `(command "_.line" (list 5000 0) (list 5000 ${depth}) "")
1133
+ (command "_.line" (list 0 4000) (list ${width} 4000) "")
1134
+ (command "_.text" "j" "c" "2500,2000" 800 0 "\u5BA2\u5385")
1135
+ (command "_.text" "j" "c" "7500,6000" 800 0 "\u9910\u5385")
1136
+ (command "_.text" "j" "c" "12500,2000" 800 0 "\u4E3B\u5367")`;
1137
+ }
1138
+ return `(command "_.text" "j" "c" (list (/ ${width} 2) (/ ${depth} 2)) 800 0 "\u623F\u95F4")`;
1139
+ }
1140
+ async function generateFloorPlanPrompt(args) {
1141
+ const { floor = "\u4E00\u5C42", scale = "1:100" } = args;
1142
+ return {
1143
+ messages: [{
1144
+ role: "user",
1145
+ content: {
1146
+ type: "text",
1147
+ text: `\u751F\u6210 ${floor} \u5E73\u9762\u56FE\uFF0C\u6BD4\u4F8B ${scale}\u3002
1148
+
1149
+ \u63D0\u793A\uFF1A
1150
+ 1. \u4F7F\u7528 RECTANGLE \u547D\u4EE4\u7ED8\u5236\u5916\u5899
1151
+ 2. \u4F7F\u7528 LINE \u547D\u4EE4\u7ED8\u5236\u5185\u90E8\u9694\u5899
1152
+ 3. \u4F7F\u7528 TEXT \u547D\u4EE4\u6DFB\u52A0\u623F\u95F4\u6807\u6CE8
1153
+ 4. \u4F7F\u7528 DIMLINEAR \u6216 DIMALIGNED \u6DFB\u52A0\u5C3A\u5BF8\u6807\u6CE8
1154
+
1155
+ \u9700\u8981\u6211\u6267\u884C\u54EA\u4E9B\u64CD\u4F5C\uFF1F`
1156
+ }
1157
+ }]
1158
+ };
1159
+ }
1160
+ async function generateAnalyzePrompt() {
1161
+ let entities = { total: 0, byType: {} };
1162
+ let layers = [];
1163
+ try {
1164
+ if (cad.connected) {
1165
+ const entResult = await cad.sendCommandWithResult(
1166
+ `(progn(setq ss(ssget "_X"))(if ss(progn(setq elst(mapcar(quote(lambda(x)(cdr(assoc 0(entget x))))) (pickset:to-list ss)))(stat:stat elst))))`
1167
+ );
1168
+ if (entResult) {
1169
+ try {
1170
+ entities = JSON.parse(entResult);
1171
+ } catch {
1172
+ }
1173
+ }
1174
+ const layerResult = await cad.sendCommandWithResult(
1175
+ `(progn(setq res nil)(while(setq e(tblnext "LAYER"(null res)))(setq res(cons(cdr(assoc 2 e))res)))(reverse res))`
1176
+ );
1177
+ if (layerResult) {
1178
+ try {
1179
+ layers = JSON.parse(layerResult);
1180
+ } catch {
1181
+ }
1182
+ }
1183
+ }
1184
+ } catch (e) {
1185
+ }
1186
+ return {
1187
+ messages: [{
1188
+ role: "user",
1189
+ content: {
1190
+ type: "text",
1191
+ text: `\u56FE\u7EB8\u5206\u6790\u62A5\u544A\uFF1A
1192
+
1193
+ **\u5B9E\u4F53\u7EDF\u8BA1\uFF1A** ${entities.total} \u4E2A
1194
+ ${Object.entries(entities.byType).map(([t, c]) => `- ${t}: ${c}`).join("\n")}
1195
+
1196
+ **\u56FE\u5C42\u5217\u8868\uFF1A** ${layers.length} \u4E2A
1197
+ ${layers.slice(0, 10).join(", ")}${layers.length > 10 ? "..." : ""}
1198
+
1199
+ **\u5EFA\u8BAE\uFF1A**
1200
+ ${generateSuggestions(entities, layers)}`
1201
+ }
1202
+ }]
1203
+ };
1204
+ }
1205
+ function generateSuggestions(entities, layers) {
1206
+ const suggestions = [];
1207
+ if (entities.total > 1e3) suggestions.push("- \u56FE\u7EB8\u5B9E\u4F53\u8F83\u591A\uFF0C\u5EFA\u8BAE\u4F7F\u7528\u56FE\u5C42\u5206\u7C7B\u7BA1\u7406");
1208
+ if (layers.length > 20) suggestions.push("- \u56FE\u5C42\u8F83\u591A\uFF0C\u5EFA\u8BAE\u6E05\u7406\u672A\u4F7F\u7528\u7684\u56FE\u5C42");
1209
+ if (entities.byType.LINE && entities.byType.LINE > 500) suggestions.push("- \u7EBF\u6761\u8F83\u591A\uFF0C\u53EF\u8003\u8651\u4F7F\u7528 BLOCK \u51CF\u5C11\u5B9E\u4F53\u6570\u91CF");
1210
+ if (!entities.byType.DIMENSION) suggestions.push("- \u672A\u53D1\u73B0\u5C3A\u5BF8\u6807\u6CE8\uFF0C\u5EFA\u8BAE\u6DFB\u52A0\u6807\u6CE8");
1211
+ return suggestions.length ? suggestions.join("\n") : "- \u56FE\u7EB8\u7ED3\u6784\u826F\u597D";
1212
+ }
1213
+ async function generateBatchLinesPrompt(args) {
1214
+ const { count = 5, length = 1e3 } = args;
1215
+ const code = `(defun c:batch-lines (/ i)
1216
+ (setq i 0)
1217
+ (repeat ${count}
1218
+ (command "_.line" (list (* i ${length}) 0) (list (* (1+ i) ${length}) 0) "")
1219
+ (setq i (1+ i))
1220
+ )
1221
+ (princ)
1222
+ )
1223
+ (c:batch-lines)`;
1224
+ return {
1225
+ messages: [{
1226
+ role: "user",
1227
+ content: {
1228
+ type: "text",
1229
+ text: `\u6279\u91CF\u7ED8\u5236 ${count} \u6761\u957F\u5EA6\u4E3A ${length} \u7684\u6C34\u5E73\u7EBF\u6BB5\uFF1A
1230
+
1231
+ \`\`\`lisp
1232
+ ${code}
1233
+ \`\`\`
1234
+
1235
+ \u6B64\u4EE3\u7801\u5C06\u7ED8\u5236 ${count} \u6761\u9996\u5C3E\u76F8\u8FDE\u7684\u6C34\u5E73\u7EBF\u6BB5\u3002`
1236
+ }
1237
+ }]
1238
+ };
1239
+ }
1240
+ async function generateDimensionPrompt(args) {
1241
+ const { style = "aligned" } = args;
1242
+ let dimStyle = "aligned";
1243
+ let lispStyle = "DIMALIGNED";
1244
+ if (style === "horizontal") {
1245
+ dimStyle = "\u6C34\u5E73";
1246
+ lispStyle = "DIMLINEAR";
1247
+ }
1248
+ if (style === "vertical") {
1249
+ dimStyle = "\u5782\u76F4";
1250
+ lispStyle = "DIMLINEAR";
1251
+ }
1252
+ return {
1253
+ messages: [{
1254
+ role: "user",
1255
+ content: {
1256
+ type: "text",
1257
+ text: `\u751F\u6210 ${dimStyle} \u5C3A\u5BF8\u6807\u6CE8\u4EE3\u7801\uFF1A
1258
+
1259
+ \`\`\`lisp
1260
+ (command "_${lispStyle}" "0,0" "1000,0" "500,200" "")
1261
+ \`\`\`
1262
+
1263
+ \u53C2\u6570\u8BF4\u660E\uFF1A
1264
+ - \u8D77\u70B9: 0,0
1265
+ - \u7EC8\u70B9: 1000,0
1266
+ - \u6807\u6CE8\u4F4D\u7F6E: 500,200
1267
+
1268
+ \u6267\u884C\u540E\u5C06\u521B\u5EFA ${dimStyle} \u5C3A\u5BF8\u6807\u6CE8\u3002`
1269
+ }
1270
+ }]
1271
+ };
1272
+ }
1273
+ async function generateExportPrompt(args) {
1274
+ const { format = "json" } = args;
1275
+ const code = format === "json" ? `(progn
1276
+ (setq ss(ssget "_X"))
1277
+ (if ss
1278
+ (progn
1279
+ (setq elst(mapcar(quote(lambda(x)(list(cdr(assoc 0(entget x)))(vlax-get x 'ObjectName))))(pickset:to-list ss)))
1280
+ (stat:stat(mapcar(quote car)elst))
1281
+ )
1282
+ )
1283
+ )` : `(progn
1284
+ (princ "\\n\u5B9E\u4F53\u7EDF\u8BA1:\\n")
1285
+ (princ "\u7C7B\u578B \u6570\u91CF\\n")
1286
+ (princ "---------------------\\n")
1287
+ (setq ss(ssget "_X"))
1288
+ (if ss
1289
+ (progn
1290
+ (setq elst(mapcar(quote(lambda(x)(cdr(assoc 0(entget x))))) (pickset:to-list ss)))
1291
+ (foreach t '("LINE" "CIRCLE" "ARC" "TEXT" "MTEXT" "POLYLINE" "LWPOLYLINE" "INSERT")
1292
+ (setq cnt(length(member t elst)))
1293
+ (if(> cnt 0)(princ(strcat t " "(itoa cnt) "\\n")))
1294
+ )
1295
+ )
1296
+ )
1297
+ )`;
1298
+ return {
1299
+ messages: [{
1300
+ role: "user",
1301
+ content: {
1302
+ type: "text",
1303
+ text: `\u5BFC\u51FA\u5B9E\u4F53\u7EDF\u8BA1 (${format} \u683C\u5F0F)\uFF1A
1304
+
1305
+ \`\`\`lisp
1306
+ ${code}
1307
+ \`\`\`
1308
+
1309
+ \u6267\u884C\u540E\u5C06\u8F93\u51FA\u5B9E\u4F53\u7EDF\u8BA1\u4FE1\u606F\u3002`
1310
+ }
1311
+ }]
1312
+ };
1313
+ }
1314
+
1315
+ // src/session-manager.js
1316
+ var Session = class {
1317
+ constructor(clientId) {
1318
+ this.clientId = clientId;
1319
+ this.subscriptions = /* @__PURE__ */ new Set();
1320
+ this.createdAt = Date.now();
1321
+ this.connected = false;
1322
+ }
1323
+ };
1324
+ var SessionManager = class {
1325
+ #sessions = /* @__PURE__ */ new Map();
1326
+ createSession(clientId) {
1327
+ let session = this.#sessions.get(clientId);
1328
+ if (!session) {
1329
+ session = new Session(clientId);
1330
+ this.#sessions.set(clientId, session);
1331
+ }
1332
+ return session;
1333
+ }
1334
+ getSession(clientId) {
1335
+ return this.#sessions.get(clientId) || null;
1336
+ }
1337
+ hasSession(clientId) {
1338
+ return this.#sessions.has(clientId);
1339
+ }
1340
+ removeSession(clientId) {
1341
+ return this.#sessions.delete(clientId);
1342
+ }
1343
+ subscribe(clientId, uri) {
1344
+ const session = this.#sessions.get(clientId);
1345
+ if (session) {
1346
+ session.subscriptions.add(uri);
1347
+ return true;
1348
+ }
1349
+ return false;
1350
+ }
1351
+ unsubscribe(clientId, uri) {
1352
+ const session = this.#sessions.get(clientId);
1353
+ if (session) {
1354
+ return session.subscriptions.delete(uri);
1355
+ }
1356
+ return false;
1357
+ }
1358
+ getSubscriptions(clientId) {
1359
+ const session = this.#sessions.get(clientId);
1360
+ return session ? Array.from(session.subscriptions) : [];
1361
+ }
1362
+ isSubscribed(clientId, uri) {
1363
+ const session = this.#sessions.get(clientId);
1364
+ return session ? session.subscriptions.has(uri) : false;
1365
+ }
1366
+ getAllSubscribedUris() {
1367
+ const all = /* @__PURE__ */ new Set();
1368
+ for (const session of this.#sessions.values()) {
1369
+ for (const uri of session.subscriptions) {
1370
+ all.add(uri);
1371
+ }
1372
+ }
1373
+ return all;
1374
+ }
1375
+ list() {
1376
+ return Array.from(this.#sessions.entries()).map(([id, s]) => ({
1377
+ clientId: id,
1378
+ subscriptions: Array.from(s.subscriptions),
1379
+ createdAt: s.createdAt,
1380
+ connected: s.connected
1381
+ }));
1382
+ }
1383
+ count() {
1384
+ return this.#sessions.size;
1385
+ }
1386
+ clear() {
1387
+ this.#sessions.clear();
1388
+ }
1389
+ };
1390
+
1391
+ // src/subscription-manager.js
1392
+ var sessionManager = new SessionManager();
1393
+ var _notifyFn = null;
1394
+ function setNotify(fn) {
1395
+ _notifyFn = fn;
1396
+ }
1397
+ function subscribe(uri, sessionId) {
1398
+ if (sessionId) {
1399
+ return sessionManager.subscribe(sessionId, uri);
1400
+ }
1401
+ return true;
1402
+ }
1403
+ function unsubscribe(uri, sessionId) {
1404
+ if (sessionId) {
1405
+ return sessionManager.unsubscribe(sessionId, uri);
1406
+ }
1407
+ return true;
1408
+ }
1409
+ function list(sessionId) {
1410
+ if (sessionId) {
1411
+ return sessionManager.getSubscriptions(sessionId);
1412
+ }
1413
+ return Array.from(sessionManager.getAllSubscribedUris());
1414
+ }
1415
+ function isSubscribed(uri, sessionId) {
1416
+ if (sessionId) {
1417
+ return sessionManager.isSubscribed(sessionId, uri);
1418
+ }
1419
+ return sessionManager.getAllSubscribedUris().has(uri);
1420
+ }
1421
+ function notify(uri, data) {
1422
+ if (_notifyFn) {
1423
+ _notifyFn(uri, data);
1424
+ }
1425
+ }
1426
+
1427
+ // src/sse-session.js
1428
+ import { randomUUID as randomUUID3 } from "crypto";
1429
+ var DEFAULT_HEARTBEAT_INTERVAL = 3e4;
1430
+ var DEFAULT_RETRY_INTERVAL = 3e4;
1431
+ var MAX_SENT_MESSAGES = 1e3;
1432
+ var SSE_EVENTS = {
1433
+ MESSAGE: "message",
1434
+ ENDPOINT: "endpoint",
1435
+ PING: "ping",
1436
+ ERROR: "error",
1437
+ PROGRESS: "progress",
1438
+ CAPABILITIES: "capabilities"
1439
+ };
1440
+ var SseSession = class {
1441
+ #res;
1442
+ #clientId;
1443
+ #active = true;
1444
+ #heartbeatTimer = null;
1445
+ #lastEventId = 0;
1446
+ #heartbeatInterval;
1447
+ #retryInterval;
1448
+ #sessionData = {};
1449
+ #sentMessages = [];
1450
+ #backpressureBuffer = [];
1451
+ #drainHandler = null;
1452
+ constructor(res, options = {}) {
1453
+ this.#res = res;
1454
+ this.#clientId = options.clientId || randomUUID3();
1455
+ this.#heartbeatInterval = options.heartbeatInterval || DEFAULT_HEARTBEAT_INTERVAL;
1456
+ this.#retryInterval = options.retryInterval || DEFAULT_RETRY_INTERVAL;
1457
+ this.#sessionData = options.sessionData || {};
1458
+ if (options.resumeFromId && options.resumeFromId > 0) {
1459
+ this.#lastEventId = options.resumeFromId;
1460
+ }
1461
+ this._setupSseHeaders();
1462
+ this._sendRetry();
1463
+ this._setupDrain();
1464
+ }
1465
+ get clientId() {
1466
+ return this.#clientId;
1467
+ }
1468
+ get isActive() {
1469
+ return this.#active;
1470
+ }
1471
+ get lastEventId() {
1472
+ return this.#lastEventId;
1473
+ }
1474
+ get sessionData() {
1475
+ return this.#sessionData;
1476
+ }
1477
+ get sentMessages() {
1478
+ return [...this.#sentMessages];
1479
+ }
1480
+ getMessagesSince(eventId) {
1481
+ return this.#sentMessages.filter((m) => m.id > eventId);
1482
+ }
1483
+ setSessionData(key, value) {
1484
+ this.#sessionData[key] = value;
1485
+ }
1486
+ getSessionData(key) {
1487
+ return this.#sessionData[key];
1488
+ }
1489
+ get retryInterval() {
1490
+ return this.#retryInterval;
1491
+ }
1492
+ setRetryInterval(ms) {
1493
+ this.#retryInterval = ms;
1494
+ }
1495
+ _sendRetry() {
1496
+ this._write(`retry: ${this.#retryInterval}
1497
+
1498
+ `);
1499
+ }
1500
+ _setupSseHeaders() {
1501
+ this.#res.setHeader("Content-Type", "text/event-stream");
1502
+ this.#res.setHeader("Cache-Control", "no-cache");
1503
+ this.#res.setHeader("Connection", "keep-alive");
1504
+ this.#res.setHeader("Transfer-Encoding", "chunked");
1505
+ this.#res.setHeader("X-Accel-Buffering", "no");
1506
+ if (typeof this.#res.flush !== "function") {
1507
+ this.#res.flush = () => {
1508
+ };
1509
+ }
1510
+ }
1511
+ _formatEvent(event, data, id = null) {
1512
+ const lines = [];
1513
+ if (id !== null) {
1514
+ lines.push(`id: ${id}`);
1515
+ }
1516
+ lines.push(`event: ${event}`);
1517
+ if (data !== null && data !== void 0) {
1518
+ const jsonStr = JSON.stringify(data);
1519
+ lines.push(`data: ${jsonStr}`);
1520
+ } else {
1521
+ lines.push("data:");
1522
+ }
1523
+ return lines.join("\n") + "\n\n";
1524
+ }
1525
+ _setupDrain() {
1526
+ if (typeof this.#res.on !== "function") return;
1527
+ this.#drainHandler = () => {
1528
+ if (!this.#active) return;
1529
+ if (this.#backpressureBuffer.length > 0) {
1530
+ this._drainBuffer();
1531
+ }
1532
+ };
1533
+ this.#res.on("drain", this.#drainHandler);
1534
+ }
1535
+ _drainBuffer() {
1536
+ const batch = this.#backpressureBuffer.splice(0, 50);
1537
+ for (const item of batch) {
1538
+ const ok = this.#res.write(item);
1539
+ if (!ok) {
1540
+ this.#backpressureBuffer.unshift(...batch.slice(batch.indexOf(item)));
1541
+ return;
1542
+ }
1543
+ }
1544
+ this._flush();
1545
+ if (this.#backpressureBuffer.length > 0) {
1546
+ this._drainBuffer();
1547
+ }
1548
+ }
1549
+ _write(content) {
1550
+ if (!this.#active) return false;
1551
+ try {
1552
+ if (this.#backpressureBuffer.length > 0) {
1553
+ this.#backpressureBuffer.push(content);
1554
+ return true;
1555
+ }
1556
+ const result = this.#res.write(content);
1557
+ if (typeof this.#res.flush === "function") {
1558
+ this.#res.flush();
1559
+ }
1560
+ if (result === false) {
1561
+ this.#backpressureBuffer.push(content);
1562
+ return true;
1563
+ }
1564
+ return true;
1565
+ } catch (e) {
1566
+ this.#active = false;
1567
+ this.close();
1568
+ return false;
1569
+ }
1570
+ }
1571
+ _flush() {
1572
+ try {
1573
+ if (typeof this.#res.flush === "function") {
1574
+ this.#res.flush();
1575
+ }
1576
+ } catch (e) {
1577
+ }
1578
+ }
1579
+ flushBackpressure() {
1580
+ if (this.#backpressureBuffer.length > 0) {
1581
+ this._drainBuffer();
1582
+ }
1583
+ }
1584
+ sendEvent(event, data, id = null) {
1585
+ this.#lastEventId++;
1586
+ const eventId = id !== null ? id : this.#lastEventId;
1587
+ const formatted = this._formatEvent(event, data, eventId);
1588
+ this.#sentMessages.push({ event, data, id: eventId, timestamp: Date.now() });
1589
+ if (this.#sentMessages.length > MAX_SENT_MESSAGES) {
1590
+ this.#sentMessages.splice(0, this.#sentMessages.length - MAX_SENT_MESSAGES);
1591
+ }
1592
+ this._write(formatted);
1593
+ this._flush();
1594
+ return true;
1595
+ }
1596
+ sendMessage(data) {
1597
+ return this.sendEvent(SSE_EVENTS.MESSAGE, data);
1598
+ }
1599
+ sendEndpoint(path5) {
1600
+ return this.sendEvent(SSE_EVENTS.ENDPOINT, path5);
1601
+ }
1602
+ sendError(code, message, id = null) {
1603
+ return this.sendEvent(SSE_EVENTS.ERROR, { code, message }, id);
1604
+ }
1605
+ sendPing() {
1606
+ return this.sendEvent(SSE_EVENTS.PING, { timestamp: Date.now() });
1607
+ }
1608
+ sendProgress(current, total, message = "") {
1609
+ return this.sendEvent(SSE_EVENTS.PROGRESS, { current, total, message });
1610
+ }
1611
+ sendCapabilities(capabilities) {
1612
+ return this.sendEvent(SSE_EVENTS.CAPABILITIES, capabilities);
1613
+ }
1614
+ sendResponse(jsonrpcResponse, id = null) {
1615
+ const eventId = id !== null ? id : this.#lastEventId;
1616
+ return this.sendEvent(SSE_EVENTS.MESSAGE, jsonrpcResponse, eventId);
1617
+ }
1618
+ startHeartbeat(interval = null) {
1619
+ this.stopHeartbeat();
1620
+ const ms = interval || this.#heartbeatInterval;
1621
+ this.#heartbeatTimer = setInterval(() => {
1622
+ if (!this.#active) {
1623
+ this.stopHeartbeat();
1624
+ return;
1625
+ }
1626
+ if (!this.sendPing()) {
1627
+ this.stopHeartbeat();
1628
+ this.close();
1629
+ }
1630
+ }, ms);
1631
+ }
1632
+ stopHeartbeat() {
1633
+ if (this.#heartbeatTimer) {
1634
+ clearInterval(this.#heartbeatTimer);
1635
+ this.#heartbeatTimer = null;
1636
+ }
1637
+ }
1638
+ close() {
1639
+ this.#active = false;
1640
+ this.stopHeartbeat();
1641
+ if (this.#drainHandler) {
1642
+ this.#res.removeListener("drain", this.#drainHandler);
1643
+ this.#drainHandler = null;
1644
+ }
1645
+ this.#backpressureBuffer = [];
1646
+ try {
1647
+ this.#res.end();
1648
+ } catch (e) {
1649
+ }
1650
+ }
1651
+ };
1652
+
1653
+ // src/sse-session-manager.js
1654
+ var SseSessionManager = class {
1655
+ #sessions = /* @__PURE__ */ new Map();
1656
+ #eventHandlers = /* @__PURE__ */ new Map();
1657
+ constructor() {
1658
+ this.#sessions = /* @__PURE__ */ new Map();
1659
+ this.#eventHandlers = /* @__PURE__ */ new Map();
1660
+ }
1661
+ add(clientId, session) {
1662
+ if (!(session instanceof SseSession)) {
1663
+ throw new Error("session must be an SseSession instance");
1664
+ }
1665
+ this.#sessions.set(clientId, session);
1666
+ session.startHeartbeat();
1667
+ }
1668
+ remove(clientId) {
1669
+ const session = this.#sessions.get(clientId);
1670
+ if (session) {
1671
+ session.close();
1672
+ this.#sessions.delete(clientId);
1673
+ return true;
1674
+ }
1675
+ return false;
1676
+ }
1677
+ get(clientId) {
1678
+ return this.#sessions.get(clientId) || null;
1679
+ }
1680
+ has(clientId) {
1681
+ return this.#sessions.has(clientId);
1682
+ }
1683
+ list() {
1684
+ return Array.from(this.#sessions.keys()).map((id) => ({
1685
+ clientId: id,
1686
+ isActive: this.#sessions.get(id)?.isActive ?? false
1687
+ }));
1688
+ }
1689
+ listActive() {
1690
+ return this.list().filter((s) => s.isActive);
1691
+ }
1692
+ count() {
1693
+ return this.#sessions.size;
1694
+ }
1695
+ sendToClient(clientId, event, data, id = null) {
1696
+ const session = this.#sessions.get(clientId);
1697
+ if (!session || !session.isActive) {
1698
+ return false;
1699
+ }
1700
+ return session.sendEvent(event, data, id);
1701
+ }
1702
+ sendMessageToClient(clientId, data) {
1703
+ return this.sendToClient(clientId, SSE_EVENTS.MESSAGE, data);
1704
+ }
1705
+ sendErrorToClient(clientId, code, message, id = null) {
1706
+ return this.sendToClient(clientId, SSE_EVENTS.ERROR, { code, message }, id);
1707
+ }
1708
+ sendProgressToClient(clientId, current, total, message = "") {
1709
+ return this.sendToClient(clientId, SSE_EVENTS.PROGRESS, { current, total, message });
1710
+ }
1711
+ broadcast(event, data) {
1712
+ let successCount = 0;
1713
+ for (const [clientId, session] of this.#sessions) {
1714
+ if (session.isActive && session.sendEvent(event, data)) {
1715
+ successCount++;
1716
+ }
1717
+ }
1718
+ return successCount;
1719
+ }
1720
+ broadcastMessage(data) {
1721
+ return this.broadcast(SSE_EVENTS.MESSAGE, data);
1722
+ }
1723
+ broadcastError(code, message) {
1724
+ return this.broadcast(SSE_EVENTS.ERROR, { code, message });
1725
+ }
1726
+ on(event, handler) {
1727
+ if (!this.#eventHandlers.has(event)) {
1728
+ this.#eventHandlers.set(event, /* @__PURE__ */ new Set());
1729
+ }
1730
+ this.#eventHandlers.get(event).add(handler);
1731
+ }
1732
+ off(event, handler) {
1733
+ const handlers = this.#eventHandlers.get(event);
1734
+ if (handlers) {
1735
+ handlers.delete(handler);
1736
+ }
1737
+ }
1738
+ emit(event, data) {
1739
+ const handlers = this.#eventHandlers.get(event);
1740
+ if (handlers) {
1741
+ for (const handler of handlers) {
1742
+ try {
1743
+ handler(data);
1744
+ } catch (e) {
1745
+ }
1746
+ }
1747
+ }
1748
+ }
1749
+ cleanupInactive() {
1750
+ for (const [clientId, session] of this.#sessions) {
1751
+ if (!session.isActive) {
1752
+ session.close();
1753
+ this.#sessions.delete(clientId);
1754
+ }
1755
+ }
1756
+ }
1757
+ closeAll() {
1758
+ for (const [clientId, session] of this.#sessions) {
1759
+ session.close();
1760
+ }
1761
+ this.#sessions.clear();
1762
+ }
1763
+ };
1764
+
1765
+ // src/atlisp-mcp.js
1766
+ var __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
1767
+ var require2 = createRequire(import.meta.url);
1768
+ var { version } = require2(path4.join(__dirname2, "..", "package.json"));
1769
+ var isMcpScript = process.argv[1]?.includes("atlisp-mcp");
1770
+ if (isMcpScript) {
1771
+ const args = process.argv.slice(2);
1772
+ for (let i = 0; i < args.length; i++) {
1773
+ if (args[i] === "--version" || args[i] === "-v") {
1774
+ console.log(`atlisp-mcp v${version}`);
1775
+ process.exit(0);
1776
+ } else if (args[i] === "--help" || args[i] === "-h") {
1777
+ console.log(`
1778
+ @lisp MCP Server v${version}
1779
+
1780
+ Usage: atlisp-mcp [options]
1781
+
1782
+ Options:
1783
+ -v, --version Show version number
1784
+ --transport <type> Transport type: http or stdio (default: http)
1785
+ --port <port> HTTP port (default: 8110)
1786
+ --host <host> HTTP host (default: 0.0.0.0)
1787
+ --stdio Shorthand for --transport stdio
1788
+ -h, --help Show this help message
1789
+
1790
+ Examples:
1791
+ atlisp-mcp # Start HTTP server on port 8110
1792
+ atlisp-mcp --port 3000 # Start HTTP server on port 3000
1793
+ atlisp-mcp --stdio # Start in stdio mode for MCP clients
1794
+ atlisp-mcp --transport stdio # Same as --stdio
1795
+ `);
1796
+ process.exit(0);
1797
+ } else if (args[i] === "--transport" && args[i + 1]) {
1798
+ process.env.TRANSPORT = args[i + 1];
1799
+ i++;
1800
+ } else if (args[i] === "--stdio") {
1801
+ process.env.TRANSPORT = "stdio";
1802
+ } else if (args[i] === "--port" && args[i + 1]) {
1803
+ process.env.PORT = args[i + 1];
1804
+ i++;
1805
+ } else if (args[i] === "--host" && args[i + 1]) {
1806
+ process.env.HOST = args[i + 1];
1807
+ i++;
1808
+ }
1809
+ }
1810
+ }
1811
+ var { decode: charsetDecode, encodingExists } = iconv;
1812
+ var mcpContext = new AsyncLocalStorage();
1813
+ var sseSessionManager = new SseSessionManager();
1814
+ var SERVER_CAPABILITIES = {
1815
+ tools: {},
1816
+ resources: { subscribe: true, listChanged: true },
1817
+ prompts: {}
1818
+ };
1819
+ var { port, host, transport, apiKey, rateLimitWindow, rateLimitMax } = config_default;
1820
+ if (apiKey) log("API Key authentication enabled");
1821
+ var mcpLimiter = rateLimit({
1822
+ windowMs: rateLimitWindow,
1823
+ max: rateLimitMax,
1824
+ standardHeaders: true,
1825
+ legacyHeaders: false,
1826
+ message: { error: "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5" }
1827
+ });
1828
+ var FUNCTION_LIB_URL = process.env.FUNCTION_LIB_URL || "http://s3.atlisp.cn/json/functions.json";
1829
+ var cachedFunctionLib = null;
1830
+ var cachedAt2 = null;
1831
+ var CACHE_TTL3 = 5 * 60 * 1e3;
1832
+ async function loadAtlibFunctionLib() {
1833
+ const now = Date.now();
1834
+ if (cachedFunctionLib && cachedAt2 && now - cachedAt2 < CACHE_TTL3) {
1835
+ return cachedFunctionLib;
1836
+ }
1837
+ try {
1838
+ const response = await fetch(FUNCTION_LIB_URL, { headers: { "User-Agent": "atlisp-mcp" } });
1839
+ if (response.ok) {
1840
+ const data = await response.json();
1841
+ cachedFunctionLib = data;
1842
+ cachedAt2 = now;
1843
+ return data;
1844
+ }
1845
+ } catch (e) {
1846
+ log(`loadAtlibFunctionLib error: ${e.message}`);
1847
+ }
1848
+ return null;
1849
+ }
1850
+ var TOOL_HANDLERS = {
1851
+ connect_cad: () => connectCad(),
1852
+ eval_lisp: (a) => evalLisp(a.code),
1853
+ eval_lisp_with_result: (a) => evalLisp(a.code, true, a.encoding),
1854
+ get_cad_info: () => getCadInfo(),
1855
+ list_packages: () => listPackages(),
1856
+ search_packages: (a) => searchPackages(a.query),
1857
+ install_package: (a) => installPackage(a.packageName),
1858
+ get_platform_info: () => ({ content: [{ type: "text", text: `\u652F\u6301\u7684 CAD \u5E73\u53F0:
1859
+ ${CAD_PLATFORMS.join("\n")}
1860
+ \u5F53\u524D\u8FDE\u63A5: ${cad.connected ? "\u5DF2\u8FDE\u63A5" : "\u672A\u8FDE\u63A5"}` }] }),
1861
+ at_command: (a) => atCommand(a.command),
1862
+ new_document: () => newDocument(),
1863
+ bring_to_front: () => bringToFront(),
1864
+ install_atlisp: () => installAtlisp(),
1865
+ init_atlisp: () => initAtlisp(),
1866
+ get_function_usage: (a) => getFunctionUsage(a.name, a.package),
1867
+ list_symbols: (a) => listFunctions(a.package),
1868
+ import_funlib: async (a) => {
1869
+ const data = await loadAtlibFunctionLib();
1870
+ if (!data) return { content: [{ type: "text", text: "\u52A0\u8F7D\u51FD\u6570\u5E93\u5931\u8D25" }], isError: true };
1871
+ if (a.format === "list") {
1872
+ const items = data.functions?.map((f) => `${f.name}: ${f.description || ""}`) || [];
1873
+ return { content: [{ type: "text", text: items.join("\n") }] };
1874
+ }
1875
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1876
+ }
1877
+ };
1878
+ function getOrCreateSessionServer(sessionId) {
1879
+ let entry = sessionServers.get(sessionId);
1880
+ if (!entry) {
1881
+ const server = createMcpServer();
1882
+ const transport2 = new SessionTransport(sessionId);
1883
+ server.connect(transport2);
1884
+ entry = { server, transport: transport2 };
1885
+ sessionServers.set(sessionId, entry);
1886
+ sessionTranports.set(sessionId, transport2);
1887
+ }
1888
+ return entry;
1889
+ }
1890
+ function removeSessionServer(sessionId) {
1891
+ const entry = sessionServers.get(sessionId);
1892
+ if (entry) {
1893
+ entry.transport.close().catch(() => {
1894
+ });
1895
+ entry.server.close().catch(() => {
1896
+ });
1897
+ sessionServers.delete(sessionId);
1898
+ sessionTranports.delete(sessionId);
1899
+ }
1900
+ }
1901
+ function broadcastToAllSessions(uri) {
1902
+ for (const [sid, entry] of sessionServers) {
1903
+ entry.server.sendResourceUpdated({ uri }).catch((err) => {
1904
+ log(`Failed to send resource update to session ${sid}: ${err.message}`);
1905
+ });
1906
+ }
1907
+ }
1908
+ var tools = [
1909
+ {
1910
+ name: "connect_cad",
1911
+ description: "\u8FDE\u63A5\u5230 CAD (AutoCAD/ZWCAD/GStarCAD/BricsCAD)",
1912
+ inputSchema: { type: "object", properties: {} }
1913
+ },
1914
+ {
1915
+ name: "eval_lisp",
1916
+ description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\uFF08\u4E0D\u8FD4\u56DE\u7ED3\u679C\uFF09",
1917
+ inputSchema: {
1918
+ type: "object",
1919
+ properties: { code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" } },
1920
+ required: ["code"]
1921
+ }
1922
+ },
1923
+ {
1924
+ name: "eval_lisp_with_result",
1925
+ description: "\u5728 CAD \u4E2D\u6267\u884C AutoLISP \u4EE3\u7801\u5E76\u8FD4\u56DE\u7ED3\u679C",
1926
+ inputSchema: {
1927
+ type: "object",
1928
+ properties: {
1929
+ code: { type: "string", description: "\u8981\u6267\u884C\u7684 LISP \u4EE3\u7801" },
1930
+ 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" }
1931
+ },
1932
+ required: ["code"]
1933
+ }
1934
+ },
1935
+ {
1936
+ name: "get_cad_info",
1937
+ description: "\u83B7\u53D6\u5F53\u524D CAD \u4FE1\u606F",
1938
+ inputSchema: { type: "object", properties: {} }
1939
+ },
1940
+ {
1941
+ name: "list_packages",
1942
+ description: "\u5217\u51FA\u5DF2\u5B89\u88C5\u7684 @lisp \u5305",
1943
+ inputSchema: { type: "object", properties: {} }
1944
+ },
1945
+ {
1946
+ name: "search_packages",
1947
+ description: "\u641C\u7D22 @lisp \u5305",
1948
+ inputSchema: {
1949
+ type: "object",
1950
+ properties: { query: { type: "string", description: "\u641C\u7D22\u5173\u952E\u8BCD" } },
1951
+ required: ["query"]
1952
+ }
1953
+ },
1954
+ {
1955
+ name: "install_package",
1956
+ description: "\u5B89\u88C5 @lisp \u5305\u5230 CAD",
1957
+ inputSchema: {
1958
+ type: "object",
1959
+ properties: { packageName: { type: "string", description: "\u5305\u540D\u79F0" } },
1960
+ required: ["packageName"]
1961
+ }
1962
+ },
1963
+ {
1964
+ name: "get_platform_info",
1965
+ description: "\u83B7\u53D6 CAD \u5E73\u53F0\u4FE1\u606F",
1966
+ inputSchema: { type: "object", properties: {} }
1967
+ },
1968
+ {
1969
+ name: "at_command",
1970
+ description: "\u6267\u884C @lisp \u547D\u4EE4",
1971
+ inputSchema: {
1972
+ type: "object",
1973
+ properties: { command: { type: "string", description: "@lisp \u547D\u4EE4" } },
1974
+ required: ["command"]
1975
+ }
1976
+ },
1977
+ {
1978
+ name: "new_document",
1979
+ description: "\u5728 CAD \u4E2D\u65B0\u5EFA\u7A7A\u767D\u6587\u6863",
1980
+ inputSchema: { type: "object", properties: {} }
1981
+ },
1982
+ {
1983
+ name: "bring_to_front",
1984
+ description: "\u5C06 CAD \u7A97\u53E3\u5207\u6362\u5230\u524D\u53F0\uFF08\u4ECE\u540E\u53F0\u8FD0\u884C\u72B6\u6001\u5524\u9192\uFF09",
1985
+ inputSchema: { type: "object", properties: {} }
1986
+ },
1987
+ {
1988
+ name: "install_atlisp",
1989
+ description: "\u5728 CAD \u4E2D\u5B89\u88C5 @lisp",
1990
+ inputSchema: { type: "object", properties: {} }
1991
+ },
1992
+ {
1993
+ name: "init_atlisp",
1994
+ description: "\u521D\u59CB\u5316 CAD \u4E2D\u7684 @lisp \u73AF\u5883\uFF0C\u52A0\u8F7D\u51FD\u6570\u5E93\u548C\u521D\u59CB\u53D8\u91CF",
1995
+ inputSchema: { type: "object", properties: {} }
1996
+ },
1997
+ {
1998
+ name: "get_function_usage",
1999
+ description: "\u83B7\u53D6 @lisp \u51FD\u6570\u7528\u6CD5",
2000
+ inputSchema: {
2001
+ type: "object",
2002
+ properties: {
2003
+ name: { type: "string", description: "\u51FD\u6570\u540D\uFF0C\u5982 string:length" },
2004
+ package: { type: "string", description: "\u5305\u540D\uFF0C\u5982 string\uFF08\u53EF\u9009\uFF0C\u4E0D\u4F20\u5219\u81EA\u52A8\u641C\u7D22\uFF09" }
2005
+ },
2006
+ required: ["name"]
2007
+ }
2008
+ },
2009
+ {
2010
+ name: "list_symbols",
2011
+ description: "\u5217\u51FA @lisp \u6240\u6709\u7B26\u53F7\uFF0C\u7528\u4E8E\u68C0\u67E5\u51FD\u6570\u548C\u53D8\u91CF",
2012
+ inputSchema: {
2013
+ type: "object",
2014
+ properties: {
2015
+ package: { type: "string", description: "\u5305\u540D\uFF0C\u5982 string\uFF08\u53EF\u9009\uFF09" }
2016
+ }
2017
+ }
2018
+ },
2019
+ {
2020
+ name: "import_funlib",
2021
+ description: "\u4ECE\u7F51\u7EDC\u5BFC\u5165 @lisp \u51FD\u6570\u5E93\u6570\u636E\u5230 AI Agent",
2022
+ inputSchema: {
2023
+ type: "object",
2024
+ properties: {
2025
+ 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" }
2026
+ }
2027
+ }
2028
+ }
2029
+ ];
2030
+ async function handleToolCall(name, args) {
2031
+ const handler = TOOL_HANDLERS[name];
2032
+ if (!handler) return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177: ${name}` }], isError: true };
2033
+ try {
2034
+ const result = await handler(args || {});
2035
+ notifyResourceChanges(name);
2036
+ return result;
2037
+ } catch (e) {
2038
+ log(`Tool call error [${name}]: ${e.message}`);
2039
+ return { content: [{ type: "text", text: `\u5DE5\u5177\u6267\u884C\u9519\u8BEF: ${e.message}` }], isError: true };
2040
+ }
2041
+ }
2042
+ function notifyResourceChanges(toolName) {
2043
+ const resourceMap = {
2044
+ connect_cad: ["atlisp://cad/info"],
2045
+ new_document: ["atlisp://dwg/name", "atlisp://dwg/path", "atlisp://cad/layers", "atlisp://cad/entities"],
2046
+ install_package: ["atlisp://packages"],
2047
+ install_atlisp: ["atlisp://packages"]
2048
+ };
2049
+ const uris = resourceMap[toolName];
2050
+ if (uris) {
2051
+ for (const uri of uris) {
2052
+ if (isSubscribed(uri)) {
2053
+ notify(uri, null);
2054
+ }
2055
+ }
2056
+ }
2057
+ }
2058
+ async function initCadConnection() {
2059
+ log("Attempting to connect to CAD on startup...");
2060
+ try {
2061
+ const connected = await cad.connect();
2062
+ if (connected) {
2063
+ log("Auto-connected to CAD: " + cad.getPlatform() + " " + cad.getVersion());
2064
+ console.error("\u5DF2\u81EA\u52A8\u8FDE\u63A5\u5230 CAD: " + cad.getPlatform() + " " + cad.getVersion());
2065
+ } else {
2066
+ log("No CAD found on startup");
2067
+ }
2068
+ } catch (e) {
2069
+ log("Auto-connect failed: " + e.message);
2070
+ }
2071
+ }
2072
+ var sessionServers = /* @__PURE__ */ new Map();
2073
+ var sessionTranports = /* @__PURE__ */ new Map();
2074
+ function createMcpServer() {
2075
+ const server = new Server(
2076
+ { name: SERVER_NAME, version: SERVER_VERSION },
2077
+ { capabilities: SERVER_CAPABILITIES }
2078
+ );
2079
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
2080
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2081
+ const { name, arguments: args } = request.params;
2082
+ return await handleToolCall(name, args || {});
2083
+ });
2084
+ server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
2085
+ const ctx = mcpContext.getStore();
2086
+ const sessionId = ctx?.sessionId;
2087
+ const subscribedUris = sessionId ? list(sessionId) : list();
2088
+ return {
2089
+ resources: await listResources(subscribedUris)
2090
+ };
2091
+ });
2092
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2093
+ const { uri } = request.params;
2094
+ try {
2095
+ const data = await readResource(uri);
2096
+ return {
2097
+ contents: [{
2098
+ uri,
2099
+ mimeType: "application/json",
2100
+ text: JSON.stringify(data)
2101
+ }]
2102
+ };
2103
+ } catch (e) {
2104
+ return {
2105
+ contents: [],
2106
+ isError: true,
2107
+ error: { code: -32603, message: e.message }
2108
+ };
2109
+ }
2110
+ });
2111
+ server.setRequestHandler(SubscribeRequestSchema, async (request) => {
2112
+ const { uri } = request.params;
2113
+ const ctx = mcpContext.getStore();
2114
+ subscribe(uri, ctx?.sessionId);
2115
+ return { success: true };
2116
+ });
2117
+ server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
2118
+ const { uri } = request.params;
2119
+ const ctx = mcpContext.getStore();
2120
+ unsubscribe(uri, ctx?.sessionId);
2121
+ return { success: true };
2122
+ });
2123
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
2124
+ prompts: await listPrompts()
2125
+ }));
2126
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
2127
+ const { name, arguments: args } = request.params;
2128
+ return await getPrompt(name, args || {});
2129
+ });
2130
+ return server;
2131
+ }
2132
+ async function startServer() {
2133
+ if (config_default.transport === "stdio") {
2134
+ await initCadConnection();
2135
+ const mcpServer = createMcpServer();
2136
+ const transport2 = new StdioServerTransport();
2137
+ const sessionId = "stdio-session";
2138
+ sessionManager.createSession(sessionId);
2139
+ await mcpContext.run({ sessionId }, async () => {
2140
+ await mcpServer.connect(transport2);
2141
+ });
2142
+ console.error(`${SERVER_NAME} \u8FD0\u884C\u5728 stdio \u6A21\u5F0F`);
2143
+ setNotify((uri, data) => {
2144
+ mcpServer.sendResourceUpdated({ uri }).catch((err) => {
2145
+ log(`Failed to send resource update: ${err.message}`);
2146
+ });
2147
+ });
2148
+ } else {
2149
+ const app = express();
2150
+ const JSON_CONTENT_TYPES = ["application/json", "application/json-rpc", "application/vnd.api+json"];
2151
+ app.use(async (req, res, next) => {
2152
+ const ct = (req.headers["content-type"] || "").toLowerCase();
2153
+ if (!JSON_CONTENT_TYPES.some((t) => ct.startsWith(t))) return next();
2154
+ try {
2155
+ const buf = await rawBody(req, { limit: "10mb" });
2156
+ const m = ct.match(/charset\s*=\s*([^\s;]+)/i);
2157
+ let charset = m ? m[1].toLowerCase() : "utf-8";
2158
+ const charsetMap = { "gb2312": "gbk", "gb18030": "gbk", "gbk": "gbk", "936": "gbk" };
2159
+ charset = charsetMap[charset] || charset;
2160
+ const str = encodingExists(charset) ? charsetDecode(buf, charset) : buf.toString("utf-8");
2161
+ req.body = JSON.parse(str);
2162
+ next();
2163
+ } catch (e) {
2164
+ res.status(400).json({ error: "Invalid JSON body", message: e.message });
2165
+ }
2166
+ });
2167
+ app.use((req, res, next) => {
2168
+ log(`${req.method} ${req.path}`);
2169
+ next();
2170
+ });
2171
+ const PUBLIC_PATHS = ["/health"];
2172
+ if (apiKey) {
2173
+ app.use((req, res, next) => {
2174
+ if (PUBLIC_PATHS.includes(req.path)) return next();
2175
+ const auth = req.get("Authorization");
2176
+ if (auth === `Bearer ${apiKey}`) return next();
2177
+ res.status(401).json({ error: "Unauthorized" });
2178
+ });
2179
+ }
2180
+ app.get("/health", (req, res) => {
2181
+ res.json({ status: "ok", cad: cad.connected ? "connected" : "disconnected" });
2182
+ });
2183
+ if (config_default.enableCors) {
2184
+ app.use((req, res, next) => {
2185
+ res.setHeader("Access-Control-Allow-Origin", config_default.corsOrigin);
2186
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, CONNECT");
2187
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, mcp-session-id, last-event-id");
2188
+ res.setHeader("Access-Control-Expose-Headers", "mcp-session-id, content-type, x-accel-buffering");
2189
+ res.setHeader("Access-Control-Max-Age", "86400");
2190
+ res.setHeader("Vary", "Accept");
2191
+ if (req.method === "OPTIONS") return res.status(204).end();
2192
+ next();
2193
+ });
2194
+ }
2195
+ setNotify((uri, data) => {
2196
+ broadcastToAllSessions(uri);
2197
+ for (const session of sseSessionManager.listActive()) {
2198
+ sseSessionManager.sendToClient(session.clientId, "resource_updated", { uri });
2199
+ }
2200
+ });
2201
+ async function handleMcpPost(req, res) {
2202
+ const sessionId = req.headers["mcp-session-id"] || req.query.sessionId || randomUUID4();
2203
+ sessionManager.createSession(sessionId);
2204
+ const body = req.body;
2205
+ if (!body || !body.method) {
2206
+ return res.status(400).json({ error: "Invalid request" });
2207
+ }
2208
+ const { server, transport: transport2 } = getOrCreateSessionServer(sessionId);
2209
+ transport2.resetCleanupTimer();
2210
+ await mcpContext.run({ sessionId }, async () => {
2211
+ try {
2212
+ const response = await transport2.handleRequest(body);
2213
+ if (response) {
2214
+ const headers = { "Content-Type": "application/json" };
2215
+ if (transport2.sessionId) {
2216
+ headers["mcp-session-id"] = transport2.sessionId;
2217
+ }
2218
+ res.writeHead(200, headers);
2219
+ res.end(JSON.stringify(response));
2220
+ } else {
2221
+ if (!res.headersSent) {
2222
+ res.status(202).end();
2223
+ }
2224
+ }
2225
+ } catch (error) {
2226
+ log(`Transport error [${sessionId}]: ${error.message}`);
2227
+ if (!res.headersSent) {
2228
+ res.status(500).json({ error: "Internal server error", message: error.message });
2229
+ }
2230
+ }
2231
+ });
2232
+ }
2233
+ app.all("/mcp", mcpLimiter, handleMcpPost);
2234
+ app.all("/mcp/:path", mcpLimiter, handleMcpPost);
2235
+ app.get("/mcp/stream", mcpLimiter, handleMcpPost);
2236
+ const sseEndpoint = "/sse";
2237
+ app.get(sseEndpoint, async (req, res) => {
2238
+ const sessionId = randomUUID4();
2239
+ sessionManager.createSession(sessionId);
2240
+ getOrCreateSessionServer(sessionId);
2241
+ const sseSession = new SseSession(res, { clientId: sessionId });
2242
+ sseSessionManager.add(sessionId, sseSession);
2243
+ sseSession.sendEndpoint(`/message?sessionId=${sessionId}`);
2244
+ req.on("close", () => {
2245
+ sseSessionManager.remove(sessionId);
2246
+ removeSessionServer(sessionId);
2247
+ });
2248
+ });
2249
+ app.post("/message", mcpLimiter, async (req, res) => {
2250
+ const sessionId = req.query.sessionId;
2251
+ if (!sessionId) {
2252
+ return res.status(400).json({ error: "Missing sessionId" });
2253
+ }
2254
+ const sseSession = sseSessionManager.get(sessionId);
2255
+ if (!sseSession) {
2256
+ return res.status(404).json({ error: "Session not found" });
2257
+ }
2258
+ sessionManager.createSession(sessionId);
2259
+ const { server, transport: transport2 } = getOrCreateSessionServer(sessionId);
2260
+ transport2.resetCleanupTimer();
2261
+ const body = req.body;
2262
+ if (body && body.method) {
2263
+ try {
2264
+ const response = await transport2.handleRequest(body);
2265
+ if (response) {
2266
+ sseSession.sendResponse(response);
2267
+ }
2268
+ if (!res.headersSent) {
2269
+ res.status(202).end();
2270
+ }
2271
+ } catch (error) {
2272
+ log(`SSE message error [${sessionId}]: ${error.message}`);
2273
+ if (!res.headersSent) {
2274
+ res.status(500).json({ error: "Internal server error" });
2275
+ }
2276
+ }
2277
+ } else {
2278
+ res.status(400).json({ error: "Invalid request" });
2279
+ }
2280
+ });
2281
+ const httpServer = app.listen(config_default.port, config_default.host, async () => {
2282
+ await initCadConnection();
2283
+ console.error(`@lisp MCP Server \u542F\u52A8 - http://${config_default.host}:${config_default.port} (\u591A\u4F1A\u8BDD\u6A21\u5F0F)`);
2284
+ });
2285
+ async function shutdown(signal) {
2286
+ console.error(`
2287
+ \u6536\u5230 ${signal}\uFF0C\u6B63\u5728\u4F18\u96C5\u5173\u95ED...`);
2288
+ sseSessionManager.closeAll();
2289
+ sessionManager.clear();
2290
+ for (const [sid] of sessionServers) {
2291
+ removeSessionServer(sid);
2292
+ }
2293
+ await cad.disconnect();
2294
+ closeLog();
2295
+ httpServer.close(() => process.exit(0));
2296
+ setTimeout(() => process.exit(1), 5e3);
2297
+ }
2298
+ process.on("SIGINT", () => shutdown("SIGINT"));
2299
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
2300
+ }
2301
+ }
2302
+ if (!process.env.VITEST) {
2303
+ await startServer();
2304
+ }
2305
+ export {
2306
+ CAD_PLATFORMS,
2307
+ FILE_EXTENSIONS,
2308
+ MOCK_PACKAGES,
2309
+ createMcpServer,
2310
+ handleToolCall,
2311
+ loadAtlibFunctionLib,
2312
+ startServer,
2313
+ tools
2314
+ };