@nataliapc/mcp-openmsx 1.0.2 → 1.1.2

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/README.md CHANGED
@@ -12,13 +12,13 @@ Currently, the MCP server requires Linux to run. It has not been tested on Windo
12
12
 
13
13
  This project creates a bridge between modern AI-assisted development (e.g. GitHub Copilot, Claude Desktop) and retro computing (MSX systems) by providing:
14
14
 
15
- - **Emulator Control**: Launch, configure, and manage openMSX instances
16
- - **Media Management**: Handle ROM cartridges, floppy disks, and cassette tapes
17
- - **Debugging Tools**: Full CPU debugging with breakpoints, memory inspection, and step execution
18
- - **Video Control**: VDP register manipulation and screen capture
19
- - **Memory Operations**: Read/write RAM, VRAM, and I/O port access
20
- - **Automation**: Keyboard input simulation and savestate management
21
- - **Dual Transport**: Support for both stdio and HTTP transports
15
+ - **Emulator Control**: Launch, configure, manage openMSX instances, and replay timelines.
16
+ - **Media Management**: Handle ROM cartridges, floppy disks, and cassette tapes.
17
+ - **Debugging Tools**: Full CPU debugging with breakpoints, memory inspection, and step execution.
18
+ - **Video Control**: VDP register manipulation and screen capture.
19
+ - **Memory Operations**: Read/write RAM, VRAM, and I/O port access.
20
+ - **Automation**: Keyboard input simulation and savestate management.
21
+ - **Dual Transport**: Support for both stdio and HTTP transports.
22
22
 
23
23
  ## 🏗️ Architecture
24
24
 
@@ -44,7 +44,8 @@ The MCP server translates high-level commands from your Copilot AI into `TCL` co
44
44
  ## 🛠️ Available MCP Tools
45
45
 
46
46
  ### Emulator Control Tools
47
- - `emu_control`: Controls an openMSX emulator: _`launch`, `close`, `powerOn`, `powerOff`, `reset`, `getEmulatorSpeed`, `setEmulatorSpeed`, `machineList`, `extensionList`_.
47
+ - `emu_control`: Controls an openMSX emulator: _`launch`, `close`, `powerOn`, `powerOff`, `reset`, `getEmulatorSpeed`, `setEmulatorSpeed`, `machineList`, `extensionList`, `wait`_.
48
+ - `emu_replay`: Controls emulation timeline: _`start`, `strop`, `status`, `goBack`, `absoluteGoto`, `truncate`, `saveReplay`, `loadReplay`_.
48
49
  - `emu_info`: Obtain informacion about the current emulated machine: _`getStatus`, `getSlotsMap`, `getIOPortsMap`_.
49
50
  - `emu_media`: Manage ROM, disk, and tape media: _`tapeInsert`, `tapeRewind`, `tapeEject`, `romInsert`, `romEject`, `diskInsert`, `diskInsertFolder`, `diskEject`_.
50
51
  - `emu_vdp`: Manage VDP (Video Display Processor): _`getPalette`, `getRegisters`, `getRegisterValue`, `setRegisterValue`, `screenGetMode`, `screenGetFullText`_.
@@ -127,8 +128,8 @@ Add to your `claude_desktop_config.json`:
127
128
  |----------|-------------|---------------|---------|
128
129
  | `OPENMSX_EXECUTABLE` | Path or command to the openMSX executable | `openmsx` | `/usr/local/bin/openmsx` |
129
130
  | `OPENMSX_SHARE_DIR` | Directory containing openMSX data files (machines, extensions, etc.) | System dependent | `/home/myuser/.openmsx/share` |
