@nataliapc/mcp-openmsx 1.1.2 → 1.1.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/README.md +6 -3
- package/dist/openmsx.js +17 -19
- package/dist/server.js +27 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,8 +6,6 @@ A [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) s
|
|
|
6
6
|
|
|
7
7
|
This server provides comprehensive tools for MSX software development, testing, and automation through standardized MCP protocols.
|
|
8
8
|
|
|
9
|
-
Currently, the MCP server requires Linux to run. It has not been tested on Windows or macOS, although it will likely work on the latter as well.
|
|
10
|
-
|
|
11
9
|
## 🎯 Project Overview
|
|
12
10
|
|
|
13
11
|
This project creates a bridge between modern AI-assisted development (e.g. GitHub Copilot, Claude Desktop) and retro computing (MSX systems) by providing:
|
|
@@ -66,6 +64,8 @@ The MCP server translates high-level commands from your Copilot AI into `TCL` co
|
|
|
66
64
|
|
|
67
65
|
## 🚀 Quick Start
|
|
68
66
|
|
|
67
|
+
You can use this MCP server in this basic way with the [precompiled NPM package](https://www.npmjs.com/package/@nataliapc/mcp-openmsx). You may need to have `nodejs` installed for this to work.
|
|
68
|
+
|
|
69
69
|
### 🟢 Basic Usage with VSCode
|
|
70
70
|
|
|
71
71
|
* Install [Github Copilot extension](https://code.visualstudio.com/docs/copilot/overview)
|
|
@@ -86,7 +86,8 @@ The MCP server translates high-level commands from your Copilot AI into `TCL` co
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
```
|
|
89
|
-
|
|
89
|
+
|
|
90
|
+
**Note:** Environment variables are optional. Customize them as you need.
|
|
90
91
|
|
|
91
92
|
### Streamed HTTP mode (more advanced)
|
|
92
93
|
|
|
@@ -136,6 +137,8 @@ Add to your `claude_desktop_config.json`:
|
|
|
136
137
|
|
|
137
138
|
## 🧑💻 Advanced Manual Usage
|
|
138
139
|
|
|
140
|
+
Currently, the MCP server requires Linux to be compiled. It has not been tested on Windows or macOS, although it will likely work on the latter as well.
|
|
141
|
+
|
|
139
142
|
### Manual installation
|
|
140
143
|
|
|
141
144
|
```bash
|
package/dist/openmsx.js
CHANGED
|
@@ -144,7 +144,7 @@ export class OpenMSX {
|
|
|
144
144
|
async emu_close() {
|
|
145
145
|
return new Promise((resolve) => {
|
|
146
146
|
if (!this.process) {
|
|
147
|
-
resolve("No emulator process running");
|
|
147
|
+
resolve("Error: No emulator process running");
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
150
|
this.process.on('exit', () => {
|
|
@@ -153,7 +153,7 @@ export class OpenMSX {
|
|
|
153
153
|
resolve("Ok: Emulator process closed successfully");
|
|
154
154
|
});
|
|
155
155
|
this.process.on('error', (error) => {
|
|
156
|
-
resolve(`Error closing emulator: ${error.message}`);
|
|
156
|
+
resolve(`Error: error closing emulator: ${error.message}`);
|
|
157
157
|
});
|
|
158
158
|
// Try graceful shutdown first
|
|
159
159
|
if (this.isConnected) {
|
|
@@ -184,7 +184,7 @@ export class OpenMSX {
|
|
|
184
184
|
try {
|
|
185
185
|
const response = await this.sendCommand('machine_info');
|
|
186
186
|
if (response.startsWith('Error:')) {
|
|
187
|
-
return
|
|
187
|
+
return response;
|
|
188
188
|
}
|
|
189
189
|
// Parse machine_info output into key-value pairs
|
|
190
190
|
const parameters = response.trim().split(' ');
|
|
@@ -199,9 +199,7 @@ export class OpenMSX {
|
|
|
199
199
|
return JSON.stringify(machineInfo, null, 2);
|
|
200
200
|
}
|
|
201
201
|
catch (error) {
|
|
202
|
-
return
|
|
203
|
-
error: `Failed to get machine status: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
204
|
-
});
|
|
202
|
+
return `Error: Failed to get machine status - ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
205
203
|
}
|
|
206
204
|
}
|
|
207
205
|
/**
|
|
@@ -211,7 +209,7 @@ export class OpenMSX {
|
|
|
211
209
|
async getMachineList(machinesDirectory) {
|
|
212
210
|
// Read the machines directory
|
|
213
211
|
let machines = [];
|
|
214
|
-
let machinesList = "No machines found.";
|
|
212
|
+
let machinesList = "Error: No machines found.";
|
|
215
213
|
try {
|
|
216
214
|
const allFiles = await fs.readdir(machinesDirectory);
|
|
217
215
|
machines = await Promise.all(allFiles
|
|
@@ -222,14 +220,14 @@ export class OpenMSX {
|
|
|
222
220
|
description: await extractDescriptionFromXML(path.join(machinesDirectory, file))
|
|
223
221
|
};
|
|
224
222
|
}));
|
|
223
|
+
if (machines.length !== 0) {
|
|
224
|
+
machinesList = JSON.stringify(machines, null, 2);
|
|
225
|
+
}
|
|
226
|
+
return machinesList;
|
|
225
227
|
}
|
|
226
228
|
catch (error) {
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
if (machines.length !== 0) {
|
|
230
|
-
machinesList = JSON.stringify(machines, null, 2);
|
|
229
|
+
return `Error: error reading machines directory - ${error instanceof Error ? error.message : error}`;
|
|
231
230
|
}
|
|
232
|
-
return machinesList;
|
|
233
231
|
}
|
|
234
232
|
/**
|
|
235
233
|
* Get the list of extensions available in the openMSX emulator
|
|
@@ -238,7 +236,7 @@ export class OpenMSX {
|
|
|
238
236
|
async getExtensionList(extensionDirectory) {
|
|
239
237
|
// Read the extensions directory
|
|
240
238
|
let extensions = [];
|
|
241
|
-
let extensionsList = "No extensions found.";
|
|
239
|
+
let extensionsList = "Error: No extensions found.";
|
|
242
240
|
try {
|
|
243
241
|
const allFiles = await fs.readdir(extensionDirectory);
|
|
244
242
|
extensions = await Promise.all(allFiles
|
|
@@ -249,14 +247,14 @@ export class OpenMSX {
|
|
|
249
247
|
description: await extractDescriptionFromXML(path.join(extensionDirectory, file))
|
|
250
248
|
};
|
|
251
249
|
}));
|
|
250
|
+
if (extensions.length !== 0) {
|
|
251
|
+
extensionsList = JSON.stringify(extensions, null, 2);
|
|
252
|
+
}
|
|
253
|
+
return extensionsList;
|
|
252
254
|
}
|
|
253
255
|
catch (error) {
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
if (extensions.length !== 0) {
|
|
257
|
-
extensionsList = JSON.stringify(extensions, null, 2);
|
|
256
|
+
return `Error: error reading extensions directory - ${error instanceof Error ? error.message : error}`;
|
|
258
257
|
}
|
|
259
|
-
return extensionsList;
|
|
260
258
|
}
|
|
261
259
|
;
|
|
262
260
|
/**
|
|
@@ -285,7 +283,7 @@ export class OpenMSX {
|
|
|
285
283
|
return decodeHtmlEntities(output.trim());
|
|
286
284
|
}
|
|
287
285
|
catch (error) {
|
|
288
|
-
return `Error: ${error instanceof Error ? error.message :
|
|
286
|
+
return `Error: ${error instanceof Error ? error.message : error}`;
|
|
289
287
|
}
|
|
290
288
|
}
|
|
291
289
|
/**
|
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.1.
|
|
9
|
+
* @version 1.1.3
|
|
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.1.
|
|
24
|
+
const PACKAGE_VERSION = "1.1.3";
|
|
25
25
|
// Defaults for openMSX paths
|
|
26
26
|
var OPENMSX_EXECUTABLE = 'openmsx';
|
|
27
27
|
var OPENMSX_SHARE_DIR = '/usr/share/openmsx';
|
|
@@ -39,7 +39,7 @@ 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 can be specified
|
|
42
|
+
"'launch [machine] [extensions]': opens a powered-on openMSX emulator; you must wait some time waiting the machine is fully booted; machine and extensions parameters can be specified so use 'machineList' and 'extensionList' commands 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. " +
|
|
@@ -59,7 +59,7 @@ function registerAllTools(server) {
|
|
|
59
59
|
},
|
|
60
60
|
// Handler for the tool (function to be executed when the tool is called)
|
|
61
61
|
async ({ command, machine, extensions, emuspeed, seconds }) => {
|
|
62
|
-
let result =
|
|
62
|
+
let result = '';
|
|
63
63
|
switch (command) {
|
|
64
64
|
case "launch":
|
|
65
65
|
result = await openMSXInstance.emu_launch(OPENMSX_EXECUTABLE, machine || "", extensions || []);
|
|
@@ -69,23 +69,23 @@ function registerAllTools(server) {
|
|
|
69
69
|
break;
|
|
70
70
|
case "powerOn":
|
|
71
71
|
result = await openMSXInstance.sendCommand('set power on');
|
|
72
|
-
result
|
|
72
|
+
result = result === "true" ? "openMSX emulator powered on" : "Error: " + result;
|
|
73
73
|
break;
|
|
74
74
|
case "powerOff":
|
|
75
75
|
result = await openMSXInstance.sendCommand('set power off');
|
|
76
|
-
result
|
|
76
|
+
result = result === "false" ? "openMSX emulator powered off" : "Error: " + result;
|
|
77
77
|
break;
|
|
78
78
|
case "reset":
|
|
79
79
|
result = await openMSXInstance.sendCommand('reset');
|
|
80
|
-
result
|
|
80
|
+
result = result === "" ? "openMSX emulator reset successful" : "Error: " + result;
|
|
81
81
|
break;
|
|
82
82
|
case 'getEmulatorSpeed':
|
|
83
83
|
result = await openMSXInstance.sendCommand('set speed');
|
|
84
|
-
result = !isNaN(Number(result)) ? `Current emulator speed is ${result}%` : result;
|
|
84
|
+
result = !isNaN(Number(result)) ? `Current emulator speed is ${result}%` : "Error: " + result;
|
|
85
85
|
break;
|
|
86
86
|
case 'setEmulatorSpeed':
|
|
87
87
|
result = await openMSXInstance.sendCommand(`set speed ${emuspeed}`);
|
|
88
|
-
result = !isNaN(Number(result)) ? `Emulator speed set to ${emuspeed}%` : result;
|
|
88
|
+
result = !isNaN(Number(result)) ? `Emulator speed set to ${emuspeed}%` : "Error: " + result;
|
|
89
89
|
break;
|
|
90
90
|
case "machineList":
|
|
91
91
|
result = await openMSXInstance.getMachineList(MACHINES_DIR);
|
|
@@ -206,13 +206,13 @@ function registerAllTools(server) {
|
|
|
206
206
|
// Name of the tool (used to call it)
|
|
207
207
|
"emu_vdp",
|
|
208
208
|
// Description of the tool (what it does)
|
|
209
|
-
"Manage VDP (Video Display Processor). Commands: " +
|
|
210
|
-
"'getPalette':
|
|
211
|
-
"'getRegisters':
|
|
212
|
-
"'getRegisterValue <register>':
|
|
213
|
-
"'setRegisterValue <register> <value>':
|
|
214
|
-
"'screenGetMode': returns the current screen mode (0-12) as a number which
|
|
215
|
-
"'screenGetFullText':
|
|
209
|
+
"Manage the VDP (Video Display Processor). Commands: " +
|
|
210
|
+
"'getPalette': returns the current V9938/V9958 color palette in RGB333 format. " +
|
|
211
|
+
"'getRegisters': returns all VDP register values. " +
|
|
212
|
+
"'getRegisterValue <register>': returns the value of a specific VDP register (0-31) in decimal format. " +
|
|
213
|
+
"'setRegisterValue <register> <value>': sets a hexadecimal value to a specific VDP register (0-31). " +
|
|
214
|
+
"'screenGetMode': returns the current screen mode (0-12) as a number, which matches the BASIC SCREEN command. " +
|
|
215
|
+
"'screenGetFullText': returns the full content of an MSX text screen (screen 0 or 1) as a string; PRIORITIZE this command to view screen content in text modes. ",
|
|
216
216
|
// Schema for the tool (input validation)
|
|
217
217
|
{
|
|
218
218
|
command: z.enum(["getPalette", "getRegisters", "getRegisterValue", "setRegisterValue", "screenGetMode", "screenGetFullText"]),
|
|
@@ -605,11 +605,13 @@ function registerAllTools(server) {
|
|
|
605
605
|
"emu_keyboard",
|
|
606
606
|
// Description of the tool (what it does)
|
|
607
607
|
"Send a text to the openMSX emulator. Commands: " +
|
|
608
|
-
"'sendText <text>': type a string in the emulated MSX, this command automatically press and release keys in the MSX keyboard matrix, is useful for automating tasks in BASIC
|
|
608
|
+
"'sendText <text>': type a string in the emulated MSX, this command automatically press and release keys in the MSX keyboard matrix, is useful for automating tasks in BASIC. " +
|
|
609
|
+
"Note: each 'text' sent is limited to 200 characters, and the 'text' is sent as if it was typed in the MSX keyboard. " +
|
|
610
|
+
"Note: escape keys that needs it as Return key (use \\r), double quotes (use \\\"), etc...",
|
|
609
611
|
// Schema for the tool (input validation)
|
|
610
612
|
{
|
|
611
613
|
command: z.enum(["sendText"]),
|
|
612
|
-
text: z.string().min(1).max(
|
|
614
|
+
text: z.string().min(1).max(200).optional().default(''), // Key to send to the emulator
|
|
613
615
|
},
|
|
614
616
|
// Handler for the tool (function to be executed when the tool is called)
|
|
615
617
|
async ({ command, text }) => {
|
|
@@ -662,8 +664,8 @@ function registerAllTools(server) {
|
|
|
662
664
|
catch (error) {
|
|
663
665
|
return getResponseContent([
|
|
664
666
|
'Error creating screenshot: ' + response,
|
|
665
|
-
error instanceof Error ? error.message : String(error)
|
|
666
|
-
]);
|
|
667
|
+
error instanceof Error ? error.message : String(error),
|
|
668
|
+
], true);
|
|
667
669
|
}
|
|
668
670
|
case "to_file":
|
|
669
671
|
return getResponseContent([
|
|
@@ -693,12 +695,15 @@ function registerAllTools(server) {
|
|
|
693
695
|
]);
|
|
694
696
|
});
|
|
695
697
|
}
|
|
696
|
-
function getResponseContent(response) {
|
|
698
|
+
function getResponseContent(response, isError = false) {
|
|
699
|
+
// Check if any response line starts with "Error:" to automatically set isError to true
|
|
700
|
+
const hasError = isError || response.some(line => line.startsWith("Error:"));
|
|
697
701
|
return {
|
|
698
702
|
content: response.map(line => ({
|
|
699
703
|
type: "text",
|
|
700
704
|
text: line == '' ? "Ok" : line,
|
|
701
705
|
})),
|
|
706
|
+
isError: hasError
|
|
702
707
|
};
|
|
703
708
|
}
|
|
704
709
|
// ============================================================================
|
|
@@ -767,7 +772,7 @@ Environment variables:
|
|
|
767
772
|
|
|
768
773
|
Examples:
|
|
769
774
|
mcp-openmsx # stdio transport
|
|
770
|
-
mcp-openmsx http
|
|
775
|
+
mcp-openmsx http # HTTP transport
|
|
771
776
|
MCP_TRANSPORT=http mcp-openmsx # HTTP via environment
|
|
772
777
|
`);
|
|
773
778
|
}
|