nova-control-mcp-server 0.0.6 → 0.0.7

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.
@@ -7,9 +7,239 @@ import { Server as i } from "@modelcontextprotocol/sdk/server/index.js";
7
7
  import { StdioServerTransport as a } from "@modelcontextprotocol/sdk/server/stdio.js";
8
8
  import { StreamableHTTPServerTransport as o } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
9
9
  import { CallToolRequestSchema as s, ListToolsRequestSchema as c } from "@modelcontextprotocol/sdk/types.js";
10
- import { openNova as l, runScript as u } from "nova-control-node";
10
+ import { SerialPort as l } from "serialport";
11
+ //#region ../nova-control-node/dist/nova-control-node.js
12
+ var u = 9600, d = Object.freeze({
13
+ s1: 90,
14
+ s2: 90,
15
+ s3: 110,
16
+ s4: 90,
17
+ s5: 95
18
+ }), f = Object.freeze({
19
+ s1: [45, 135],
20
+ s2: [10, 170],
21
+ s3: [40, 150],
22
+ s4: [30, 180],
23
+ s5: [20, 150]
24
+ }), p = Object.freeze({
25
+ s1: (f.s1[1] - f.s1[0]) / 1e3,
26
+ s2: (f.s2[1] - f.s2[0]) / 1e3,
27
+ s3: (f.s3[1] - f.s3[0]) / 1e3,
28
+ s4: (f.s4[1] - f.s4[0]) / 1e3,
29
+ s5: (f.s5[1] - f.s5[0]) / 1e3
30
+ });
31
+ function m(e, t) {
32
+ let [n, r] = f[t];
33
+ return Math.max(n, Math.min(r, Math.round(e)));
34
+ }
35
+ function h(e) {
36
+ return new Uint8Array([
37
+ m(e.s4, "s4"),
38
+ m(e.s3, "s3"),
39
+ m(e.s2, "s2"),
40
+ m(e.s1, "s1"),
41
+ m(e.s5, "s5")
42
+ ]);
43
+ }
44
+ async function g(e, t) {
45
+ let n = new l({
46
+ path: e,
47
+ baudRate: t,
48
+ autoOpen: !1
49
+ });
50
+ return await new Promise((e, t) => {
51
+ n.open((n) => {
52
+ n == null ? e() : t(n);
53
+ });
54
+ }), await new Promise((e) => setTimeout(e, 2e3)), {
55
+ async write(e) {
56
+ await new Promise((t, r) => {
57
+ n.write(Buffer.from(e), (e) => {
58
+ e == null ? t() : r(e);
59
+ });
60
+ }), await new Promise((e, t) => {
61
+ n.drain((n) => {
62
+ n == null ? e() : t(n);
63
+ });
64
+ });
65
+ },
66
+ destroy() {
67
+ n.close();
68
+ }
69
+ };
70
+ }
71
+ function _(e, t) {
72
+ let n = Math.min(.499, Math.max(0, t)), r = 1 / (1 - n);
73
+ if (e <= n) return r * e * e / (2 * n);
74
+ if (e <= 1 - n) return r * (e - n / 2);
75
+ let i = 1 - e;
76
+ return 1 - r * i * i / (2 * n);
77
+ }
78
+ async function v(e, t = u, n) {
79
+ let r = n?.StepIntervalMs ?? 20, i = n?.RampRatio ?? .25, a = await g(e, t), o = { ...d }, s, c = Promise.resolve();
80
+ function l(e) {
81
+ s = {
82
+ ...s ?? o,
83
+ ...e
84
+ };
85
+ }
86
+ async function f() {
87
+ let e = c;
88
+ c = (async () => {
89
+ try {
90
+ await e;
91
+ } catch {}
92
+ for (; s != null;) {
93
+ let e = s, t = !0, n = { ...o };
94
+ for (let i of [
95
+ "s1",
96
+ "s2",
97
+ "s3",
98
+ "s4",
99
+ "s5"
100
+ ]) {
101
+ let a = e[i] - o[i], s = r > 0 ? p[i] * r : Infinity;
102
+ Math.abs(a) > s ? (n[i] = o[i] + Math.sign(a) * s, t = !1) : n[i] = e[i];
103
+ }
104
+ t && (s = void 0), o = { ...n }, await a.write(h(n)), t || await new Promise((e) => setTimeout(e, r));
105
+ }
106
+ })(), await c;
107
+ }
108
+ async function m(e, t) {
109
+ let n = c;
110
+ c = (async () => {
111
+ try {
112
+ await n;
113
+ } catch {}
114
+ let c = { ...o }, l = r > 0 ? Math.max(1, Math.round(t / r)) : 1;
115
+ s = void 0;
116
+ for (let t = 1; t <= l; t++) {
117
+ let n = _(t / l, i), s = { ...o };
118
+ for (let t of Object.keys(e)) s[t] = Math.round(c[t] + (e[t] - c[t]) * n);
119
+ o = s, await a.write(h(s)), t < l && await new Promise((e) => setTimeout(e, r));
120
+ }
121
+ })(), await c;
122
+ }
123
+ return {
124
+ async home(e) {
125
+ e != null && e > 0 ? await m({ ...d }, e) : (l({ ...d }), await f());
126
+ },
127
+ async shiftHeadTo(e, t) {
128
+ t != null && t > 0 ? await m({ s1: e }, t) : (l({ s1: e }), await f());
129
+ },
130
+ async rollHeadTo(e, t) {
131
+ t != null && t > 0 ? await m({ s2: e }, t) : (l({ s2: e }), await f());
132
+ },
133
+ async pitchHeadTo(e, t) {
134
+ t != null && t > 0 ? await m({ s3: e }, t) : (l({ s3: e }), await f());
135
+ },
136
+ async liftHeadTo(e, t) {
137
+ t != null && t > 0 ? await m({ s5: e }, t) : (l({ s5: e }), await f());
138
+ },
139
+ async rotateBodyTo(e, t) {
140
+ t != null && t > 0 ? await m({ s4: e }, t) : (l({ s4: e }), await f());
141
+ },
142
+ async moveTo(e, t) {
143
+ t != null && t > 0 ? await m(e, t) : (l(e), await f());
144
+ },
145
+ get State() {
146
+ return structuredClone(s ?? o);
147
+ },
148
+ set State(e) {
149
+ s = {
150
+ ...o,
151
+ ...e
152
+ };
153
+ },
154
+ async sendServoState() {
155
+ await f();
156
+ },
157
+ destroy() {
158
+ a.destroy();
159
+ }
160
+ };
161
+ }
162
+ async function y(e, t) {
163
+ let n = t.split("\n");
164
+ for (let t = 0; t < n.length; t++) {
165
+ let r = n[t].trim(), i = t + 1;
166
+ if (r === "" || r.startsWith("#")) continue;
167
+ let a = r.split(/\s+/), o = a[0].toLowerCase();
168
+ switch (!0) {
169
+ case o === "home":
170
+ await e.home();
171
+ break;
172
+ case o === "shift-to": {
173
+ let t = Number(a[1]);
174
+ if (isNaN(t)) throw Error(`line ${i}: shift-to requires a numeric angle, got '${a[1]}'`);
175
+ await e.shiftHeadTo(t);
176
+ break;
177
+ }
178
+ case o === "roll-to": {
179
+ let t = Number(a[1]);
180
+ if (isNaN(t)) throw Error(`line ${i}: roll-to requires a numeric angle, got '${a[1]}'`);
181
+ await e.rollHeadTo(t);
182
+ break;
183
+ }
184
+ case o === "pitch-to": {
185
+ let t = Number(a[1]);
186
+ if (isNaN(t)) throw Error(`line ${i}: pitch-to requires a numeric angle, got '${a[1]}'`);
187
+ await e.pitchHeadTo(t);
188
+ break;
189
+ }
190
+ case o === "rotate-to": {
191
+ let t = Number(a[1]);
192
+ if (isNaN(t)) throw Error(`line ${i}: rotate-to requires a numeric angle, got '${a[1]}'`);
193
+ await e.rotateBodyTo(t);
194
+ break;
195
+ }
196
+ case o === "lift-to": {
197
+ let t = Number(a[1]);
198
+ if (isNaN(t)) throw Error(`line ${i}: lift-to requires a numeric angle, got '${a[1]}'`);
199
+ await e.liftHeadTo(t);
200
+ break;
201
+ }
202
+ case o === "move": {
203
+ let t = {};
204
+ for (let e = 1; e < a.length; e += 2) {
205
+ let n = a[e].toLowerCase(), r = Number(a[e + 1]);
206
+ if (isNaN(r)) throw Error(`line ${i}: '${n}' requires a numeric angle, got '${a[e + 1]}'`);
207
+ switch (n) {
208
+ case "shift-to":
209
+ t.s1 = r;
210
+ break;
211
+ case "roll-to":
212
+ t.s2 = r;
213
+ break;
214
+ case "pitch-to":
215
+ t.s3 = r;
216
+ break;
217
+ case "rotate-to":
218
+ t.s4 = r;
219
+ break;
220
+ case "lift-to":
221
+ t.s5 = r;
222
+ break;
223
+ default: throw Error(`line ${i}: unknown move argument '${n}'`);
224
+ }
225
+ }
226
+ if (Object.keys(t).length === 0) throw Error(`line ${i}: move requires at least one servo argument`);
227
+ e.State = t, await e.sendServoState();
228
+ break;
229
+ }
230
+ case o === "wait": {
231
+ let e = Number(a[1]);
232
+ if (isNaN(e) || e < 0) throw Error(`line ${i}: wait requires a non-negative number in ms, got '${a[1]}'`);
233
+ await new Promise((t) => setTimeout(t, e));
234
+ break;
235
+ }
236
+ default: throw Error(`line ${i}: unknown command '${o}'`);
237
+ }
238
+ }
239
+ }
240
+ //#endregion
11
241
  //#region src/nova-control-mcp-server.ts