130
- | `OPENMSX_SCREENSHOT_DIR` | Directory where screenshots will be saved | Current working directory | `/myproject/screenshots` |
131
- | `OPENMSX_SCREENDUMP_DIR` | Directory where screen dumps will be saved | Current working directory | `/myproject/screendumps` |
131
+ | `OPENMSX_SCREENSHOT_DIR` | Directory where screenshots will be saved | Default for openmsx | `/myproject/screenshots` |
132
+ | `OPENMSX_SCREENDUMP_DIR` | Directory where screen dumps will be saved | Default for openmsx | `/myproject/screendumps` |
132
133
  | `MCP_TRANSPORT` | Transport mode (`stdio` or `http`) | `stdio` | `http` |
133
134
  | `MCP_HTTP_PORT` | Port number for HTTP transport mode | `3000` | `8080` |
134
135
 
package/dist/openmsx.js CHANGED
@@ -7,6 +7,7 @@
7
7
  import fs from "fs/promises";
8
8
  import { extractDescriptionFromXML, decodeHtmlEntities } from "./utils.js";
9
9
  import { spawn } from 'child_process';
10
+ import path from 'path';
10
11
  /**
11
12
  * OpenMSX class for controlling the openMSX emulator via TCL commands over TCP socket
12
13
  */
@@ -89,8 +90,21 @@ export class OpenMSX {
89
90
  this.sendCommand('set renderer SDLGL-PP');
90
91
  // set machine on
91
92
  this.sendCommand('set power on');
93
+ // start reverse replay mode
94
+ this.sendCommand('reverse start');
92
95
  // Return success message
93
- safeResolve('Ok: openMSX emulator launched successfully');
96
+ let result = 'Ok: openMSX emulator launched successfully';
97
+ if (machine) {
98
+ result += ` with machine "${machine}"`;
99
+ }
100
+ if (extensions && extensions.length > 0) {
101
+ if (machine) {
102
+ result += ' and';
103
+ }
104
+ result += ` with extensions: "${extensions.join('", "')}"`;
105
+ }
106
+ result += ', is powered on, and replay mode is started.';
107
+ safeResolve(result);
94
108
  }
95
109
  catch (error) {
96
110
  safeResolve(`Error: Failed to send control commands - ${error instanceof Error ? error.message : 'Unknown error'}`);
@@ -179,7 +193,7 @@ export class OpenMSX {
179
193
  const trimmedLine = param.trim();
180
194
  if (trimmedLine) {
181
195
  const value = await this.sendCommand(`machine_info ${trimmedLine}`);
182
- machineInfo[trimmedLine] = value;
196
+ machineInfo[trimmedLine] = value.trim();
183
197
  }
184
198
  }
185
199
  return JSON.stringify(machineInfo, null, 2);
@@ -205,7 +219,7 @@ export class OpenMSX {
205
219
  .map(async (file) => {
206
220
  return {
207
221
  name: file.replace('.xml', ''),
208
- description: await extractDescriptionFromXML(`${machinesDirectory}+path.sep+${file}`)
222
+ description: await extractDescriptionFromXML(path.join(machinesDirectory, file))
209
223
  };
210
224
  }));
211
225
  }
@@ -232,7 +246,7 @@ export class OpenMSX {
232
246
  .map(async (file) => {
233
247
  return {
234
248
  name: file.replace('.xml', ''),
235
- description: await extractDescriptionFromXML(`${extensionDirectory}+path.sep+${file}`)
249
+ description: await extractDescriptionFromXML(path.join(extensionDirectory, file))
236
250
  };
237
251
  }));
238
252
  }
package/dist/server.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * through TCL commands via stdio.
7
7
  *
8
8
  * @package @nataliapc/mcp-openmsx
9
- * @version 1.0.2
9
+ * @version 1.1.2
10
10
  * @author Natalia Pujol Cremades (@nataliapc)
11
11
  * @license GPL2
12
12
  */
@@ -21,7 +21,7 @@ import fs from "fs/promises";
21
21
  import path from "path";
22
22
  import { openMSXInstance } from "./openmsx.js";
23
23
  // Version info for CLI
24
- const PACKAGE_VERSION = "1.0.2";
24
+ const PACKAGE_VERSION = "1.1.2";
25
25
  // Defaults for openMSX paths
