cyclecad 0.2.2 → 0.2.3
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/API-BUILD-MANIFEST.txt +339 -0
- package/API-SERVER.md +535 -0
- package/Architecture-Deck.pptx +0 -0
- package/CLAUDE.md +172 -11
- package/CLI-BUILD-SUMMARY.md +504 -0
- package/CLI-INDEX.md +356 -0
- package/CLI-README.md +466 -0
- package/COLLABORATION-INTEGRATION-GUIDE.md +325 -0
- package/CONNECTED_FABS_GUIDE.md +612 -0
- package/CONNECTED_FABS_README.md +310 -0
- package/DELIVERABLES.md +343 -0
- package/DFM-ANALYZER-INTEGRATION.md +368 -0
- package/DFM-QUICK-START.js +253 -0
- package/Dockerfile +69 -0
- package/IMPLEMENTATION.md +327 -0
- package/LICENSE +31 -0
- package/MARKETPLACE_QUICK_REFERENCE.txt +294 -0
- package/MCP-INDEX.md +264 -0
- package/QUICKSTART-API.md +388 -0
- package/QUICKSTART-CLI.md +211 -0
- package/QUICKSTART-MCP.md +196 -0
- package/README-MCP.md +208 -0
- package/TEST-TOKEN-ENGINE.md +319 -0
- package/TOKEN-ENGINE-SUMMARY.md +266 -0
- package/TOKENS-README.md +263 -0
- package/TOOLS-REFERENCE.md +254 -0
- package/app/index.html +168 -3
- package/app/js/TOKEN-INTEGRATION.md +391 -0
- package/app/js/agent-api.js +3 -3
- package/app/js/ai-copilot.js +1435 -0
- package/app/js/cam-pipeline.js +840 -0
- package/app/js/collaboration-ui.js +995 -0
- package/app/js/collaboration.js +1116 -0
- package/app/js/connected-fabs-example.js +404 -0
- package/app/js/connected-fabs.js +1449 -0
- package/app/js/dfm-analyzer.js +1760 -0
- package/app/js/marketplace.js +1994 -0
- package/app/js/material-library.js +2115 -0
- package/app/js/token-dashboard.js +563 -0
- package/app/js/token-engine.js +743 -0
- package/app/test-agent.html +1801 -0
- package/bin/cyclecad-cli.js +662 -0
- package/bin/cyclecad-mcp +2 -0
- package/bin/server.js +242 -0
- package/cycleCAD-Architecture.pptx +0 -0
- package/cycleCAD-Investor-Deck.pptx +0 -0
- package/demo-mcp.sh +60 -0
- package/docs/API-SERVER-SUMMARY.md +375 -0
- package/docs/API-SERVER.md +667 -0
- package/docs/CAM-EXAMPLES.md +344 -0
- package/docs/CAM-INTEGRATION.md +612 -0
- package/docs/CAM-QUICK-REFERENCE.md +199 -0
- package/docs/CLI-INTEGRATION.md +510 -0
- package/docs/CLI.md +872 -0
- package/docs/MARKETPLACE-API-SCHEMA.json +564 -0
- package/docs/MARKETPLACE-INTEGRATION.md +467 -0
- package/docs/MARKETPLACE-SETUP.html +439 -0
- package/docs/MCP-SERVER.md +403 -0
- package/examples/api-client-example.js +488 -0
- package/examples/api-client-example.py +359 -0
- package/examples/batch-manufacturing.txt +28 -0
- package/examples/batch-simple.txt +26 -0
- package/model-marketplace.html +1273 -0
- package/package.json +14 -3
- package/server/api-server.js +1120 -0
- package/server/mcp-server.js +1161 -0
- package/test-api-server.js +432 -0
- package/test-mcp.js +198 -0
- package/~$cycleCAD-Investor-Deck.pptx +0 -0
|
@@ -0,0 +1,1161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* cycleCAD MCP Server
|
|
5
|
+
*
|
|
6
|
+
* Implements the Model Context Protocol (MCP) for the cycleCAD Agent API.
|
|
7
|
+
* Exposes all 55+ commands from the Agent API as MCP tools.
|
|
8
|
+
*
|
|
9
|
+
* Protocol: JSON-RPC 2.0 over stdio
|
|
10
|
+
* Methods: initialize, tools/list, tools/call
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* npx cyclecad-mcp [--help] [--port 3000] [--ws-url ws://localhost:3000/api/ws]
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const readline = require('readline');
|
|
17
|
+
const http = require('http');
|
|
18
|
+
|
|
19
|
+
// Try to load WebSocket, but make it optional
|
|
20
|
+
let WebSocket = null;
|
|
21
|
+
try {
|
|
22
|
+
WebSocket = require('ws');
|
|
23
|
+
} catch (e) {
|
|
24
|
+
if (config && config.debug) {
|
|
25
|
+
console.error('[MCP] WebSocket module not available, using HTTP only');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// =============================================================================
|
|
30
|
+
// Configuration
|
|
31
|
+
// =============================================================================
|
|
32
|
+
|
|
33
|
+
const config = {
|
|
34
|
+
wsUrl: process.env.CYCLECAD_WS_URL || 'ws://localhost:3000/api/ws',
|
|
35
|
+
httpUrl: process.env.CYCLECAD_HTTP_URL || 'http://localhost:3000/api/execute',
|
|
36
|
+
timeout: 30000,
|
|
37
|
+
debug: process.env.DEBUG_MCP === '1'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// MCP Protocol Implementation
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
class MCPServer {
|
|
45
|
+
constructor() {
|
|
46
|
+
this.messageId = 0;
|
|
47
|
+
this.pendingRequests = new Map();
|
|
48
|
+
this.wsConnection = null;
|
|
49
|
+
this.isInitialized = false;
|
|
50
|
+
this.commandQueue = [];
|
|
51
|
+
this.sessionId = null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Initialize the MCP server and optionally connect to cycleCAD
|
|
56
|
+
*/
|
|
57
|
+
async initialize() {
|
|
58
|
+
this.isInitialized = true;
|
|
59
|
+
this.sessionId = this.generateId('session');
|
|
60
|
+
|
|
61
|
+
// Try to connect to cycleCAD WebSocket (non-blocking)
|
|
62
|
+
this.connectToWebSocket().catch(err => {
|
|
63
|
+
if (config.debug) console.error('[MCP] WebSocket connection failed:', err.message);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
protocolVersion: '2024-11-05',
|
|
68
|
+
capabilities: {
|
|
69
|
+
tools: {
|
|
70
|
+
listChanged: false
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
serverInfo: {
|
|
74
|
+
name: 'cyclecad-mcp',
|
|
75
|
+
version: '1.0.0'
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Connect to cycleCAD via WebSocket
|
|
82
|
+
*/
|
|
83
|
+
async connectToWebSocket() {
|
|
84
|
+
if (!WebSocket) {
|
|
85
|
+
if (config.debug) console.error('[MCP] WebSocket not available, skipping connection');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
this.wsConnection = new WebSocket(config.wsUrl);
|
|
91
|
+
|
|
92
|
+
this.wsConnection.on('open', () => {
|
|
93
|
+
if (config.debug) console.error('[MCP] WebSocket connected');
|
|
94
|
+
// Process queued commands
|
|
95
|
+
while (this.commandQueue.length > 0) {
|
|
96
|
+
const cmd = this.commandQueue.shift();
|
|
97
|
+
this.sendCommand(cmd.method, cmd.params, cmd.resolve, cmd.reject);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.wsConnection.on('message', (data) => {
|
|
102
|
+
try {
|
|
103
|
+
const msg = JSON.parse(data);
|
|
104
|
+
if (msg.id && this.pendingRequests.has(msg.id)) {
|
|
105
|
+
const { resolve, reject, timeout } = this.pendingRequests.get(msg.id);
|
|
106
|
+
clearTimeout(timeout);
|
|
107
|
+
this.pendingRequests.delete(msg.id);
|
|
108
|
+
if (msg.error) {
|
|
109
|
+
reject(new Error(msg.error));
|
|
110
|
+
} else {
|
|
111
|
+
resolve(msg.result);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch (e) {
|
|
115
|
+
if (config.debug) console.error('[MCP] WebSocket message parse error:', e);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
this.wsConnection.on('error', (err) => {
|
|
120
|
+
if (config.debug) console.error('[MCP] WebSocket error:', err.message);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
this.wsConnection.on('close', () => {
|
|
124
|
+
if (config.debug) console.error('[MCP] WebSocket closed');
|
|
125
|
+
this.wsConnection = null;
|
|
126
|
+
});
|
|
127
|
+
} catch (e) {
|
|
128
|
+
if (config.debug) console.error('[MCP] WebSocket connection error:', e.message);
|
|
129
|
+
throw e;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Execute a cycleCAD command via WebSocket or HTTP
|
|
135
|
+
*/
|
|
136
|
+
async executeCommand(method, params = {}) {
|
|
137
|
+
const cmd = { method, params };
|
|
138
|
+
|
|
139
|
+
if (WebSocket && this.wsConnection && this.wsConnection.readyState === WebSocket.OPEN) {
|
|
140
|
+
// Use WebSocket
|
|
141
|
+
return this.sendCommandViaWS(cmd);
|
|
142
|
+
} else if (this.isInitialized && WebSocket) {
|
|
143
|
+
// Queue for when connected
|
|
144
|
+
return new Promise((resolve, reject) => {
|
|
145
|
+
this.commandQueue.push({ method, params, resolve, reject });
|
|
146
|
+
// Try HTTP as fallback immediately
|
|
147
|
+
this.sendCommandViaHTTP(cmd).then(resolve).catch(() => {
|
|
148
|
+
// Keep queued
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
// Use HTTP
|
|
153
|
+
return this.sendCommandViaHTTP(cmd);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Send command via WebSocket
|
|
159
|
+
*/
|
|
160
|
+
sendCommandViaWS(cmd) {
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
const id = ++this.messageId;
|
|
163
|
+
const timeout = setTimeout(() => {
|
|
164
|
+
this.pendingRequests.delete(id);
|
|
165
|
+
reject(new Error(`Command timeout: ${cmd.method}`));
|
|
166
|
+
}, config.timeout);
|
|
167
|
+
|
|
168
|
+
this.pendingRequests.set(id, { resolve, reject, timeout });
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
this.wsConnection.send(JSON.stringify({ id, ...cmd }));
|
|
172
|
+
} catch (e) {
|
|
173
|
+
clearTimeout(timeout);
|
|
174
|
+
this.pendingRequests.delete(id);
|
|
175
|
+
reject(e);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Send command via HTTP POST
|
|
182
|
+
*/
|
|
183
|
+
sendCommandViaHTTP(cmd) {
|
|
184
|
+
return new Promise((resolve, reject) => {
|
|
185
|
+
const payload = JSON.stringify(cmd);
|
|
186
|
+
const options = {
|
|
187
|
+
method: 'POST',
|
|
188
|
+
headers: {
|
|
189
|
+
'Content-Type': 'application/json',
|
|
190
|
+
'Content-Length': Buffer.byteLength(payload)
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const req = http.request(config.httpUrl, options, (res) => {
|
|
195
|
+
let data = '';
|
|
196
|
+
res.on('data', chunk => data += chunk);
|
|
197
|
+
res.on('end', () => {
|
|
198
|
+
try {
|
|
199
|
+
const result = JSON.parse(data);
|
|
200
|
+
if (result.ok) {
|
|
201
|
+
resolve(result.result);
|
|
202
|
+
} else {
|
|
203
|
+
reject(new Error(result.error || 'Command failed'));
|
|
204
|
+
}
|
|
205
|
+
} catch (e) {
|
|
206
|
+
reject(e);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
req.on('error', reject);
|
|
212
|
+
req.setTimeout(config.timeout, () => {
|
|
213
|
+
req.destroy();
|
|
214
|
+
reject(new Error(`HTTP timeout: ${cmd.method}`));
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
req.write(payload);
|
|
218
|
+
req.end();
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get all available tools (MCP tools/list)
|
|
224
|
+
*/
|
|
225
|
+
getTools() {
|
|
226
|
+
return TOOL_DEFINITIONS;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Call a tool (MCP tools/call)
|
|
231
|
+
*/
|
|
232
|
+
async callTool(name, args) {
|
|
233
|
+
const tool = TOOL_DEFINITIONS.find(t => t.name === name);
|
|
234
|
+
if (!tool) {
|
|
235
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Extract command method from tool name
|
|
239
|
+
// Tool name format: "sketch_start" -> method: "sketch.start"
|
|
240
|
+
const method = name.replace(/_/g, '.');
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
const result = await this.executeCommand(method, args);
|
|
244
|
+
return {
|
|
245
|
+
content: [
|
|
246
|
+
{
|
|
247
|
+
type: 'text',
|
|
248
|
+
text: JSON.stringify(result, null, 2)
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
};
|
|
252
|
+
} catch (e) {
|
|
253
|
+
return {
|
|
254
|
+
content: [
|
|
255
|
+
{
|
|
256
|
+
type: 'text',
|
|
257
|
+
text: JSON.stringify({
|
|
258
|
+
ok: false,
|
|
259
|
+
error: e.message,
|
|
260
|
+
method,
|
|
261
|
+
args
|
|
262
|
+
}, null, 2)
|
|
263
|
+
}
|
|
264
|
+
],
|
|
265
|
+
isError: true
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
generateId(prefix) {
|
|
271
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// =============================================================================
|
|
276
|
+
// MCP Tool Definitions
|
|
277
|
+
// =============================================================================
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Convert Agent API schema to MCP tool definitions
|
|
281
|
+
*/
|
|
282
|
+
const TOOL_DEFINITIONS = [
|
|
283
|
+
// SKETCH tools
|
|
284
|
+
{
|
|
285
|
+
name: 'sketch_start',
|
|
286
|
+
description: 'Start sketch mode on XY, XZ, or YZ plane',
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: {
|
|
290
|
+
plane: { type: 'string', enum: ['XY', 'XZ', 'YZ'], description: 'Construction plane' }
|
|
291
|
+
},
|
|
292
|
+
required: []
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'sketch_end',
|
|
297
|
+
description: 'End sketch and return all entities drawn',
|
|
298
|
+
inputSchema: { type: 'object', properties: {} }
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
name: 'sketch_line',
|
|
302
|
+
description: 'Draw a line segment in the sketch',
|
|
303
|
+
inputSchema: {
|
|
304
|
+
type: 'object',
|
|
305
|
+
properties: {
|
|
306
|
+
x1: { type: 'number', description: 'Start X coordinate' },
|
|
307
|
+
y1: { type: 'number', description: 'Start Y coordinate' },
|
|
308
|
+
x2: { type: 'number', description: 'End X coordinate' },
|
|
309
|
+
y2: { type: 'number', description: 'End Y coordinate' }
|
|
310
|
+
},
|
|
311
|
+
required: ['x1', 'y1', 'x2', 'y2']
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: 'sketch_rect',
|
|
316
|
+
description: 'Draw a rectangle in the sketch',
|
|
317
|
+
inputSchema: {
|
|
318
|
+
type: 'object',
|
|
319
|
+
properties: {
|
|
320
|
+
x: { type: 'number', description: 'Origin X (default: 0)' },
|
|
321
|
+
y: { type: 'number', description: 'Origin Y (default: 0)' },
|
|
322
|
+
width: { type: 'number', description: 'Rectangle width' },
|
|
323
|
+
height: { type: 'number', description: 'Rectangle height' }
|
|
324
|
+
},
|
|
325
|
+
required: ['width', 'height']
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: 'sketch_circle',
|
|
330
|
+
description: 'Draw a circle in the sketch',
|
|
331
|
+
inputSchema: {
|
|
332
|
+
type: 'object',
|
|
333
|
+
properties: {
|
|
334
|
+
cx: { type: 'number', description: 'Center X (default: 0)' },
|
|
335
|
+
cy: { type: 'number', description: 'Center Y (default: 0)' },
|
|
336
|
+
radius: { type: 'number', description: 'Circle radius' }
|
|
337
|
+
},
|
|
338
|
+
required: ['radius']
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'sketch_arc',
|
|
343
|
+
description: 'Draw an arc in the sketch',
|
|
344
|
+
inputSchema: {
|
|
345
|
+
type: 'object',
|
|
346
|
+
properties: {
|
|
347
|
+
cx: { type: 'number', description: 'Center X (default: 0)' },
|
|
348
|
+
cy: { type: 'number', description: 'Center Y (default: 0)' },
|
|
349
|
+
radius: { type: 'number', description: 'Arc radius' },
|
|
350
|
+
startAngle: { type: 'number', description: 'Start angle in radians (default: 0)' },
|
|
351
|
+
endAngle: { type: 'number', description: 'End angle in radians (default: π)' }
|
|
352
|
+
},
|
|
353
|
+
required: ['radius']
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: 'sketch_clear',
|
|
358
|
+
description: 'Clear all sketch entities',
|
|
359
|
+
inputSchema: { type: 'object', properties: {} }
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: 'sketch_entities',
|
|
363
|
+
description: 'List all entities in the current sketch',
|
|
364
|
+
inputSchema: { type: 'object', properties: {} }
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
// OPERATIONS tools
|
|
368
|
+
{
|
|
369
|
+
name: 'ops_extrude',
|
|
370
|
+
description: 'Extrude sketch profile into 3D solid',
|
|
371
|
+
inputSchema: {
|
|
372
|
+
type: 'object',
|
|
373
|
+
properties: {
|
|
374
|
+
height: { type: 'number', description: 'Extrusion height' },
|
|
375
|
+
symmetric: { type: 'boolean', description: 'Extrude symmetrically' },
|
|
376
|
+
material: { type: 'string', description: 'Material name (steel, aluminum, etc)' }
|
|
377
|
+
},
|
|
378
|
+
required: ['height']
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: 'ops_revolve',
|
|
383
|
+
description: 'Revolve sketch profile around an axis',
|
|
384
|
+
inputSchema: {
|
|
385
|
+
type: 'object',
|
|
386
|
+
properties: {
|
|
387
|
+
axis: { type: 'string', enum: ['X', 'Y', 'Z'], description: 'Rotation axis' },
|
|
388
|
+
angle: { type: 'number', description: 'Revolution angle in degrees' },
|
|
389
|
+
material: { type: 'string', description: 'Material name' }
|
|
390
|
+
},
|
|
391
|
+
required: []
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: 'ops_primitive',
|
|
396
|
+
description: 'Create a primitive 3D shape (box, sphere, cylinder, etc)',
|
|
397
|
+
inputSchema: {
|
|
398
|
+
type: 'object',
|
|
399
|
+
properties: {
|
|
400
|
+
shape: { type: 'string', enum: ['box', 'sphere', 'cylinder', 'cone', 'torus', 'capsule'] },
|
|
401
|
+
width: { type: 'number', description: 'Width dimension' },
|
|
402
|
+
height: { type: 'number', description: 'Height dimension' },
|
|
403
|
+
depth: { type: 'number', description: 'Depth dimension' },
|
|
404
|
+
radius: { type: 'number', description: 'Radius' },
|
|
405
|
+
segments: { type: 'number', description: 'Tessellation segments' },
|
|
406
|
+
material: { type: 'string', description: 'Material name' }
|
|
407
|
+
},
|
|
408
|
+
required: ['shape']
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: 'ops_fillet',
|
|
413
|
+
description: 'Apply fillet radius to edges of a feature',
|
|
414
|
+
inputSchema: {
|
|
415
|
+
type: 'object',
|
|
416
|
+
properties: {
|
|
417
|
+
target: { type: 'string', description: 'Feature ID to fillet' },
|
|
418
|
+
radius: { type: 'number', description: 'Fillet radius' }
|
|
419
|
+
},
|
|
420
|
+
required: ['target']
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: 'ops_chamfer',
|
|
425
|
+
description: 'Apply chamfer to edges of a feature',
|
|
426
|
+
inputSchema: {
|
|
427
|
+
type: 'object',
|
|
428
|
+
properties: {
|
|
429
|
+
target: { type: 'string', description: 'Feature ID to chamfer' },
|
|
430
|
+
distance: { type: 'number', description: 'Chamfer distance' }
|
|
431
|
+
},
|
|
432
|
+
required: ['target']
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: 'ops_boolean',
|
|
437
|
+
description: 'Perform boolean operation (union, cut, intersect)',
|
|
438
|
+
inputSchema: {
|
|
439
|
+
type: 'object',
|
|
440
|
+
properties: {
|
|
441
|
+
operation: { type: 'string', enum: ['union', 'cut', 'intersect'] },
|
|
442
|
+
targetA: { type: 'string', description: 'First feature ID' },
|
|
443
|
+
targetB: { type: 'string', description: 'Second feature ID' }
|
|
444
|
+
},
|
|
445
|
+
required: ['operation', 'targetA', 'targetB']
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
name: 'ops_shell',
|
|
450
|
+
description: 'Create a hollow shell from a solid',
|
|
451
|
+
inputSchema: {
|
|
452
|
+
type: 'object',
|
|
453
|
+
properties: {
|
|
454
|
+
target: { type: 'string', description: 'Feature ID to shell' },
|
|
455
|
+
thickness: { type: 'number', description: 'Shell wall thickness' }
|
|
456
|
+
},
|
|
457
|
+
required: ['target']
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: 'ops_pattern',
|
|
462
|
+
description: 'Create array pattern of a feature',
|
|
463
|
+
inputSchema: {
|
|
464
|
+
type: 'object',
|
|
465
|
+
properties: {
|
|
466
|
+
target: { type: 'string', description: 'Feature ID to pattern' },
|
|
467
|
+
type: { type: 'string', enum: ['rect', 'circular'], description: 'Pattern type' },
|
|
468
|
+
count: { type: 'number', description: 'Number of copies' },
|
|
469
|
+
spacing: { type: 'number', description: 'Space between copies' }
|
|
470
|
+
},
|
|
471
|
+
required: ['target']
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
name: 'ops_material',
|
|
476
|
+
description: 'Change material of a feature',
|
|
477
|
+
inputSchema: {
|
|
478
|
+
type: 'object',
|
|
479
|
+
properties: {
|
|
480
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
481
|
+
material: { type: 'string', description: 'Material name' }
|
|
482
|
+
},
|
|
483
|
+
required: ['target', 'material']
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
name: 'ops_sweep',
|
|
488
|
+
description: 'Sweep a profile along a path',
|
|
489
|
+
inputSchema: {
|
|
490
|
+
type: 'object',
|
|
491
|
+
properties: {
|
|
492
|
+
profile: { type: 'object', description: 'Profile to sweep' },
|
|
493
|
+
path: { type: 'object', description: 'Path to follow' },
|
|
494
|
+
twist: { type: 'number', description: 'Twist angle' },
|
|
495
|
+
scale: { type: 'number', description: 'Scale interpolation' }
|
|
496
|
+
},
|
|
497
|
+
required: ['profile', 'path']
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
name: 'ops_loft',
|
|
502
|
+
description: 'Loft between multiple profiles',
|
|
503
|
+
inputSchema: {
|
|
504
|
+
type: 'object',
|
|
505
|
+
properties: {
|
|
506
|
+
profiles: { type: 'array', description: 'Profiles to loft between' }
|
|
507
|
+
},
|
|
508
|
+
required: ['profiles']
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
name: 'ops_spring',
|
|
513
|
+
description: 'Generate a helical spring',
|
|
514
|
+
inputSchema: {
|
|
515
|
+
type: 'object',
|
|
516
|
+
properties: {
|
|
517
|
+
radius: { type: 'number', description: 'Spring radius' },
|
|
518
|
+
wireRadius: { type: 'number', description: 'Wire radius' },
|
|
519
|
+
height: { type: 'number', description: 'Spring height' },
|
|
520
|
+
turns: { type: 'number', description: 'Number of turns' },
|
|
521
|
+
material: { type: 'string', description: 'Material name' }
|
|
522
|
+
},
|
|
523
|
+
required: []
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
name: 'ops_thread',
|
|
528
|
+
description: 'Generate a screw thread',
|
|
529
|
+
inputSchema: {
|
|
530
|
+
type: 'object',
|
|
531
|
+
properties: {
|
|
532
|
+
outerRadius: { type: 'number', description: 'Outer radius' },
|
|
533
|
+
innerRadius: { type: 'number', description: 'Inner radius' },
|
|
534
|
+
pitch: { type: 'number', description: 'Thread pitch' },
|
|
535
|
+
length: { type: 'number', description: 'Thread length' },
|
|
536
|
+
material: { type: 'string', description: 'Material name' }
|
|
537
|
+
},
|
|
538
|
+
required: []
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
name: 'ops_bend',
|
|
543
|
+
description: 'Bend sheet metal',
|
|
544
|
+
inputSchema: {
|
|
545
|
+
type: 'object',
|
|
546
|
+
properties: {
|
|
547
|
+
target: { type: 'string', description: 'Feature ID to bend' },
|
|
548
|
+
angle: { type: 'number', description: 'Bend angle in degrees' },
|
|
549
|
+
radius: { type: 'number', description: 'Bend radius' }
|
|
550
|
+
},
|
|
551
|
+
required: ['target']
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
// TRANSFORM tools
|
|
556
|
+
{
|
|
557
|
+
name: 'transform_move',
|
|
558
|
+
description: 'Translate a feature by X, Y, Z offset',
|
|
559
|
+
inputSchema: {
|
|
560
|
+
type: 'object',
|
|
561
|
+
properties: {
|
|
562
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
563
|
+
x: { type: 'number', description: 'X offset' },
|
|
564
|
+
y: { type: 'number', description: 'Y offset' },
|
|
565
|
+
z: { type: 'number', description: 'Z offset' }
|
|
566
|
+
},
|
|
567
|
+
required: ['target']
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
name: 'transform_rotate',
|
|
572
|
+
description: 'Rotate a feature around X, Y, Z axes',
|
|
573
|
+
inputSchema: {
|
|
574
|
+
type: 'object',
|
|
575
|
+
properties: {
|
|
576
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
577
|
+
x: { type: 'number', description: 'Rotation around X axis (degrees)' },
|
|
578
|
+
y: { type: 'number', description: 'Rotation around Y axis (degrees)' },
|
|
579
|
+
z: { type: 'number', description: 'Rotation around Z axis (degrees)' }
|
|
580
|
+
},
|
|
581
|
+
required: ['target']
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
name: 'transform_scale',
|
|
586
|
+
description: 'Scale a feature along X, Y, Z axes',
|
|
587
|
+
inputSchema: {
|
|
588
|
+
type: 'object',
|
|
589
|
+
properties: {
|
|
590
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
591
|
+
x: { type: 'number', description: 'X scale factor' },
|
|
592
|
+
y: { type: 'number', description: 'Y scale factor' },
|
|
593
|
+
z: { type: 'number', description: 'Z scale factor' }
|
|
594
|
+
},
|
|
595
|
+
required: ['target']
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
|
|
599
|
+
// VIEW tools
|
|
600
|
+
{
|
|
601
|
+
name: 'view_set',
|
|
602
|
+
description: 'Set camera to a standard view',
|
|
603
|
+
inputSchema: {
|
|
604
|
+
type: 'object',
|
|
605
|
+
properties: {
|
|
606
|
+
view: {
|
|
607
|
+
type: 'string',
|
|
608
|
+
enum: ['front', 'back', 'left', 'right', 'top', 'bottom', 'isometric'],
|
|
609
|
+
description: 'View direction'
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
required: []
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
name: 'view_fit',
|
|
617
|
+
description: 'Fit view to a feature or all features',
|
|
618
|
+
inputSchema: {
|
|
619
|
+
type: 'object',
|
|
620
|
+
properties: {
|
|
621
|
+
target: { type: 'string', description: 'Feature ID (optional, fits all if omitted)' }
|
|
622
|
+
},
|
|
623
|
+
required: []
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
name: 'view_wireframe',
|
|
628
|
+
description: 'Toggle wireframe rendering',
|
|
629
|
+
inputSchema: {
|
|
630
|
+
type: 'object',
|
|
631
|
+
properties: {
|
|
632
|
+
enabled: { type: 'boolean', description: 'Enable or disable wireframe' }
|
|
633
|
+
},
|
|
634
|
+
required: []
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
name: 'view_grid',
|
|
639
|
+
description: 'Toggle grid visibility',
|
|
640
|
+
inputSchema: {
|
|
641
|
+
type: 'object',
|
|
642
|
+
properties: {
|
|
643
|
+
visible: { type: 'boolean', description: 'Show or hide grid' }
|
|
644
|
+
},
|
|
645
|
+
required: []
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
|
|
649
|
+
// EXPORT tools
|
|
650
|
+
{
|
|
651
|
+
name: 'export_stl',
|
|
652
|
+
description: 'Export model as STL file',
|
|
653
|
+
inputSchema: {
|
|
654
|
+
type: 'object',
|
|
655
|
+
properties: {
|
|
656
|
+
filename: { type: 'string', description: 'Output filename' },
|
|
657
|
+
binary: { type: 'boolean', description: 'Use binary format (default: true)' }
|
|
658
|
+
},
|
|
659
|
+
required: []
|
|
660
|
+
}
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
name: 'export_obj',
|
|
664
|
+
description: 'Export model as OBJ file',
|
|
665
|
+
inputSchema: {
|
|
666
|
+
type: 'object',
|
|
667
|
+
properties: {
|
|
668
|
+
filename: { type: 'string', description: 'Output filename' }
|
|
669
|
+
},
|
|
670
|
+
required: []
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
name: 'export_gltf',
|
|
675
|
+
description: 'Export model as glTF 2.0 file',
|
|
676
|
+
inputSchema: {
|
|
677
|
+
type: 'object',
|
|
678
|
+
properties: {
|
|
679
|
+
filename: { type: 'string', description: 'Output filename' }
|
|
680
|
+
},
|
|
681
|
+
required: []
|
|
682
|
+
}
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
name: 'export_json',
|
|
686
|
+
description: 'Export model as cycleCAD JSON',
|
|
687
|
+
inputSchema: {
|
|
688
|
+
type: 'object',
|
|
689
|
+
properties: {
|
|
690
|
+
filename: { type: 'string', description: 'Output filename' }
|
|
691
|
+
},
|
|
692
|
+
required: []
|
|
693
|
+
}
|
|
694
|
+
},
|
|
695
|
+
|
|
696
|
+
// VALIDATE tools
|
|
697
|
+
{
|
|
698
|
+
name: 'validate_dimensions',
|
|
699
|
+
description: 'Get dimensions and bounding box of a feature',
|
|
700
|
+
inputSchema: {
|
|
701
|
+
type: 'object',
|
|
702
|
+
properties: {
|
|
703
|
+
target: { type: 'string', description: 'Feature ID' }
|
|
704
|
+
},
|
|
705
|
+
required: ['target']
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
name: 'validate_wallThickness',
|
|
710
|
+
description: 'Check minimum wall thickness of a feature',
|
|
711
|
+
inputSchema: {
|
|
712
|
+
type: 'object',
|
|
713
|
+
properties: {
|
|
714
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
715
|
+
minWall: { type: 'number', description: 'Minimum wall thickness in mm' }
|
|
716
|
+
},
|
|
717
|
+
required: ['target']
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
name: 'validate_printability',
|
|
722
|
+
description: 'Check if part is printable via FDM, SLA, or CNC',
|
|
723
|
+
inputSchema: {
|
|
724
|
+
type: 'object',
|
|
725
|
+
properties: {
|
|
726
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
727
|
+
process: { type: 'string', enum: ['FDM', 'SLA', 'CNC'], description: 'Manufacturing process' }
|
|
728
|
+
},
|
|
729
|
+
required: ['target']
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
name: 'validate_cost',
|
|
734
|
+
description: 'Estimate manufacturing cost',
|
|
735
|
+
inputSchema: {
|
|
736
|
+
type: 'object',
|
|
737
|
+
properties: {
|
|
738
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
739
|
+
process: { type: 'string', enum: ['FDM', 'SLA', 'CNC', 'injection'], description: 'Manufacturing process' },
|
|
740
|
+
material: { type: 'string', description: 'Material name' }
|
|
741
|
+
},
|
|
742
|
+
required: ['target']
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
name: 'validate_mass',
|
|
747
|
+
description: 'Estimate part mass (weight)',
|
|
748
|
+
inputSchema: {
|
|
749
|
+
type: 'object',
|
|
750
|
+
properties: {
|
|
751
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
752
|
+
material: { type: 'string', description: 'Material name' }
|
|
753
|
+
},
|
|
754
|
+
required: ['target']
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
name: 'validate_surfaceArea',
|
|
759
|
+
description: 'Calculate surface area of a feature',
|
|
760
|
+
inputSchema: {
|
|
761
|
+
type: 'object',
|
|
762
|
+
properties: {
|
|
763
|
+
target: { type: 'string', description: 'Feature ID' }
|
|
764
|
+
},
|
|
765
|
+
required: ['target']
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
name: 'validate_centerOfMass',
|
|
770
|
+
description: 'Get geometric centroid of a feature',
|
|
771
|
+
inputSchema: {
|
|
772
|
+
type: 'object',
|
|
773
|
+
properties: {
|
|
774
|
+
target: { type: 'string', description: 'Feature ID' }
|
|
775
|
+
},
|
|
776
|
+
required: ['target']
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
name: 'validate_designReview',
|
|
781
|
+
description: 'Auto-analyze feature for manufacturing issues (scored A/B/C/F)',
|
|
782
|
+
inputSchema: {
|
|
783
|
+
type: 'object',
|
|
784
|
+
properties: {
|
|
785
|
+
target: { type: 'string', description: 'Feature ID' }
|
|
786
|
+
},
|
|
787
|
+
required: ['target']
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
|
|
791
|
+
// RENDER tools
|
|
792
|
+
{
|
|
793
|
+
name: 'render_snapshot',
|
|
794
|
+
description: 'Render current viewport as PNG image',
|
|
795
|
+
inputSchema: {
|
|
796
|
+
type: 'object',
|
|
797
|
+
properties: {
|
|
798
|
+
width: { type: 'number', description: 'Image width in pixels' },
|
|
799
|
+
height: { type: 'number', description: 'Image height in pixels' }
|
|
800
|
+
},
|
|
801
|
+
required: []
|
|
802
|
+
}
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
name: 'render_multiview',
|
|
806
|
+
description: 'Render 6 standard views (front/back/left/right/top/isometric) as PNGs',
|
|
807
|
+
inputSchema: {
|
|
808
|
+
type: 'object',
|
|
809
|
+
properties: {
|
|
810
|
+
width: { type: 'number', description: 'Image width in pixels' },
|
|
811
|
+
height: { type: 'number', description: 'Image height in pixels' }
|
|
812
|
+
},
|
|
813
|
+
required: []
|
|
814
|
+
}
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
name: 'render_highlight',
|
|
818
|
+
description: 'Highlight a component with color',
|
|
819
|
+
inputSchema: {
|
|
820
|
+
type: 'object',
|
|
821
|
+
properties: {
|
|
822
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
823
|
+
color: { type: 'string', description: 'Hex color code (e.g., #FF0000)' },
|
|
824
|
+
duration: { type: 'number', description: 'Duration in milliseconds' }
|
|
825
|
+
},
|
|
826
|
+
required: ['target']
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
name: 'render_hide',
|
|
831
|
+
description: 'Hide or show a component',
|
|
832
|
+
inputSchema: {
|
|
833
|
+
type: 'object',
|
|
834
|
+
properties: {
|
|
835
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
836
|
+
hidden: { type: 'boolean', description: 'Hide or show' }
|
|
837
|
+
},
|
|
838
|
+
required: ['target']
|
|
839
|
+
}
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
name: 'render_section',
|
|
843
|
+
description: 'Enable section cutting (cross-section)',
|
|
844
|
+
inputSchema: {
|
|
845
|
+
type: 'object',
|
|
846
|
+
properties: {
|
|
847
|
+
enabled: { type: 'boolean', description: 'Enable or disable section cut' },
|
|
848
|
+
axis: { type: 'string', enum: ['X', 'Y', 'Z'], description: 'Cut axis' },
|
|
849
|
+
position: { type: 'number', description: 'Cut position along axis' },
|
|
850
|
+
mode: { type: 'string', enum: ['single', 'clip'], description: 'Section mode' }
|
|
851
|
+
},
|
|
852
|
+
required: []
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
|
|
856
|
+
// QUERY tools
|
|
857
|
+
{
|
|
858
|
+
name: 'query_features',
|
|
859
|
+
description: 'List all features in the model',
|
|
860
|
+
inputSchema: { type: 'object', properties: {} }
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
name: 'query_bbox',
|
|
864
|
+
description: 'Get bounding box of a feature',
|
|
865
|
+
inputSchema: {
|
|
866
|
+
type: 'object',
|
|
867
|
+
properties: {
|
|
868
|
+
target: { type: 'string', description: 'Feature ID' }
|
|
869
|
+
},
|
|
870
|
+
required: ['target']
|
|
871
|
+
}
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
name: 'query_materials',
|
|
875
|
+
description: 'List available materials',
|
|
876
|
+
inputSchema: { type: 'object', properties: {} }
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
name: 'query_session',
|
|
880
|
+
description: 'Get session info',
|
|
881
|
+
inputSchema: { type: 'object', properties: {} }
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
name: 'query_log',
|
|
885
|
+
description: 'Get recent command log',
|
|
886
|
+
inputSchema: {
|
|
887
|
+
type: 'object',
|
|
888
|
+
properties: {
|
|
889
|
+
last: { type: 'number', description: 'Number of recent commands' }
|
|
890
|
+
},
|
|
891
|
+
required: []
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
|
|
895
|
+
// ASSEMBLY tools
|
|
896
|
+
{
|
|
897
|
+
name: 'assembly_addComponent',
|
|
898
|
+
description: 'Add component to assembly',
|
|
899
|
+
inputSchema: {
|
|
900
|
+
type: 'object',
|
|
901
|
+
properties: {
|
|
902
|
+
name: { type: 'string', description: 'Component name' },
|
|
903
|
+
meshOrFile: { type: 'string', description: 'Feature ID or file path' },
|
|
904
|
+
position: { type: 'array', description: '[x, y, z] position' },
|
|
905
|
+
material: { type: 'string', description: 'Material name' }
|
|
906
|
+
},
|
|
907
|
+
required: ['name']
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
name: 'assembly_removeComponent',
|
|
912
|
+
description: 'Remove component from assembly',
|
|
913
|
+
inputSchema: {
|
|
914
|
+
type: 'object',
|
|
915
|
+
properties: {
|
|
916
|
+
target: { type: 'string', description: 'Component ID' }
|
|
917
|
+
},
|
|
918
|
+
required: ['target']
|
|
919
|
+
}
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
name: 'assembly_mate',
|
|
923
|
+
description: 'Define mate constraint between components',
|
|
924
|
+
inputSchema: {
|
|
925
|
+
type: 'object',
|
|
926
|
+
properties: {
|
|
927
|
+
target1: { type: 'string', description: 'First component ID' },
|
|
928
|
+
target2: { type: 'string', description: 'Second component ID' },
|
|
929
|
+
type: { type: 'string', enum: ['coincident', 'concentric', 'parallel', 'tangent'], description: 'Mate type' },
|
|
930
|
+
offset: { type: 'number', description: 'Mate offset' }
|
|
931
|
+
},
|
|
932
|
+
required: ['target1', 'target2']
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
name: 'assembly_explode',
|
|
937
|
+
description: 'Explode component or assembly',
|
|
938
|
+
inputSchema: {
|
|
939
|
+
type: 'object',
|
|
940
|
+
properties: {
|
|
941
|
+
target: { type: 'string', description: 'Component ID or "*" for all' },
|
|
942
|
+
distance: { type: 'number', description: 'Explode distance' }
|
|
943
|
+
},
|
|
944
|
+
required: ['target']
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
|
|
948
|
+
// SCENE tools
|
|
949
|
+
{
|
|
950
|
+
name: 'scene_clear',
|
|
951
|
+
description: 'Clear all features from the scene',
|
|
952
|
+
inputSchema: { type: 'object', properties: {} }
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
name: 'scene_snapshot',
|
|
956
|
+
description: 'Capture viewport as PNG (legacy)',
|
|
957
|
+
inputSchema: { type: 'object', properties: {} }
|
|
958
|
+
},
|
|
959
|
+
|
|
960
|
+
// AI tools
|
|
961
|
+
{
|
|
962
|
+
name: 'ai_identifyPart',
|
|
963
|
+
description: 'Identify part using Gemini Vision API',
|
|
964
|
+
inputSchema: {
|
|
965
|
+
type: 'object',
|
|
966
|
+
properties: {
|
|
967
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
968
|
+
imageData: { type: 'string', description: 'Base64 image data (optional)' }
|
|
969
|
+
},
|
|
970
|
+
required: ['target']
|
|
971
|
+
}
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
name: 'ai_suggestImprovements',
|
|
975
|
+
description: 'Get AI-generated design improvement suggestions',
|
|
976
|
+
inputSchema: {
|
|
977
|
+
type: 'object',
|
|
978
|
+
properties: {
|
|
979
|
+
target: { type: 'string', description: 'Feature ID' }
|
|
980
|
+
},
|
|
981
|
+
required: ['target']
|
|
982
|
+
}
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
name: 'ai_estimateCostAI',
|
|
986
|
+
description: 'AI-powered cost estimation with recommendations',
|
|
987
|
+
inputSchema: {
|
|
988
|
+
type: 'object',
|
|
989
|
+
properties: {
|
|
990
|
+
target: { type: 'string', description: 'Feature ID' },
|
|
991
|
+
process: { type: 'string', enum: ['FDM', 'SLA', 'CNC', 'auto'], description: 'Manufacturing process' },
|
|
992
|
+
material: { type: 'string', description: 'Material name or "auto"' },
|
|
993
|
+
quantity: { type: 'number', description: 'Production quantity' }
|
|
994
|
+
},
|
|
995
|
+
required: ['target']
|
|
996
|
+
}
|
|
997
|
+
},
|
|
998
|
+
|
|
999
|
+
// META tools
|
|
1000
|
+
{
|
|
1001
|
+
name: 'meta_ping',
|
|
1002
|
+
description: 'Health check and session uptime',
|
|
1003
|
+
inputSchema: { type: 'object', properties: {} }
|
|
1004
|
+
},
|
|
1005
|
+
{
|
|
1006
|
+
name: 'meta_version',
|
|
1007
|
+
description: 'Get version info and feature flags',
|
|
1008
|
+
inputSchema: { type: 'object', properties: {} }
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
name: 'meta_schema',
|
|
1012
|
+
description: 'Get full API schema',
|
|
1013
|
+
inputSchema: { type: 'object', properties: {} }
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
name: 'meta_modules',
|
|
1017
|
+
description: 'Check which modules are available',
|
|
1018
|
+
inputSchema: { type: 'object', properties: {} }
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
name: 'meta_history',
|
|
1022
|
+
description: 'Get undo/redo history stack',
|
|
1023
|
+
inputSchema: { type: 'object', properties: {} }
|
|
1024
|
+
}
|
|
1025
|
+
];
|
|
1026
|
+
|
|
1027
|
+
// =============================================================================
|
|
1028
|
+
// Main Server Loop
|
|
1029
|
+
// =============================================================================
|
|
1030
|
+
|
|
1031
|
+
async function main() {
|
|
1032
|
+
const args = process.argv.slice(2);
|
|
1033
|
+
|
|
1034
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
1035
|
+
console.log(`
|
|
1036
|
+
cycleCAD MCP Server
|
|
1037
|
+
Usage: cyclecad-mcp [OPTIONS]
|
|
1038
|
+
|
|
1039
|
+
OPTIONS:
|
|
1040
|
+
--help Show this help message
|
|
1041
|
+
--ws-url URL WebSocket URL (default: ws://localhost:3000/api/ws)
|
|
1042
|
+
--http-url URL HTTP URL (default: http://localhost:3000/api/execute)
|
|
1043
|
+
--debug Enable debug logging
|
|
1044
|
+
--version Show version
|
|
1045
|
+
|
|
1046
|
+
ENVIRONMENT:
|
|
1047
|
+
CYCLECAD_WS_URL WebSocket URL
|
|
1048
|
+
CYCLECAD_HTTP_URL HTTP URL
|
|
1049
|
+
DEBUG_MCP Enable debug logging
|
|
1050
|
+
|
|
1051
|
+
EXAMPLES:
|
|
1052
|
+
npx cyclecad-mcp
|
|
1053
|
+
npx cyclecad-mcp --debug
|
|
1054
|
+
npx cyclecad-mcp --ws-url ws://10.0.0.1:3000/api/ws
|
|
1055
|
+
|
|
1056
|
+
`);
|
|
1057
|
+
process.exit(0);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
1061
|
+
console.log('cyclecad-mcp 1.0.0');
|
|
1062
|
+
process.exit(0);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// Parse environment and CLI args
|
|
1066
|
+
if (args.includes('--debug')) config.debug = true;
|
|
1067
|
+
|
|
1068
|
+
const wsIdx = args.indexOf('--ws-url');
|
|
1069
|
+
if (wsIdx !== -1 && args[wsIdx + 1]) {
|
|
1070
|
+
config.wsUrl = args[wsIdx + 1];
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
const httpIdx = args.indexOf('--http-url');
|
|
1074
|
+
if (httpIdx !== -1 && args[httpIdx + 1]) {
|
|
1075
|
+
config.httpUrl = args[httpIdx + 1];
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
const server = new MCPServer();
|
|
1079
|
+
|
|
1080
|
+
// Setup stdio transport
|
|
1081
|
+
const rl = readline.createInterface({
|
|
1082
|
+
input: process.stdin,
|
|
1083
|
+
output: process.stdout,
|
|
1084
|
+
terminal: false
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
let isInitialized = false;
|
|
1088
|
+
|
|
1089
|
+
rl.on('line', async (line) => {
|
|
1090
|
+
try {
|
|
1091
|
+
const request = JSON.parse(line);
|
|
1092
|
+
let response = null;
|
|
1093
|
+
|
|
1094
|
+
if (config.debug) {
|
|
1095
|
+
console.error(`[MCP] ← ${request.method}`);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
if (request.method === 'initialize') {
|
|
1099
|
+
const result = await server.initialize();
|
|
1100
|
+
isInitialized = true;
|
|
1101
|
+
response = {
|
|
1102
|
+
jsonrpc: '2.0',
|
|
1103
|
+
id: request.id,
|
|
1104
|
+
result
|
|
1105
|
+
};
|
|
1106
|
+
} else if (request.method === 'tools/list') {
|
|
1107
|
+
response = {
|
|
1108
|
+
jsonrpc: '2.0',
|
|
1109
|
+
id: request.id,
|
|
1110
|
+
result: {
|
|
1111
|
+
tools: server.getTools()
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
} else if (request.method === 'tools/call') {
|
|
1115
|
+
const result = await server.callTool(request.params.name, request.params.arguments);
|
|
1116
|
+
response = {
|
|
1117
|
+
jsonrpc: '2.0',
|
|
1118
|
+
id: request.id,
|
|
1119
|
+
result
|
|
1120
|
+
};
|
|
1121
|
+
} else {
|
|
1122
|
+
response = {
|
|
1123
|
+
jsonrpc: '2.0',
|
|
1124
|
+
id: request.id,
|
|
1125
|
+
error: {
|
|
1126
|
+
code: -32601,
|
|
1127
|
+
message: `Unknown method: ${request.method}`
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
if (config.debug) {
|
|
1133
|
+
console.error(`[MCP] → ${response.result ? 'OK' : 'ERROR'}`);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
console.log(JSON.stringify(response));
|
|
1137
|
+
} catch (e) {
|
|
1138
|
+
console.error(`[MCP Server Error] ${e.message}`);
|
|
1139
|
+
console.log(JSON.stringify({
|
|
1140
|
+
jsonrpc: '2.0',
|
|
1141
|
+
error: {
|
|
1142
|
+
code: -32603,
|
|
1143
|
+
message: e.message
|
|
1144
|
+
}
|
|
1145
|
+
}));
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
rl.on('close', () => {
|
|
1150
|
+
process.exit(0);
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
if (config.debug) {
|
|
1154
|
+
console.error('[MCP] Server ready, waiting for requests...');
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
main().catch(e => {
|
|
1159
|
+
console.error('Fatal error:', e);
|
|
1160
|
+
process.exit(1);
|
|
1161
|
+
});
|