@sknoble/slvsx-mcp-server 0.1.2 → 0.2.0

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/mcp-server.js CHANGED
@@ -41,7 +41,8 @@ function findSlvsxBinary() {
41
41
  // 3. Check if slvsx is in PATH
42
42
  try {
43
43
  const which = execSync('which slvsx 2>/dev/null || where slvsx 2>nul', { encoding: 'utf-8' }).trim();
44
- if (which) return which.split('\n')[0];
44
+ // Handle Windows CRLF line endings by trimming each line
45
+ if (which) return which.split('\n')[0].trim();
45
46
  } catch (e) {
46
47
  // Not in PATH
47
48
  }
@@ -440,49 +441,49 @@ class SlvsxServer {
440
441
  schema: 'slvs-json/1',
441
442
  units: 'mm',
442
443
  entities: [
443
- { id: 'p1', type: 'Point', x: 0, y: 0 },
444
- { id: 'p2', type: 'Point', x: 100, y: 0 },
445
- { id: 'p3', type: 'Point', x: 50, y: 86.6 }
444
+ { type: 'point', id: 'p1', at: [0, 0, 0] },
445
+ { type: 'point', id: 'p2', at: [100, 0, 0] },
446
+ { type: 'point', id: 'p3', at: [50, 86.6, 0] }
446
447
  ],
447
448
  constraints: [
448
- { type: 'Fixed', entity: 'p1' },
449
- { type: 'Distance', entities: ['p1', 'p2'], distance: 100 },
450
- { type: 'Distance', entities: ['p2', 'p3'], distance: 100 },
451
- { type: 'Distance', entities: ['p3', 'p1'], distance: 100 }
449
+ { type: 'fixed', entity: 'p1' },
450
+ { type: 'distance', between: ['p1', 'p2'], value: 100 },
451
+ { type: 'distance', between: ['p2', 'p3'], value: 100 },
452
+ { type: 'distance', between: ['p3', 'p1'], value: 100 }
452
453
  ]
453
454
  },
454
455
  square: {
455
456
  schema: 'slvs-json/1',
456
457
  units: 'mm',
457
458
  entities: [
458
- { id: 'p1', type: 'Point', x: 0, y: 0 },
459
- { id: 'p2', type: 'Point', x: 100, y: 0 },
460
- { id: 'p3', type: 'Point', x: 100, y: 100 },
461
- { id: 'p4', type: 'Point', x: 0, y: 100 },
462
- { id: 'l1', type: 'Line', points: ['p1', 'p2'] },
463
- { id: 'l2', type: 'Line', points: ['p2', 'p3'] },
464
- { id: 'l3', type: 'Line', points: ['p3', 'p4'] },
465
- { id: 'l4', type: 'Line', points: ['p4', 'p1'] }
459
+ { type: 'point', id: 'p1', at: [0, 0, 0] },
460
+ { type: 'point', id: 'p2', at: [100, 0, 0] },
461
+ { type: 'point', id: 'p3', at: [100, 100, 0] },
462
+ { type: 'point', id: 'p4', at: [0, 100, 0] },
463
+ { type: 'line', id: 'l1', p1: 'p1', p2: 'p2' },
464
+ { type: 'line', id: 'l2', p1: 'p2', p2: 'p3' },
465
+ { type: 'line', id: 'l3', p1: 'p3', p2: 'p4' },
466
+ { type: 'line', id: 'l4', p1: 'p4', p2: 'p1' }
466
467
  ],
467
468
  constraints: [
468
- { type: 'Fixed', entity: 'p1' },
469
- { type: 'Fixed', entity: 'p2' },
470
- { type: 'Perpendicular', entities: ['l1', 'l2'] },
471
- { type: 'Perpendicular', entities: ['l2', 'l3'] },
472
- { type: 'Perpendicular', entities: ['l3', 'l4'] },
473
- { type: 'Equal', entities: ['l1', 'l2'] }
469
+ { type: 'fixed', entity: 'p1' },
470
+ { type: 'fixed', entity: 'p2' },
471
+ { type: 'perpendicular', a: 'l1', b: 'l2' },
472
+ { type: 'perpendicular', a: 'l2', b: 'l3' },
473
+ { type: 'perpendicular', a: 'l3', b: 'l4' },
474
+ { type: 'equal_length', entities: ['l1', 'l2'] }
474
475
  ]
475
476
  },
476
477
  circle: {
477
478
  schema: 'slvs-json/1',
478
479
  units: 'mm',
479
480
  entities: [
480
- { id: 'center', type: 'Point', x: 50, y: 50 },
481
- { id: 'c1', type: 'Circle', center: 'center', radius: 30 }
481
+ { type: 'point', id: 'center', at: [50, 50, 0] },
482
+ { type: 'circle', id: 'c1', center: [50, 50, 0], diameter: 60 }
482
483
  ],
483
484
  constraints: [
484
- { type: 'Fixed', entity: 'center' },
485
- { type: 'Radius', entity: 'c1', radius: 30 }
485
+ { type: 'fixed', entity: 'center' },
486
+ { type: 'diameter', circle: 'c1', value: 60 }
486
487
  ]
487
488
  },
488
489
  linkage: {
@@ -492,21 +493,20 @@ class SlvsxServer {
492
493
  input_angle: 45
493
494
  },
494
495
  entities: [
495
- { id: 'ground1', type: 'Point', x: 0, y: 0 },
496
- { id: 'ground2', type: 'Point', x: 100, y: 0 },
497
- { id: 'joint1', type: 'Point', x: 30, y: 30 },
498
- { id: 'joint2', type: 'Point', x: 70, y: 40 },
499
- { id: 'link1', type: 'Line', points: ['ground1', 'joint1'] },
500
- { id: 'link2', type: 'Line', points: ['joint1', 'joint2'] },
501
- { id: 'link3', type: 'Line', points: ['joint2', 'ground2'] }
496
+ { type: 'point', id: 'ground1', at: [0, 0, 0] },
497
+ { type: 'point', id: 'ground2', at: [100, 0, 0] },
498
+ { type: 'point', id: 'joint1', at: [30, 30, 0] },
499
+ { type: 'point', id: 'joint2', at: [70, 40, 0] },
500
+ { type: 'line', id: 'link1', p1: 'ground1', p2: 'joint1' },
501
+ { type: 'line', id: 'link2', p1: 'joint1', p2: 'joint2' },
502
+ { type: 'line', id: 'link3', p1: 'joint2', p2: 'ground2' }
502
503
  ],
503
504
  constraints: [
504
- { type: 'Fixed', entity: 'ground1' },
505
- { type: 'Fixed', entity: 'ground2' },
506
- { type: 'Distance', entities: ['ground1', 'joint1'], distance: 40 },
507
- { type: 'Distance', entities: ['joint1', 'joint2'], distance: 50 },
508
- { type: 'Distance', entities: ['joint2', 'ground2'], distance: 35 },
509
- { type: 'Angle', entities: ['link1'], angle: '$input_angle' }
505
+ { type: 'fixed', entity: 'ground1' },
506
+ { type: 'fixed', entity: 'ground2' },
507
+ { type: 'distance', between: ['ground1', 'joint1'], value: 40 },
508
+ { type: 'distance', between: ['joint1', 'joint2'], value: 50 },
509
+ { type: 'distance', between: ['joint2', 'ground2'], value: 35 }
510
510
  ]
511
511
  },
512
512
  parametric: {
@@ -515,48 +515,61 @@ class SlvsxServer {
515
515
  parameters: {
516
516
  width: 150,
517
517
  height: 100,
518
- hole_radius: 10
518
+ hole_diameter: 20
519
519
  },
520
520
  entities: [
521
- { id: 'p1', type: 'Point', x: 0, y: 0 },
522
- { id: 'p2', type: 'Point', x: '$width', y: 0 },
523
- { id: 'p3', type: 'Point', x: '$width', y: '$height' },
524
- { id: 'p4', type: 'Point', x: 0, y: '$height' },
525
- { id: 'hole_center', type: 'Point', x: 75, y: 50 },
526
- { id: 'hole', type: 'Circle', center: 'hole_center', radius: '$hole_radius' }
521
+ { type: 'point', id: 'p1', at: [0, 0, 0] },
522
+ { type: 'point', id: 'p2', at: ['$width', 0, 0] },
523
+ { type: 'point', id: 'p3', at: ['$width', '$height', 0] },
524
+ { type: 'point', id: 'p4', at: [0, '$height', 0] },
525
+ { type: 'line', id: 'bottom', p1: 'p1', p2: 'p2' },
526
+ { type: 'line', id: 'right', p1: 'p2', p2: 'p3' },
527
+ { type: 'line', id: 'top', p1: 'p3', p2: 'p4' },
528
+ { type: 'line', id: 'left', p1: 'p4', p2: 'p1' },
529
+ { type: 'circle', id: 'hole', center: [75, 50, 0], diameter: '$hole_diameter' }
527
530
  ],
528
531
  constraints: [
529
- { type: 'Fixed', entity: 'p1' },
530
- { type: 'HorizontalDistance', entities: ['p1', 'p2'], distance: '$width' },
531
- { type: 'VerticalDistance', entities: ['p1', 'p4'], distance: '$height' },
532
- { type: 'Horizontal', entity: 'p2' },
533
- { type: 'Vertical', entity: 'p4' },
534
- { type: 'Radius', entity: 'hole', radius: '$hole_radius' }
532
+ { type: 'fixed', entity: 'p1' },
533
+ { type: 'distance', between: ['p1', 'p2'], value: '$width' },
534
+ { type: 'distance', between: ['p1', 'p4'], value: '$height' },
535
+ { type: 'perpendicular', a: 'bottom', b: 'right' },
536
+ { type: 'perpendicular', a: 'right', b: 'top' },
537
+ { type: 'diameter', circle: 'hole', value: '$hole_diameter' }
535
538
  ]
536
539
  },
537
540
  '3d': {
538
541
  schema: 'slvs-json/1',
539
542
  units: 'mm',
540
543
  entities: [
541
- { id: 'p1', type: 'Point', x: 0, y: 0, z: 0 },
542
- { id: 'p2', type: 'Point', x: 100, y: 0, z: 0 },
543
- { id: 'p3', type: 'Point', x: 100, y: 100, z: 0 },
544
- { id: 'p4', type: 'Point', x: 0, y: 100, z: 0 },
545
- { id: 'p5', type: 'Point', x: 0, y: 0, z: 50 },
546
- { id: 'p6', type: 'Point', x: 100, y: 0, z: 50 },
547
- { id: 'p7', type: 'Point', x: 100, y: 100, z: 50 },
548
- { id: 'p8', type: 'Point', x: 0, y: 100, z: 50 }
544
+ { type: 'point', id: 'p1', at: [0, 0, 0] },
545
+ { type: 'point', id: 'p2', at: [100, 0, 0] },
546
+ { type: 'point', id: 'p3', at: [100, 100, 0] },
547
+ { type: 'point', id: 'p4', at: [0, 100, 0] },
548
+ { type: 'point', id: 'p5', at: [0, 0, 50] },
549
+ { type: 'point', id: 'p6', at: [100, 0, 50] },
550
+ { type: 'point', id: 'p7', at: [100, 100, 50] },
551
+ { type: 'point', id: 'p8', at: [0, 100, 50] },
552
+ { type: 'line', id: 'base1', p1: 'p1', p2: 'p2' },
553
+ { type: 'line', id: 'base2', p1: 'p2', p2: 'p3' },
554
+ { type: 'line', id: 'base3', p1: 'p3', p2: 'p4' },
555
+ { type: 'line', id: 'base4', p1: 'p4', p2: 'p1' },
556
+ { type: 'line', id: 'top1', p1: 'p5', p2: 'p6' },
557
+ { type: 'line', id: 'top2', p1: 'p6', p2: 'p7' },
558
+ { type: 'line', id: 'top3', p1: 'p7', p2: 'p8' },
559
+ { type: 'line', id: 'top4', p1: 'p8', p2: 'p5' },
560
+ { type: 'line', id: 'vert1', p1: 'p1', p2: 'p5' },
561
+ { type: 'line', id: 'vert2', p1: 'p2', p2: 'p6' },
562
+ { type: 'line', id: 'vert3', p1: 'p3', p2: 'p7' },
563
+ { type: 'line', id: 'vert4', p1: 'p4', p2: 'p8' }
549
564
  ],
550
565
  constraints: [
551
- { type: 'Fixed', entity: 'p1' },
552
- { type: 'Distance', entities: ['p1', 'p2'], distance: 100 },
553
- { type: 'Distance', entities: ['p2', 'p3'], distance: 100 },
554
- { type: 'Distance', entities: ['p3', 'p4'], distance: 100 },
555
- { type: 'Distance', entities: ['p4', 'p1'], distance: 100 },
556
- { type: 'Distance', entities: ['p1', 'p5'], distance: 50 },
557
- { type: 'Distance', entities: ['p2', 'p6'], distance: 50 },
558
- { type: 'Distance', entities: ['p3', 'p7'], distance: 50 },
559
- { type: 'Distance', entities: ['p4', 'p8'], distance: 50 }
566
+ { type: 'fixed', entity: 'p1' },
567
+ { type: 'fixed', entity: 'p2' },
568
+ { type: 'fixed', entity: 'p4' },
569
+ { type: 'distance', between: ['p1', 'p5'], value: 50 },
570
+ { type: 'distance', between: ['p2', 'p6'], value: 50 },
571
+ { type: 'distance', between: ['p3', 'p7'], value: 50 },
572
+ { type: 'distance', between: ['p4', 'p8'], value: 50 }
560
573
  ]
561
574
  }
562
575
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sknoble/slvsx-mcp-server",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server for SLVSX geometric constraint solver",
5
5
  "type": "module",
6
6
  "bin": {
@@ -74,16 +74,25 @@ async function downloadFile(url, dest) {
74
74
  const follow = (url) => {
75
75
  https.get(url, (res) => {
76
76
  if (res.statusCode === 302 || res.statusCode === 301) {
77
+ // Consume the redirect response to release the connection
78
+ res.resume();
77
79
  follow(res.headers.location);
78
80
  return;
79
81
  }
80
82
 
81
83
  if (res.statusCode !== 200) {
84
+ res.resume(); // Consume response body
82
85
  reject(new Error(`Download failed with status ${res.statusCode}`));
83
86
  return;
84
87
  }
85
88
 
86
89
  const file = createWriteStream(dest);
90
+ // Handle file stream errors to prevent hanging
91
+ file.on('error', (err) => {
92
+ file.close();
93
+ fs.unlink(dest, () => {}); // Clean up partial file
94
+ reject(err);
95
+ });
87
96
  res.pipe(file);
88
97
  file.on('finish', () => {
89
98
  file.close();