26
26
  var OPENMSX_EXECUTABLE = 'openmsx';
27
27
  var OPENMSX_SHARE_DIR = '/usr/share/openmsx';
@@ -39,41 +39,30 @@ function registerAllTools(server) {
39
39
  "emu_control",
40
40
  // Description of the tool (what it does)
41
41
  "Controls an openMSX emulator. Commands: " +
42
- "'launch [machine] [extensions]': opens a powered on openMSX emulator, machine and extensions parameters could be specified, always use 'machine_list' and 'extension_list' resources to obtain valid values. " +
42
+ "'launch [machine] [extensions]': opens a powered-on openMSX emulator; machine and extensions parameters can be specified; use 'machineList' and 'extensionList' tools to obtain valid values. " +
43
43
  "'close': closes the openMSX emulator. " +
44
44
  "'powerOn': powers on the openMSX emulator. " +
45
45
  "'powerOff': powers off the openMSX emulator. " +
46
46
  "'reset': resets the current machine. " +
47
- "'getEmulatorSpeed': get current emulator speed in percentage, default is 100. " +
48
- "'setEmulatorSpeed <emuspeed>': set the emulator speed in percentage, valid values are 1-10000, default is 100. " +
49
- "'machineList': get a list of all available MSX machines that can be emulated with openMSX. " +
50
- "'extensionList': get a list of all available MSX extensions that can be used with openMSX. ",
47
+ "'getEmulatorSpeed': gets the current emulator speed as a percentage, default is 100. " +
48
+ "'setEmulatorSpeed <emuspeed>': sets the emulator speed as a percentage, valid values are 1-10000, default is 100. " +
49
+ "'machineList': gets a list of all available MSX machines that can be emulated with openMSX. " +
50
+ "'extensionList': gets a list of all available MSX extensions that can be used with openMSX. " +
51
+ "'wait <seconds>': performs a wait for the specified number of seconds, default is 2. ",
51
52
  // Schema for the tool (input validation)
52
53
  {
53
- command: z.enum(["launch", "close", "powerOn", "powerOff", "reset", "getEmulatorSpeed", "setEmulatorSpeed", "machineList", "extensionList"]),
54
+ command: z.enum(["launch", "close", "powerOn", "powerOff", "reset", "getEmulatorSpeed", "setEmulatorSpeed", "machineList", "extensionList", "wait"]),
54
55
  machine: z.string().min(1).max(100).optional(),
55
56
  extensions: z.array(z.string().min(1).max(100)).optional(),
56
57
  emuspeed: z.number().min(1).max(10000).optional().default(100),
58
+ seconds: z.number().min(1).max(10).optional().default(2), // Seconds to wait
57
59
  },
58
60
  // Handler for the tool (function to be executed when the tool is called)
59
- async ({ command, machine, extensions, emuspeed }) => {
61
+ async ({ command, machine, extensions, emuspeed, seconds }) => {
60
62
  let result = "Error";
61
63
  switch (command) {
62
64
  case "launch":
63
65
  result = await openMSXInstance.emu_launch(OPENMSX_EXECUTABLE, machine || "", extensions || []);
64
- // Check if launch was successful
65
- if (result === "Ok") {
66
- result = "openMSX emulator launched";
67
- if (machine) {
68
- result += ` with machine "${machine}"`;
69
- }
70
- if (extensions && extensions.length > 0) {
71
- if (machine) {
72
- result += ' and ';
73
- }
74
- result += ` with extensions: ${extensions.join(', ')}`;
75
- }
76
- }
77
66
  break;
78
67
  case "close":
79
68
  result = await openMSXInstance.emu_close();
@@ -104,17 +93,18 @@ function registerAllTools(server) {
104
93
  case "extensionList":
105
94
  result = await openMSXInstance.getExtensionList(EXTENSIONS_DIR);
106
95
  break;
96
+ case "wait":
97
+ await new Promise(resolve => setTimeout(resolve, seconds * 1000));
98
+ result = `Waited for ${seconds} seconds.`;
99
+ break;
107
100
  default:
108
101
  result = `Error: Unknown command "${command}".`;
109
102
  break;
110
103
  }
111
104
  // Return result with proper format for MCP
112
- return {
113
- content: [{
114
- type: "text",
115
- text: result === '' ? 'Ok' : result,
116
- }],
117
- };
105
+ return getResponseContent([
106
+ result
107
+ ]);
118
108
  });
119
109
  server.tool(
120
110
  // Name of the tool (used to call it)
@@ -166,21 +156,15 @@ function registerAllTools(server) {
166
156
  tclCommand = "diska eject";
167
157
  break;
168
158
  default:
169
- return {
170
- content: [{
171
- type: "text",
172
- text: `Error: Unknown emulator media command "${command}".`,
173
- }],
174
- };
159
+ return getResponseContent([
160
+ `Error: Unknown emulator media command "${command}".`
161
+ ]);
175
162
  }
176
163
  const response = await openMSXInstance.sendCommand(tclCommand);
177
164
  // Return the response from openMSX
178
- return {
179
- content: [{
180
- type: "text",
181
- text: response === '' ? 'Ok' : response,
182
- }],
183
- };
165
+ return getResponseContent([
166
+ response
167
+ ]);
184
168
  });
185
169
  server.tool(
186
170
  // Name of the tool (used to call it)
@@ -199,12 +183,9 @@ function registerAllTools(server) {
199
183
  let tclCommand;
200
184
  switch (command) {
201
185
  case "getStatus":
202
- return {
203
- content: [{
204
- type: "text",
205
- text: await openMSXInstance.emu_status(),
206
- }],
207
- };
186
+ return getResponseContent([
187
+ await openMSXInstance.emu_status()
188
+ ]);
208
189
  case "getSlotsMap":
209
190
  tclCommand = "slotmap";
210
191
  break;
@@ -212,20 +193,14 @@ function registerAllTools(server) {
212
193
  tclCommand = "iomap";
213
194
  break;
214
195
  default:
215
- return {
216
- content: [{
217
- type: "text",
218
- text: `Error: Unknown emulator info command "${command}".`,
219
- }],
220
- };
196
+ return getResponseContent([
197
+ `Error: Unknown emulator info command "${command}".`
198
+ ]);
221
199
  }
222
200
  const response = await openMSXInstance.sendCommand(tclCommand);
223
- return {
224
- content: [{
225
- type: "text",
226
- text: response,
227
- }],
228
- };
201
+ return getResponseContent([
202
+ response
203
+ ]);
229
204
  });
230
205
  server.tool(
231
206
  // Name of the tool (used to call it)
@@ -265,35 +240,18 @@ function registerAllTools(server) {
265
240
  break;
266
241
  case "screenGetFullText":
267
242
  const response = await openMSXInstance.sendCommand('get_screen');
268
- return response.startsWith('Error:') ? {
269
- content: [{
270
- type: "text",
271
- text: response,
272
- }]
273
- } : {
274
- content: [{
275
- type: "text",
276
- text: "The screen text is:",
277
- }, {
278
- type: "text",
279
- text: response,
280
- }],
281
- };
243
+ return response.startsWith('Error:') ?
244
+ getResponseContent([response]) :
245
+ getResponseContent(["The screen text is:", response]);
282
246
  default:
283
- return {
284
- content: [{
285
- type: "text",
286
- text: `Error: Unknown emulator vdp command "${command}".`,
287
- }],
288
- };
247
+ return getResponseContent([
248
+ `Error: Unknown emulator vdp command "${command}".`
249
+ ]);
289
250
  }
290
251
  const response = await openMSXInstance.sendCommand(tclCommand);
291
- return {
292
- content: [{
293
- type: "text",
294
- text: response === "" ? "Ok" : response,
295
- }],
296
- };
252
+ return getResponseContent([
253
+ response
254
+ ]);
297
255
  });
298
256
  server.tool(
299
257
  // Name of the tool (used to call it)
@@ -343,20 +301,14 @@ function registerAllTools(server) {
343
301
  tclCommand = `run_to ${address}`;
344
302
  break;
345
303
  default:
346
- return {
347
- content: [{
348
- type: "text",
349
- text: `Error: Unknown debug command "${command}".`,
350
- }],
351
- };
304
+ return getResponseContent([
305
+ `Error: Unknown debug command "${command}".`
306
+ ]);
352
307
  }
353
308
  const response = await openMSXInstance.sendCommand(tclCommand);
354
- return {
355
- content: [{
356
- type: "text",
357
- text: response === '' ? 'Ok' : response,
358
- }],
359
- };
309
+ return getResponseContent([
310
+ response
311
+ ]);
360
312
  });