12
- function d() {
242
+ function b() {
13
243
  try {
14
244
  let { values: e } = r({
15
245
  args: process.argv.slice(2),
@@ -46,20 +276,20 @@ function d() {
46
276
  process.stderr.write(`nova-control-mcp: ${e.message ?? e}\n`), process.exit(1);
47
277
  }
48
278
  }
49
- var f = "", p = 9600, m;
50
- async function h() {
51
- return m ??= await l(f, p), m;
279
+ var x = "", S = 9600, C;
280
+ async function w() {
281
+ return C ??= await v(x, S), C;
52
282
  }
53
- function g() {
54
- m != null && (m.destroy(), m = void 0);
283
+ function T() {
284
+ C != null && (C.destroy(), C = void 0);
55
285
  }
56
- function _(e, t = 9600) {
57
- f = e, p = t;
286
+ function E(e, t = 9600) {
287
+ x = e, S = t;
58
288
  }
59
- function v() {
60
- g(), f = "", p = 9600;
289
+ function D() {
290
+ T(), x = "", S = 9600;
61
291
  }
62
- var y = [
292
+ var O = [
63
293
  {
64
294
  name: "home",
65
295
  description: "send all servos to their home positions — pass within_ms for smooth, fluid motion with automatic velocity ramp-up and ramp-down; without within_ms the robot moves at constant maximum speed",
@@ -269,104 +499,104 @@ var y = [
269
499
  }
270
500
  }
271
501
  ];
272
- async function b(e) {
502
+ async function k(e) {
273
503
  let t = e.within_ms == null ? void 0 : Number(e.within_ms);
274
- return await (await h()).home(t), "all servos moved to home positions";
504
+ return await (await w()).home(t), "all servos moved to home positions";
275
505
  }
276
- async function x(e) {
506
+ async function A(e) {
277
507
  let t = {};
278
508
  if (e.shift_to != null && (t.s1 = Number(e.shift_to)), e.roll_to != null && (t.s2 = Number(e.roll_to)), e.pitch_to != null && (t.s3 = Number(e.pitch_to)), e.rotate_to != null && (t.s4 = Number(e.rotate_to)), e.lift_to != null && (t.s5 = Number(e.lift_to)), Object.keys(t).length === 0) throw Error("move: at least one of shift_to, roll_to, pitch_to, rotate_to, lift_to is required");
279
509
  let n = e.within_ms == null ? void 0 : Number(e.within_ms);
280
- return await (await h()).moveTo(t, n), `servos updated: ${JSON.stringify(t)}`;
510
+ return await (await w()).moveTo(t, n), `servos updated: ${JSON.stringify(t)}`;
281
511
  }
282
- async function S(e) {
512
+ async function j(e) {
283
513
  let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
284
- return await (await h()).shiftHeadTo(t, n), `s1 (shift) → ${t}°`;
514
+ return await (await w()).shiftHeadTo(t, n), `s1 (shift) → ${t}°`;
285
515
  }
286
- async function C(e) {
516
+ async function M(e) {
287
517
  let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
288
- return await (await h()).rollHeadTo(t, n), `s2 (roll) → ${t}°`;
518
+ return await (await w()).rollHeadTo(t, n), `s2 (roll) → ${t}°`;
289
519
  }
290
- async function w(e) {
520
+ async function N(e) {
291
521
  let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
292
- return await (await h()).pitchHeadTo(t, n), `s3 (pitch) → ${t}°`;
522
+ return await (await w()).pitchHeadTo(t, n), `s3 (pitch) → ${t}°`;
293
523
  }
294
- async function T(e) {
524
+ async function P(e) {
295
525
  let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
296
- return await (await h()).rotateBodyTo(t, n), `s4 (rotate) → ${t}°`;
526
+ return await (await w()).rotateBodyTo(t, n), `s4 (rotate) → ${t}°`;
297
527
  }
298
- async function E(e) {
528
+ async function F(e) {
299
529
  let t = Number(e.angle), n = e.within_ms == null ? void 0 : Number(e.within_ms);
300
- return await (await h()).liftHeadTo(t, n), `s5 (lift) → ${t}°`;
530
+ return await (await w()).liftHeadTo(t, n), `s5 (lift) → ${t}°`;
301
531
  }
302
- async function D() {
303
- return m == null ? "not connected" : (g(), "disconnected");
532
+ async function I() {
533
+ return C == null ? "not connected" : (T(), "disconnected");
304
534
  }
305
- async function O(e) {
535
+ async function L(e) {
306
536
  let t = Number(e.within_ms);
307
537
  if (isNaN(t) || t <= 0) throw Error("move_to: within_ms must be a positive number");
308
538
  let n = {};
309
539
  if (e.s1 != null && (n.s1 = Number(e.s1)), e.s2 != null && (n.s2 = Number(e.s2)), e.s3 != null && (n.s3 = Number(e.s3)), e.s4 != null && (n.s4 = Number(e.s4)), e.s5 != null && (n.s5 = Number(e.s5)), Object.keys(n).length === 0) throw Error("move_to: at least one servo target (s1–s5) must be specified");
310
- return await (await h()).moveTo(n, t), "move completed";
540
+ return await (await w()).moveTo(n, t), "move completed";
311
541
  }
312
- async function k(e) {
542
+ async function R(e) {
313
543
  let t = Number(e.ms);
314
544
  if (isNaN(t) || t < 0) throw Error(`wait: invalid duration '${e.ms}' — expected a non-negative number`);
315
545
  return await new Promise((e) => setTimeout(e, t)), `waited ${t} ms`;
316
546
  }
317
- async function A() {
318
- let e = await h();
547
+ async function z() {
548
+ let e = await w();
319
549
  return JSON.stringify(e.State);
320
550
  }
321
- async function j(e) {
551
+ async function B(e) {
322
552
  let t = String(e.script ?? "");
323
- return await u(await h(), t), "script executed successfully";
553
+ return await y(await w(), t), "script executed successfully";
324
554
  }
325
- function M() {
555
+ function V() {
326
556
  let e = new i({
327
557
  name: "nova-control-mcp-server",
328
- version: "0.0.6"
558
+ version: "0.0.7"
329
559
  }, { capabilities: { tools: {} } });
330
- return e.setRequestHandler(c, async () => ({ tools: y })), e.setRequestHandler(s, async (e) => {
560
+ return e.setRequestHandler(c, async () => ({ tools: O })), e.setRequestHandler(s, async (e) => {
331
561
  let t = e.params.name, n = e.params.arguments ?? {};
332
562
  try {
333
563
  let e;
334
564
  switch (t) {
335
565
  case "home":
336
- e = await b(n);
566
+ e = await k(n);
337
567
  break;
338
568
  case "move":
339
- e = await x(n);
569
+ e = await A(n);
340
570
  break;
341
571
  case "shift_to":
342
- e = await S(n);
572
+ e = await j(n);
343
573
  break;
344
574
  case "roll_to":
345
- e = await C(n);
575
+ e = await M(n);
346
576
  break;
347
577
  case "pitch_to":
348
- e = await w(n);
578
+ e = await N(n);
349
579
  break;
350
580
  case "rotate_to":
351
- e = await T(n);
581
+ e = await P(n);
352
582
  break;
353
583
  case "lift_to":
354
- e = await E(n);
584
+ e = await F(n);
355
585
  break;
356
586
  case "move_to":
357
- e = await O(n);
587
+ e = await L(n);
358
588
  break;
359
589
  case "wait":
360
- e = await k(n);
590
+ e = await R(n);
361
591
  break;
362
592
  case "get_state":
363
- e = await A();
593
+ e = await z();
364
594
  break;
365
595
  case "run_script":
366
- e = await j(n);
596
+ e = await B(n);
367
597
  break;
368
598
  case "disconnect":
369
- e = await D();
599
+ e = await I();
370
600
  break;
371
601
  default: return {
372
602
  content: [{
@@ -391,14 +621,14 @@ function M() {
391
621
  }
392
622
  }), e;
393
623
  }
394
- async function N(e) {
624
+ async function H(e) {
395
625
  let t = new a();
396
626
  await e.connect(t);
397
627
  for (let e of ["SIGINT", "SIGTERM"]) process.on(e, () => {
398
- g(), process.exit(0);
628
+ T(), process.exit(0);
399
629
  });
400
630
  }
401
- async function P(e, t) {
631
+ async function U(e, t) {
402
632
  let r = new o({ sessionIdGenerator: void 0 });
403
633
  await e.connect(r);
404
634
  let i = n(async (e, t) => {
@@ -410,17 +640,17 @@ async function P(e, t) {
410
640
  }), i.once("error", n);
411
641
  });
412
642
  for (let e of ["SIGINT", "SIGTERM"]) process.on(e, async () => {
413
- await r.close(), i.close(), g(), process.exit(0);
643
+ await r.close(), i.close(), T(), process.exit(0);
414
644
  });
415
645
  }
416
- async function F() {
417
- let { Port: e, BaudRate: t, Transport: n, ListenPort: r } = d();
418
- f = e, p = t;
419
- let i = M();
420
- n === "http" ? await P(i, r) : await N(i);
646
+ async function W() {
647
+ let { Port: e, BaudRate: t, Transport: n, ListenPort: r } = b();
648
+ x = e, S = t;
649
+ let i = V();
650
+ n === "http" ? await U(i, r) : await H(i);
421
651
  }
422
- t(process.argv[1]) === e(import.meta.url) && F().catch((e) => {
652
+ t(process.argv[1]) === e(import.meta.url) && W().catch((e) => {
423
653
  process.stderr.write(`nova-control-mcp: fatal: ${e.message ?? e}\n`), process.exit(1);
424
654
  });
425
655
  //#endregion
426
- export { v as _destroyForTests, _ as _setupForTests, M as createServer };
656
+ export { D as _destroyForTests, E as _setupForTests, V as createServer };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nova-control-mcp-server",
3
3
  "description": "MCP server for controlling a NOVA DIY Artificial Intelligence Robot by Creoqode",
4
- "version": "0.0.6",
4
+ "version": "0.0.7",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "nova",