@atlisp/mcp 1.0.9 → 1.0.10
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.
- package/package.json +8 -4
- package/src/cad.js +20 -2
- package/src/index.js +112 -88
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlisp/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "MCP Server for @lisp on CAD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
18
|
"start": "node src/index.js",
|
|
19
|
-
"test": "
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest"
|
|
20
21
|
},
|
|
21
22
|
"keywords": [
|
|
22
23
|
"mcp",
|
|
@@ -37,10 +38,13 @@
|
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
40
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
40
|
-
"edge-js": "^25.0.1"
|
|
41
|
+
"edge-js": "^25.0.1",
|
|
42
|
+
"express": "^5.2.1"
|
|
41
43
|
},
|
|
42
44
|
"engines": {
|
|
43
45
|
"node": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"vitest": "^4.1.5"
|
|
44
49
|
}
|
|
45
|
-
|
|
46
50
|
}
|
package/src/cad.js
CHANGED
|
@@ -20,7 +20,14 @@ let worker = null;
|
|
|
20
20
|
let _platform = null;
|
|
21
21
|
let _version = null;
|
|
22
22
|
|
|
23
|
-
function
|
|
23
|
+
export function resetWorker() {
|
|
24
|
+
if (worker) {
|
|
25
|
+
worker.kill();
|
|
26
|
+
worker = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getWorker() {
|
|
24
31
|
if (!worker || !worker.connected) {
|
|
25
32
|
worker = spawn('node', [workerPath], {
|
|
26
33
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
@@ -36,7 +43,7 @@ function getWorker() {
|
|
|
36
43
|
return worker;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
function sendMessage(msg) {
|
|
46
|
+
export function sendMessage(msg) {
|
|
40
47
|
return new Promise((resolve, reject) => {
|
|
41
48
|
const w = getWorker();
|
|
42
49
|
let responded = false;
|
|
@@ -177,6 +184,17 @@ class CadConnection {
|
|
|
177
184
|
worker = null;
|
|
178
185
|
}
|
|
179
186
|
}
|
|
187
|
+
|
|
188
|
+
_reset() {
|
|
189
|
+
this.connected = false;
|
|
190
|
+
this.version = null;
|
|
191
|
+
this.product = null;
|
|
192
|
+
_platform = null;
|
|
193
|
+
if (worker) {
|
|
194
|
+
worker.kill();
|
|
195
|
+
worker = null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
180
198
|
}
|
|
181
199
|
|
|
182
200
|
export const cad = new CadConnection();
|
package/src/index.js
CHANGED
|
@@ -20,15 +20,19 @@ log('Server starting, cad loaded: ' + typeof cad);
|
|
|
20
20
|
const PORT = process.env.PORT || 8110;
|
|
21
21
|
const HOST = process.env.HOST || '0.0.0.0';
|
|
22
22
|
const TRANSPORT = process.env.TRANSPORT || 'http';
|
|
23
|
+
const API_KEY = process.env.MCP_API_KEY || '';
|
|
24
|
+
if (API_KEY) {
|
|
25
|
+
log('API Key authentication enabled');
|
|
26
|
+
}
|
|
23
27
|
|
|
24
|
-
const CAD_PLATFORMS = ['AutoCAD', 'ZWCAD', 'GStarCAD', 'BricsCAD'];
|
|
25
|
-
const FILE_EXTENSIONS = {
|
|
28
|
+
export const CAD_PLATFORMS = ['AutoCAD', 'ZWCAD', 'GStarCAD', 'BricsCAD'];
|
|
29
|
+
export const FILE_EXTENSIONS = {
|
|
26
30
|
'AutoCAD': ['.fas', '.vlx'],
|
|
27
31
|
'ZWCAD': ['.zelx', '.vls'],
|
|
28
32
|
'BricsCAD': ['.des']
|
|
29
33
|
};
|
|
30
34
|
|
|
31
|
-
const MOCK_PACKAGES = [
|
|
35
|
+
export const MOCK_PACKAGES = [
|
|
32
36
|
{ name: 'base', description: '基础函数库', version: '1.0.0' },
|
|
33
37
|
{ name: 'at-pm', description: '工程项目管理', version: '2.1.0' },
|
|
34
38
|
{ name: 'network', description: '网络功能模块', version: '1.5.0' },
|
|
@@ -40,7 +44,7 @@ const MOCK_PACKAGES = [
|
|
|
40
44
|
{ name: 'function', description: '函数库', version: '3.0.0' }
|
|
41
45
|
];
|
|
42
46
|
|
|
43
|
-
const tools = [
|
|
47
|
+
export const tools = [
|
|
44
48
|
{
|
|
45
49
|
name: 'connect_cad',
|
|
46
50
|
description: '连接到 CAD (AutoCAD/ZWCAD/GStarCAD/BricsCAD)',
|
|
@@ -132,7 +136,7 @@ const tools = [
|
|
|
132
136
|
}
|
|
133
137
|
];
|
|
134
138
|
|
|
135
|
-
async function handleToolCall(name, args) {
|
|
139
|
+
export async function handleToolCall(name, args) {
|
|
136
140
|
switch (name) {
|
|
137
141
|
case 'connect_cad':
|
|
138
142
|
log('connect_cad called, cad.connected before: ' + cad.connected);
|
|
@@ -218,7 +222,7 @@ async function handleToolCall(name, args) {
|
|
|
218
222
|
return { content: [{ type: 'text', text: '已发送 @lisp 安装代码到 CAD' }] };
|
|
219
223
|
|
|
220
224
|
default:
|
|
221
|
-
|
|
225
|
+
return { content: [{ type: 'text', text: `未知工具: ${name}` }], isError: true };
|
|
222
226
|
}
|
|
223
227
|
}
|
|
224
228
|
|
|
@@ -237,9 +241,9 @@ async function initCadConnection() {
|
|
|
237
241
|
}
|
|
238
242
|
}
|
|
239
243
|
|
|
240
|
-
function createMcpServer() {
|
|
244
|
+
export function createMcpServer() {
|
|
241
245
|
const server = new Server(
|
|
242
|
-
{ name: 'atlisp-mcp-server', version: '1.0.
|
|
246
|
+
{ name: 'atlisp-mcp-server', version: '1.0.10' },
|
|
243
247
|
{ capabilities: { tools: {} } }
|
|
244
248
|
);
|
|
245
249
|
|
|
@@ -253,103 +257,123 @@ function createMcpServer() {
|
|
|
253
257
|
return server;
|
|
254
258
|
}
|
|
255
259
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (!mcpServer) {
|
|
275
|
-
mcpServer = createMcpServer();
|
|
260
|
+
export async function startServer() {
|
|
261
|
+
if (TRANSPORT === 'stdio') {
|
|
262
|
+
await initCadConnection();
|
|
263
|
+
const mcpServer = createMcpServer();
|
|
264
|
+
const transport = new StdioServerTransport();
|
|
265
|
+
await mcpServer.connect(transport);
|
|
266
|
+
console.error('@lisp MCP Server 运行在 stdio 模式');
|
|
267
|
+
} else {
|
|
268
|
+
const app = express();
|
|
269
|
+
app.use(express.json({ type: ['application/json', 'application/json-rpc'] }));
|
|
270
|
+
|
|
271
|
+
if (API_KEY) {
|
|
272
|
+
app.use((req, res, next) => {
|
|
273
|
+
if (req.path === '/health') return next();
|
|
274
|
+
const auth = req.get('Authorization');
|
|
275
|
+
if (auth === `Bearer ${API_KEY}`) return next();
|
|
276
|
+
res.status(401).json({ error: 'Unauthorized' });
|
|
277
|
+
});
|
|
276
278
|
}
|
|
277
279
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
280
|
+
app.get('/health', (req, res) => {
|
|
281
|
+
res.json({ status: 'ok', cad: cad.connected ? 'connected' : 'disconnected' });
|
|
282
|
+
});
|
|
281
283
|
|
|
282
|
-
|
|
284
|
+
let mcpServer = null;
|
|
285
|
+
const sessionId = crypto.randomUUID();
|
|
283
286
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
res.setHeader('Content-Type', 'text/event-stream');
|
|
288
|
-
return res.end('data: ' + json + '\n\n');
|
|
289
|
-
} else {
|
|
290
|
-
res.setHeader('Content-Type', 'application/json');
|
|
291
|
-
return res.end(json);
|
|
287
|
+
app.post('/mcp', async (req, res) => {
|
|
288
|
+
if (!mcpServer) {
|
|
289
|
+
mcpServer = createMcpServer();
|
|
292
290
|
}
|
|
293
|
-
};
|
|
294
291
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
292
|
+
const body = req.body || {};
|
|
293
|
+
const method = body.method;
|
|
294
|
+
const params = body.params;
|
|
295
|
+
const id = body.id;
|
|
296
|
+
const accept = req.get('Accept') || '';
|
|
297
|
+
log('accept: "' + accept + '", wantsSSE: ' + accept.includes('text/event-stream'));
|
|
298
|
+
|
|
299
|
+
res.setHeader('mcp-session-id', sessionId);
|
|
300
|
+
|
|
301
|
+
const sendResponse = (data) => {
|
|
302
|
+
const json = JSON.stringify(data);
|
|
303
|
+
if (accept.includes('text/event-stream')) {
|
|
304
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
305
|
+
return res.end('data: ' + json + '\n\n');
|
|
306
|
+
} else {
|
|
307
|
+
res.setHeader('Content-Type', 'application/json');
|
|
308
|
+
return res.end(json);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
298
311
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
sendResponse({
|
|
302
|
-
jsonrpc: '2.0',
|
|
303
|
-
id,
|
|
304
|
-
result: {
|
|
305
|
-
protocolVersion: '2024-11-05',
|
|
306
|
-
capabilities: { tools: {} },
|
|
307
|
-
serverInfo: { name: 'atlisp-mcp-server', version: '1.0.9' }
|
|
308
|
-
}
|
|
309
|
-
});
|
|
312
|
+
if (!id) {
|
|
313
|
+
return res.status(204).end();
|
|
310
314
|
}
|
|
311
315
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
316
|
+
try {
|
|
317
|
+
if (method === 'initialize') {
|
|
318
|
+
sendResponse({
|
|
319
|
+
jsonrpc: '2.0',
|
|
320
|
+
id,
|
|
321
|
+
result: {
|
|
322
|
+
protocolVersion: '2024-11-05',
|
|
323
|
+
capabilities: { tools: {} },
|
|
324
|
+
serverInfo: { name: 'atlisp-mcp-server', version: '1.0.10' }
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
319
329
|
|
|
320
|
-
|
|
321
|
-
const toolName = params?.name;
|
|
322
|
-
if (!toolName) {
|
|
330
|
+
if (method === 'tools/list') {
|
|
323
331
|
sendResponse({
|
|
324
332
|
jsonrpc: '2.0',
|
|
325
333
|
id,
|
|
326
|
-
|
|
334
|
+
result: { tools }
|
|
327
335
|
});
|
|
328
336
|
return;
|
|
329
337
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
338
|
+
|
|
339
|
+
if (method === 'tools/call') {
|
|
340
|
+
const toolName = params?.name;
|
|
341
|
+
if (!toolName) {
|
|
342
|
+
sendResponse({
|
|
343
|
+
jsonrpc: '2.0',
|
|
344
|
+
id,
|
|
345
|
+
error: { code: -32602, message: 'Invalid params: missing tool name' }
|
|
346
|
+
});
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const toolArgs = params?.arguments || {};
|
|
350
|
+
const result = await handleToolCall(toolName, toolArgs);
|
|
351
|
+
sendResponse({ jsonrpc: '2.0', id, result });
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
sendResponse({
|
|
356
|
+
jsonrpc: '2.0',
|
|
357
|
+
id,
|
|
358
|
+
error: { code: -32601, message: 'Method not found' }
|
|
359
|
+
});
|
|
360
|
+
} catch (e) {
|
|
361
|
+
log('MCP error: ' + e.message);
|
|
362
|
+
return res.status(500).json({
|
|
363
|
+
jsonrpc: '2.0',
|
|
364
|
+
id,
|
|
365
|
+
error: { code: -32603, message: e.message }
|
|
366
|
+
});
|
|
334
367
|
}
|
|
368
|
+
});
|
|
335
369
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
log('MCP error: ' + e.message);
|
|
343
|
-
return res.status(500).json({
|
|
344
|
-
jsonrpc: '2.0',
|
|
345
|
-
id,
|
|
346
|
-
error: { code: -32603, message: e.message }
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
});
|
|
370
|
+
app.listen(PORT, HOST, async () => {
|
|
371
|
+
await initCadConnection();
|
|
372
|
+
console.error(`@lisp MCP Server 启动 - http://${HOST}:${PORT} - Streamable HTTP`);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
350
376
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
console.error(`@lisp MCP Server 启动 - http://${HOST}:${PORT} - Streamable HTTP`);
|
|
354
|
-
});
|
|
377
|
+
if (!process.env.VITEST) {
|
|
378
|
+
await startServer();
|
|
355
379
|
}
|