361
313
  server.tool(
362
314
  // Name of the tool (used to call it)
@@ -401,20 +353,14 @@ function registerAllTools(server) {
401
353
  tclCommand = "get_active_cpu";
402
354
  break;
403
355
  default:
404
- return {
405
- content: [{
406
- type: "text",
407
- text: `Error: Unknown memory command "${command}".`,
408
- }],
409
- };
356
+ return getResponseContent([
357
+ `Error: Unknown memory command "${command}".`
358
+ ]);
410
359
  }
411
360
  const response = await openMSXInstance.sendCommand(tclCommand);
412
- return {
413
- content: [{
414
- type: "text",
415
- text: response === '' ? 'Ok' : response,
416
- }],
417
- };
361
+ return getResponseContent([
362
+ response
363
+ ]);
418
364
  });
419
365
  server.tool(
420
366
  // Name of the tool (used to call it)
@@ -463,20 +409,14 @@ function registerAllTools(server) {
463
409
  tclCommand = "listing";
464
410
  break;
465
411
  default:
466
- return {
467
- content: [{
468
- type: "text",
469
- text: `Error: Unknown memory command "${command}".`,
470
- }],
471
- };
412
+ return getResponseContent([
413
+ `Error: Unknown memory command "${command}".`
414
+ ]);
472
415
  }
473
416
  const response = await openMSXInstance.sendCommand(tclCommand);
474
- return {
475
- content: [{
476
- type: "text",
477
- text: response === '' ? 'Ok' : response,
478
- }],
479
- };
417
+ return getResponseContent([
418
+ response
419
+ ]);
480
420
  });
