@atlisp/mcp 1.8.16 → 1.8.17

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 (34) hide show
  1. package/dist/atlisp-mcp.js +7 -7
  2. package/dist/handlers/3d-handlers.js +128 -0
  3. package/dist/handlers/batch-handlers.js +260 -0
  4. package/dist/handlers/block-handlers.js +214 -0
  5. package/dist/handlers/cad-handlers.js +180 -0
  6. package/dist/handlers/constraint-handlers.js +130 -0
  7. package/dist/handlers/dim-hatch-handlers.js +340 -0
  8. package/dist/handlers/draw-handlers.js +175 -0
  9. package/dist/handlers/edit-handlers.js +179 -0
  10. package/dist/handlers/entity-handlers.js +196 -0
  11. package/dist/handlers/external-handlers.js +218 -0
  12. package/dist/handlers/file-handlers.js +172 -0
  13. package/dist/handlers/function-handlers.js +158 -0
  14. package/dist/handlers/layer-handlers.js +181 -0
  15. package/dist/handlers/package-handlers.js +89 -0
  16. package/dist/handlers/parametric-handlers.js +272 -0
  17. package/dist/handlers/pdf-annotation-handlers.js +117 -0
  18. package/dist/handlers/prompt-handlers.js +9 -0
  19. package/dist/handlers/purge-handlers.js +137 -0
  20. package/dist/handlers/query-print-handlers.js +662 -0
  21. package/dist/handlers/resource-cache.js +63 -0
  22. package/dist/handlers/resource-defs.js +79 -0
  23. package/dist/handlers/resource-handlers.js +131 -0
  24. package/dist/handlers/resource-readers.js +1045 -0
  25. package/dist/handlers/sheetset-handlers.js +430 -0
  26. package/dist/handlers/spatial-handlers.js +174 -0
  27. package/dist/handlers/style-handlers.js +162 -0
  28. package/dist/handlers/text-handlers.js +198 -0
  29. package/dist/handlers/ucs-layout-handlers.js +271 -0
  30. package/dist/handlers/viewport-handlers.js +150 -0
  31. package/dist/handlers/xdata-handlers.js +148 -0
  32. package/dist/handlers/xref-handlers.js +334 -0
  33. package/dist/lisp-security.js +7 -2
  34. package/package.json +2 -2