481
421
  server.tool(
482
422
  // Name of the tool (used to call it)
@@ -508,20 +448,14 @@ function registerAllTools(server) {
508
448
  tclCommand = `vpoke ${address} ${value8}`;
509
449
  break;
510
450
  default:
511
- return {
512
- content: [{
513
- type: "text",
514
- text: `Error: Unknown video memory command "${command}".`,
515
- }],
516
- };
451
+ return getResponseContent([
452
+ `Error: Unknown video memory command "${command}".`
453
+ ]);
517
454
  }
518
455
  const response = await openMSXInstance.sendCommand(tclCommand);
519
- return {
520
- content: [{
521
- type: "text",
522
- text: response === '' ? 'Ok' : response,
523
- }],
524
- };
456
+ return getResponseContent([
457
+ response
458
+ ]);
525
459
  });
526
460
  server.tool(
527
461
  // Name of the tool (used to call it)
@@ -553,20 +487,14 @@ function registerAllTools(server) {
553
487
  tclCommand = 'debug list_bp';
554
488
  break;
555
489
  default:
556
- return {
557
- content: [{
558
- type: "text",
559
- text: `Error: Unknown breakpoint command "${command}".`,
560
- }],
561
- };
490
+ return getResponseContent([
491
+ `Error: Unknown breakpoint command "${command}".`
492
+ ]);
562
493
  }
563
494
  const response = await openMSXInstance.sendCommand(tclCommand);
564
- return {
565
- content: [{
566
- type: "text",
567
- text: response,
568
- }],
569
- };
495
+ return getResponseContent([
496
+ response
497
+ ]);
570
498
  });