@@ -0,0 +1,63 @@
1
+ import config from '../config.js';
2
+
3
+ const CACHE_TTL = config.resourceCacheTtl;
4
+ const resourceCache = new Map();
5
+ const CLEANUP_INTERVAL = 60000;
6
+
7
+ let cleanupTimer = null;
8
+
9
+ function ensureCleanup() {
10
+ if (cleanupTimer) return;
11
+ cleanupTimer = setInterval(() => {
12
+ const now = Date.now();
13
+ for (const [key, entry] of resourceCache) {
14
+ if (now - entry.timestamp >= CACHE_TTL) {
15
+ resourceCache.delete(key);
16
+ }
17
+ }
18
+ }, CLEANUP_INTERVAL);
19
+ cleanupTimer.unref();
20
+ }
21
+
22
+ ensureCleanup();
23
+
24
+ // 每个资源 URI 对应的 cache key 前缀映射
25
+ const URI_CACHE_PREFIX = {
26
+ 'atlisp://cad/info': 'cad:info',
27
+ 'atlisp://dwg/entities': 'dwg:entities',
28
+ 'atlisp://dwg/text-content': 'dwg:text-content',
29
+ 'atlisp://dwg/name': 'dwg:name',
30
+ 'atlisp://dwg/path': 'dwg:path',
31
+ 'atlisp://dwg/block-refs': 'dwg:block-refs',
32
+ 'atlisp://dwg/tbl': 'dwg:tbl',
33
+ 'atlisp://cad/dwgs': 'cad:dwgs',
34
+ 'atlisp://packages': 'packages',
35
+ };
36
+
37
+ function getCached(key) {
38
+ const entry = resourceCache.get(key);
39
+ if (entry && Date.now() - entry.timestamp < CACHE_TTL) {
40
+ return entry.data;
41
+ }
42
+ resourceCache.delete(key);
43
+ return null;
44
+ }
45
+
46
+ function setCache(key, data) {
47
+ resourceCache.set(key, { data, timestamp: Date.now() });
48
+ }
49
+
50
+ export function clearCache(uri) {
51
+ const prefix = URI_CACHE_PREFIX[uri];
52
+ if (prefix) {
53
+ for (const key of resourceCache.keys()) {
54
+ if (key === prefix || key.startsWith(prefix + ':')) {
55
+ resourceCache.delete(key);
56
+ }
57
+ }
58
+ } else if (!uri) {
59
+ resourceCache.clear();
60
+ }
61
+ }
62
+
63
+ export { getCached, setCache };
@@ -0,0 +1,79 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+
6
+ export const STANDARDS_DIR = path.join(__dirname, '..', 'standards');
7
+
8
+ export const RESOURCES = [
9
+ { uri: 'atlisp://cad/info', name: 'CAD Info', description: 'CAD 连接信息', mimeType: 'application/json', subscribable: true },
10
+ { uri: 'atlisp://dwg/entities', name: 'Entities', description: '图元列表。无参数时返回总数和按类型统计;有参数时返回 filtered 图文的 entity:properties 全属性 JSON(ObjectName, Handle, Layer, Color, Center, Radius 等),支持 ?type=LINE&layer=0&bbox=0,0,100,100', mimeType: 'application/json', subscribable: true },
11
+ { uri: 'atlisp://dwg/text-content', name: 'Text Content', description: '文本内容及位置,用于语义分析。支持 ?layer=0&bbox=0,0,100,100&limit=5000&offset=0', mimeType: 'application/json', subscribable: true },
12
+ { uri: 'atlisp://dwg/text-combined', name: 'Combined Text', description: '碎片文本按位置组合为段落,跨行续句拼接。支持 ?layer=0&bbox=0,0,100,100', mimeType: 'application/json', subscribable: true },
13
+ { uri: 'atlisp://cad/events', name: 'CAD Events', description: 'CAD 变更事件通知 (实体创建/修改/删除, DWG 切换)', mimeType: 'application/json', subscribable: true },
14
+ { uri: 'atlisp://dwg/name', name: 'DWG Name', description: '当前 DWG 文件名', mimeType: 'application/json', subscribable: true },
15
+ { uri: 'atlisp://dwg/path', name: 'DWG Path', description: '文件完整路径', mimeType: 'application/json', subscribable: true },
16
+ { uri: 'atlisp://dwg/block-refs', name: 'Block References', description: '块参照列表及属性值、动态块特性,支持路径方式 atlisp://dwg/block-refs/{blockName}', mimeType: 'application/json', subscribable: true },
17
+ { uri: 'atlisp://dwg/tbl', name: 'Tables', description: 'CAD 表数据 (layer, block, textstyle, dimstyle, linetype, ucs, view, viewport, regapp),支持路径方式 atlisp://dwg/tbl/layer', mimeType: 'application/json', subscribable: true },
18
+ { uri: 'atlisp://cad/dwgs', name: 'DWG List', description: '所有打开的 DWG 文件列表', mimeType: 'application/json', subscribable: true },
19
+ { uri: 'atlisp://packages', name: 'Packages', description: '已安装包列表', mimeType: 'application/json', subscribable: true },
20
+ { uri: 'atlisp://platforms', name: 'Platforms', description: '支持的平台信息', mimeType: 'application/json', subscribable: false },
21
+ { uri: 'atlisp://standards/drafting', name: 'Drafting Standards', description: 'CAD 制图规范', mimeType: 'application/json', subscribable: false },
22
+ { uri: 'atlisp://standards/coding', name: 'Coding Standards', description: '@lisp 编码规范', mimeType: 'application/json', subscribable: false },
23
+ ];
24
+
25
+ export const TBL_INFO = {
26
+ layer: { name: '图层', fields: ['name', 'color', 'linetype', 'frozen', 'locked', 'on'] },
27
+ block: { name: '块定义', fields: ['name', 'path', 'flags'] },
28
+ textstyle: { name: '文字样式', fields: ['name', 'font', 'bigfont', 'height', 'width', 'oblique', 'flags'] },
29
+ dimstyle: { name: '标注样式', fields: ['name', 'arrow', 'textheight', 'scale'] },
30
+ linetype: { name: '线型', fields: ['name', 'description', 'pattern'] },
31
+ ucs: { name: 'UCS', fields: ['name', 'origin', 'xaxis', 'yaxis'] },
32
+ view: { name: '视图', fields: ['name', 'center', 'height', 'width'] },
33
+ viewport: { name: '视口', fields: ['name', 'number', 'flags'] },
34
+ regapp: { name: '注册应用', fields: ['name'] },
35
+ plotstyle: { name: '打印样式', fields: ['name'] },
36
+ };
37
+
38
+ export const TBL_QUERIES = {
39
+ layer: { name: 'layer', lisp: `(progn
40
+ (setq @d nil)
41
+ (setq @e (tblnext "layer" t))
42
+ (while @e
43
+ (setq @d (cons (list
44
+ (cons 2 (cdr (assoc 2 @e)))
45
+ (cons 62 (cdr (assoc 62 @e)))
46
+ (cons 6 (cdr (assoc 6 @e)))
47
+ (cons 70 (cdr (assoc 70 @e)))
48
+ (cons 60 (if (< (cdr (assoc 62 @e)) 0) 1 0))
49
+ ) @d))
50
+ (setq @e (tblnext "layer" nil)))
51
+ (list (cons 'table 'layer) (cons 'total (length @d)) (cons 'data (list (reverse @d)))))` },
52
+ block: { name: 'block', lisp: `(progn
53
+ (setq @d nil)
54
+ (vlax-for e (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object)))
55
+ (if (not (and (vlax-property-available-p e 'isfoundation) (vlax-get e 'isfoundation)))
56
+ (setq @d (cons (@:get-props e '("Name" "Count" "Handle" "ObjectName" "IsXRef" "IsXRefOverlay" "IsDynamicBlock" "HasAttributes" "HasExtensionDictionary" "Path" "Description" "Layout" "BlockScope")) @d))))
57
+ (list (cons 'table 'block) (cons 'total (length @d)) (cons 'data (list (reverse @d)))))` },
58
+ style: { name: 'style', lisp: `(progn
59
+ (setq @d nil)
60
+ (vlax-for e (vla-get-textstyles (vla-get-activedocument (vlax-get-acad-object)))
61
+ (setq @d (cons (@:get-props e '("Name" "FontFile" "BigFontFile" "Height" "Width" "ObliqueAngle" "TextGenerationFlag" "Handle" "ObjectName")) @d)))
62
+ (list (cons 'table 'style) (cons 'total (length @d)) (cons 'data (list (reverse @d)))))` },
63
+ dimstyle: { name: 'dimstyle', lisp: `(progn
64
+ (setq @d nil)
65
+ (vlax-for e (vla-get-dimstyles (vla-get-activedocument (vlax-get-acad-object)))
66
+ (setq @d (cons (@:get-props e '("Name" "Handle" "ObjectName")) @d)))
67
+ (list (cons 'table 'dimstyle) (cons 'total (length @d)) (cons 'data (list (reverse @d)))))` },
68
+ ltype: { name: 'linetype', lisp: `(progn
69
+ (setq @d nil)
70
+ (vlax-for e (vla-get-linetypes (vla-get-activedocument (vlax-get-acad-object)))
71
+ (setq @d (cons (@:get-props e '("Name" "Description" "PatternLength" "Handle" "ObjectName")) @d)))
72
+ (list (cons 'table 'linetype) (cons 'total (length @d)) (cons 'data (list (reverse @d)))))` },
73
+ };
74
+
75
+ export const TBL_KEY_ALIAS = {
76
+ textstyle: 'style',
77
+ textstyles: 'style',
78
+ linetype: 'ltype',
79
+ };
@@ -0,0 +1,131 @@
1
+ import { RESOURCES } from './resource-defs.js';
2
+ import { clearCache } from './resource-cache.js';
3
+ import {
4
+ getEntityByHandle,
5
+ getCadInfo,
6
+ getEntities,
7
+ getTextContent,
8
+ combineText,
9
+ getDwgName,
10
+ getDwgPath,
11
+ getBlockRefs,
12
+ getTables,
13
+ getPackages,
14
+ getPlatforms,
15
+ getDwgList,
16
+ getDraftingStandards,
17
+ getCodingStandards,
18
+ getCadEvents,
19
+ } from './resource-readers.js';
20
+
21
+ function parseQuery(uri) {
22
+ const qIdx = uri.indexOf('?');
23
+ if (qIdx === -1) return { base: uri, params: {} };
24
+ const base = uri.substring(0, qIdx);
25
+ const params = {};
26
+ uri.substring(qIdx + 1).split('&').forEach(p => {
27
+ const eqIdx = p.indexOf('=');
28
+ const k = eqIdx === -1 ? p : p.substring(0, eqIdx);
29
+ const v = eqIdx === -1 ? '' : p.substring(eqIdx + 1);
30
+ if (k) params[k] = decodeURIComponent(v);
31
+ });
32
+ return { base, params };
33
+ }
34
+
35
+ function parseTemplateUri(uri) {
36
+ const entityMatch = uri.match(/^atlisp:\/\/dwg\/entity\/([^?]+)$/);
37
+ if (entityMatch) return { type: 'entity', handle: entityMatch[1] };
38
+
39
+ const tblMatch = uri.match(/^atlisp:\/\/dwg\/tbl\/([^?]+)$/);
40
+ if (tblMatch) return { type: 'tbl', tblName: tblMatch[1] };
41
+
42
+ const blockRefMatch = uri.match(/^atlisp:\/\/dwg\/block-refs\/([^?]+)$/);
43
+ if (blockRefMatch) return { type: 'block-refs', blockName: blockRefMatch[1] };
44
+
45
+ return null;
46
+ }
47
+
48
+ export async function listResources(subscribedUris = []) {
49
+ return RESOURCES.map(r => ({
50
+ uri: r.uri,
51
+ name: r.name,
52
+ description: r.description,
53
+ mimeType: r.mimeType,
54
+ subscribed: subscribedUris.includes(r.uri)
55
+ }));
56
+ }
57
+
58
+ export async function readResource(uri) {
59
+ const templateMatch = parseTemplateUri(uri);
60
+ if (templateMatch) {
61
+ if (templateMatch.type === 'entity') {
62
+ return await getEntityByHandle(templateMatch.handle);
63
+ }
64
+ if (templateMatch.type === 'tbl') {
65
+ return await getTables({ tbl: templateMatch.tblName });
66
+ }
67
+ if (templateMatch.type === 'block-refs') {
68
+ const { blockName } = templateMatch;
69
+ const queryIdx = uri.indexOf('?');
70
+ const queryParams = {};
71
+ if (queryIdx !== -1) {
72
+ uri.substring(queryIdx + 1).split('&').forEach(p => {
73
+ const eqIdx = p.indexOf('=');
74
+ const k = eqIdx === -1 ? p : p.substring(0, eqIdx);
75
+ const v = eqIdx === -1 ? '' : p.substring(eqIdx + 1);
76
+ if (k) queryParams[k] = decodeURIComponent(v);
77
+ });
78
+ }
79
+ const refs = await getBlockRefs({ blockName });
80
+ const offset = parseInt(queryParams.offset, 10) || 0;
81
+ const limit = parseInt(queryParams.limit, 10) || 0;
82
+ if (limit > 0 && Array.isArray(refs)) return refs.slice(offset, offset + limit);
83
+ return refs;
84
+ }
85
+ }
86
+
87
+ const { base, params } = parseQuery(uri);
88
+
89
+ switch (base) {
90
+ case 'atlisp://cad/info':
91
+ return await getCadInfo();
92
+ case 'atlisp://dwg/entities': {
93
+ const entityData = await getEntities(params);
94
+ // Support pagination via offset/limit params
95
+ const offset = parseInt(params.offset, 10) || 0;
96
+ const limit = parseInt(params.limit, 10) || 0;
97
+ if (limit > 0 && Array.isArray(entityData)) {
98
+ return entityData.slice(offset, offset + limit);
99
+ }
100
+ return entityData;
101
+ }
102
+ case 'atlisp://dwg/text-content':
103
+ return await getTextContent(params);
104
+ case 'atlisp://dwg/text-combined':
105
+ return await combineText(params);
106
+ case 'atlisp://dwg/name':
107
+ return await getDwgName();
108
+ case 'atlisp://dwg/path':
109
+ return await getDwgPath();
110
+ case 'atlisp://dwg/block-refs':
111
+ return await getBlockRefs(params);
112
+ case 'atlisp://dwg/tbl':
113
+ return await getTables(params);
114
+ case 'atlisp://cad/dwgs':
115
+ return await getDwgList();
116
+ case 'atlisp://packages':
117
+ return await getPackages(params.name);
118
+ case 'atlisp://platforms':
119
+ return getPlatforms();
120
+ case 'atlisp://standards/drafting':
121
+ return getDraftingStandards();
122
+ case 'atlisp://standards/coding':
123
+ return getCodingStandards();
124
+ case 'atlisp://cad/events':
125
+ return await getCadEvents(params);
126
+ default:
127
+ throw new Error(`资源不存在: ${uri}`);
128
+ }
129
+ }
130
+
131
+ export { RESOURCES, clearCache };