571
499
  server.tool(
572
500
  // Name of the tool (used to call it)
@@ -600,23 +528,77 @@ function registerAllTools(server) {
600
528
  tclCommand = 'list_savestates';
601
529
  break;
602
530
  default:
603
- return {
604
- content: [{
605
- type: "text",
606
- text: `Error: Unknown savestate command "${command}".`,
607
- }],
608
- };
531
+ return getResponseContent([
532
+ `Error: Unknown savestate command "${command}".`
533
+ ]);
609
534
  }
610
535
  const response = await openMSXInstance.sendCommand(tclCommand);
611
- return {
612
- content: [{
613
- type: "text",
614
- text: textResponse,
615
- }, {
616
- type: "text",
617
- text: response,
618
- }],
619
- };
536
+ return getResponseContent([
537
+ textResponse,
538
+ response
539
+ ]);
540
+ });
541
+ server.tool(
542
+ // Name of the tool (used to call it)
543
+ "emu_replay",
544
+ // Description of the tool (what it does)
545
+ "When replay is enabled (the default) the emulator collect data while emulating, which enables you to go back and forward in MSX time; consider do a 'pause' to maintain the the timeline before a 'goBack' or 'absoluteGoto'. Commands: " +
546
+ "'start': starts the replay mode (enabled by default when emulator is launched). " +
547
+ "'stop': stops the replay mode. " +
548
+ "'status': gives information about the replay feature and the data that is collected. " +
549
+ "'goBack <seconds>': go back specified seconds (1-60) in the timeline, you cannot go back to a time before the time the replay started. " +
550
+ "'absoluteGoto <time>': go to the indicated absolute time in seconds in the MSX timeline, if time is before replay started it will jump to the time when is started. " +
551
+ "'truncate': stop replaying and wipe all the future replay data after now. " +
552
+ "'saveReplay [filename]': saves the current replay data to a file (extension .omr), filename is returned in the response. " +
553
+ "'loadReplay <filename>': loads a previously saved replay file (extension .omr), starts replaying from the begin, and starts replay mode.",
554
+ // Schema for the tool (input validation)
555
+ {
556
+ command: z.enum(["start", "stop", "status", "goBack", "absoluteGoto", "truncate", "saveReplay", "loadReplay"]),
557
+ seconds: z.number().min(1).max(60).optional(), // Seconds to go back
558
+ time: z.string().regex(/^\d+$/).optional(), // Time in seconds to go to
559
+ filename: z.string().min(1).max(200).optional(), // Filename to save/load replay
560
+ },
561
+ // Handler for the tool (function to be executed when the tool is called)
562
+ async ({ command, seconds, time, filename }) => {
563
+ let tclCommand;
564
+ switch (command) {
565
+ case "start":
566
+ tclCommand = "reverse start";
567
+ break;
568
+ case "stop":
569
+ tclCommand = "reverse stop";
570
+ break;
571
+ case "status":
572
+ tclCommand = "reverse status";
573
+ break;
574
+ case "goBack":
575
+ tclCommand = `reverse goback ${seconds}`;
576
+ break;
577
+ case "absoluteGoto":
578
+ tclCommand = `reverse goto ${time}`;
579
+ break;
580
+ case "truncate":
581
+ tclCommand = "reverse truncatereplay";
582
+ break;
583
+ case "saveReplay":
584
+ if (filename)
585
+ filename = `"${filename}"`;
586
+ tclCommand = `reverse savereplay ${filename || ''}`;
587
+ break;
588
+ case "loadReplay":
589
+ if (filename)
590
+ filename = `"${filename}"`;
591
+ tclCommand = `reverse loadreplay ${filename}`;
592
+ break;
593
+ default:
594
+ return getResponseContent([
595
+ `Error: Unknown replay command "${command}".`
596
+ ]);
597
+ }
598
+ const response = await openMSXInstance.sendCommand(tclCommand);
599
+ return getResponseContent([
600
+ response
601
+ ]);
620
602
  });
621
603
  server.tool(
622
604
  // Name of the tool (used to call it)
@@ -637,20 +619,14 @@ function registerAllTools(server) {
637
619
  tclCommand = `type "${text}"`;
638
620
  break;
639
621
  default:
640
- return {
641
- content: [{
642
- type: "text",
643
- text: `Error: Unknown keyboard command "${command}".`,
644
- }],
645
- };
622
+ return getResponseContent([
623
+ `Error: Unknown keyboard command "${command}".`
624
+ ]);
646
625
  }
647
626
  const response = await openMSXInstance.sendCommand(tclCommand);
648
- return {
649
- content: [{
650
- type: "text",
651
- text: response === '' ? 'Ok' : response,
652
- }],
653
- };
627
+ return getResponseContent([
628
+ response
629
+ ]);
654
630
  });
655
631
  server.tool(
656
632
  // Name of the tool (used to call it)
@@ -684,30 +660,19 @@ function registerAllTools(server) {
684
660
  };
685
661
  }
686
662
  catch (error) {
687
- return {
688
- content: [{
689
- type: "text",
690
- text: 'Error creating screenshot: ' + response,
691
- }, {
692
- type: "text",
693
- text: error instanceof Error ? error.message : String(error),
694
- }],
695
- };
663
+ return getResponseContent([
664
+ 'Error creating screenshot: ' + response,
665
+ error instanceof Error ? error.message : String(error)
666
+ ]);
696
667
  }
697
668
  case "to_file":
698
- return {
699
- content: [{
700
- type: "text",
701
- text: response.startsWith('Error:') ? response : 'Screenshot taken in file: ' + response,
702
- }],
703
- };
669
+ return getResponseContent([
670
+ response.startsWith('Error:') ? response : 'Screenshot taken in file: ' + response
671
+ ]);
704
672
  }
705
- return {
706
- content: [{
707
- type: "text",
708
- text: `Error: Unknown screen_shot command "${command}".`,
709
- }],
710
- };
673
+ return getResponseContent([
674
+ `Error: Unknown screen_shot command "${command}".`
675
+ ]);
711
676
  });
712
677
  server.tool(
713
678
  // Name of the tool (used to call it)
@@ -722,17 +687,20 @@ function registerAllTools(server) {
722
687
  async ({ scrbasename }) => {
723
688
  const openmsxCommand = `save_msx_screen "${OPENMSX_SCREENDUMP_DIR + scrbasename}"`;
724
689
  const response = await openMSXInstance.sendCommand(openmsxCommand);
725
- return {
726
- content: [{
727
- type: "text",
728
- text: response.startsWith('Error:') ? 'Fail:' : 'Screendump file saved as:',
729
- }, {
730
- type: "text",
731
- text: response,
732
- }],
733
- };
690
+ return getResponseContent([
691
+ response.startsWith('Error:') ? 'Fail:' : 'Screendump file saved as:',
692
+ response
693
+ ]);
734
694
  });
735
695
  }
696
+ function getResponseContent(response) {
697
+ return {
698
+ content: response.map(line => ({
699
+ type: "text",
700
+ text: line == '' ? "Ok" : line,
701
+ })),
702
+ };
703
+ }
736
704
  // ============================================================================
737
705
  // Cleanup handlers for graceful shutdown of MCP server
738
706
  // Ensure openMSX emulator is closed when MCP server stops
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nataliapc/mcp-openmsx",
3
- "version": "1.0.2",
3
+ "version": "1.1.2",
4
4
  "description": "Model context protocol server for openMSX automation and control",
5
5
  "main": "dist/server.js",
6
6
  "type": "module",