@nataliapc/mcp-openmsx 1.1.14 → 1.1.15

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
@@ -2,6 +2,14 @@
2
2
 
3
3
  *"Orchestrating a binary opera where AI conducts, MCP interprets, and openMSX acts as the 8-bit diva."*
4
4
 
5
+ [![Built by NataliaPC](https://img.shields.io/badge/Built%20by-NataliaPC-blue)](https://github.com/nataliapc)
6
+ [![License](https://img.shields.io/badge/License-GPL2-blue.svg)](https://github.com/nataliapc/mcp-openmsx/blob/main/LICENSE)
7
+ [![GitHub Repo stars](https://img.shields.io/github/stars/nataliapc/mcp-openmsx)
8
+ ](https://github.com/nataliapc/mcp-openmsx/stargazers/)
9
+ [![NPM Version](https://img.shields.io/npm/v/%40nataliapc%2Fmcp-openmsx)](https://www.npmjs.com/package/@nataliapc/mcp-openmsx?activeTab=versions)
10
+ [![NPM Downloads](https://img.shields.io/npm/dm/%40nataliapc%2Fmcp-openmsx?color=808000)]()
11
+
12
+
5
13
  A [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) server for automating [openMSX emulator](https://github.com/openMSX/openMSX) instances.
6
14
 
7
15
  This server provides comprehensive tools for MSX software development, testing, and automation through standardized MCP protocols.
@@ -64,6 +72,7 @@ The MCP server translates high-level commands from your Copilot AI into `TCL` co
64
72
  - `emu_savestates`: Save and restore machine states: _`load`, `save`, `list`_.
65
73
  - `screen_shot`: Capture emulator screen: _`as_image`, `to_file`_.
66
74
  - `screen_dump`: Export screen data as BASIC BSAVE instruction.
75
+ - `msxdocs_resource_get`: Retrieve MCP resources for MCP clients that don't support MCP resources.
67
76
 
68
77
  ## 📚 Available MCP Resources
69
78
 
@@ -85,10 +94,11 @@ There are more than 60 resources available, some included directly in the MCP an
85
94
  - `MSX-UNAPI`
86
95
  - `MSX BASIC`
87
96
 
88
- And two books:
97
+ And books and manuals:
89
98
 
90
99
  - `MSX2 Technical Handbook`
91
100
  - `The MSX Red Book`
101
+ - `SDCC Compiler`
92
102
 
93
103
  ### Resources from:
94
104
 
@@ -98,8 +108,9 @@ And two books:
98
108
  - [MSX2 Technical Handbook](https://github.com/Konamiman/MSX2-Technical-Handbook)
99
109
  - [Konamiman MSX-UNAPI-specification](https://github.com/Konamiman/MSX-UNAPI-specification)
100
110
  - [BiFi MSX Net](http://bifi.msxnet.org/msxnet/)
101
- - [MSX Wiki](https://www.msx.org/wiki/Main_Page)
111
+ - [MRC Wiki](https://www.msx.org/wiki/Main_Page)
102
112
  - [MSX Banzai!](http://msxbanzai.tni.nl/)
113
+ - [SDCC](https://sdcc.sourceforge.net/)
103
114
 
104
115
  Thanks to the authors of these resources, who have made them available under various licenses. This MCP server includes some of these resources to enhance the development experience.
105
116
 
@@ -116,6 +127,9 @@ You can use this MCP server in this basic way with the [precompiled NPM package]
116
127
 
117
128
  ### STDIO mode (recommended)
118
129
 
130
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%7B%22type%22%3A%20%22stdio%22%2C%22command%22%3A%20%22npx%22%2C%22args%22%3A%20%5B%22%40nataliapc%2Fmcp-openmsx%22%5D%2C%20%22env%22%3A%20%7B%22OPENMSX_SHARE_DIR%22%3A%20%22%2Fusr%2Fshare%2Fopenmsx%22%7D%7D)
131
+ [![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_Server-24bfa5?logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%7B%22type%22%3A%20%22stdio%22%2C%22command%22%3A%20%22npx%22%2C%22args%22%3A%20%5B%22%40nataliapc%2Fmcp-openmsx%22%5D%2C%20%22env%22%3A%20%7B%22OPENMSX_SHARE_DIR%22%3A%20%22%2Fusr%2Fshare%2Fopenmsx%22%7D%7D&quality=insiders)
132
+
119
133
  ```json
120
134
  {
121
135
  "servers": {
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.14
9
+ * @version 1.1.15
10
10
  * @author Natalia Pujol Cremades (@nataliapc)
11
11
  * @license GPL2
12
12
  */
@@ -22,7 +22,7 @@ import path from "path";
22
22
  import { openMSXInstance } from "./openmsx.js";
23
23
  import { fetchCleanWebpage, addFileExtension, listResourcesDirectory, encodeTypeText, isErrorResponse, getResponseContent } from "./utils.js";
24
24
  // Version info for CLI
25
- const PACKAGE_VERSION = "1.1.14";
25
+ export const PACKAGE_VERSION = "1.1.15";
26
26
  const resourcesDir = path.join(path.dirname(new URL(import.meta.url).pathname), "../resources");
27
27
  // Defaults for openMSX paths
28
28
  var OPENMSX_EXECUTABLE = 'openmsx';
@@ -864,84 +864,95 @@ The parameter scrbasename is the name of the filename (without path) to save the
864
864
  // ============================================================================
865
865
  // MSX Documentation resources
866
866
  const resdocs = (await listResourcesDirectory(resourcesDir)).sort();
867
+ const regResources = [];
867
868
  for (let index = 0; index < resdocs.length; index++) {
868
869
  const sectionName = resdocs[index];
869
870
  const tocFile = path.join(resourcesDir, `${sectionName}/toc.json`);
870
871
  const tocContent = JSON.parse(await fs.readFile(tocFile, 'utf8'));
871
872
  tocContent.toc.forEach((item, itemIndex) => {
872
873
  const itemName = path.parse(item.uri.split('/').pop()).name || '';
873
- server.registerResource(
874
- // Name of the resource (used to call it)
875
- `msxdocs_${sectionName}_${item.title.replace(/[^a-z0-9]+/gi, '_').toLowerCase()}`,
876
- // Resource URI template
877
- item.uri,
878
- // Metadata for the resource
879
- {
880
- title: item.title || `MSX Documentation '${sectionName}': ${itemName}`,
881
- description: item.description || `Documentation for MSX resource '${sectionName}': ${itemName}`,
882
- mimeType: item.mimeType || 'text/markdown',
883
- },
884
- // Handler for the resource (function to be executed when the resource is called)
885
- async (uri) => {
886
- let resourceContent;
887
- let mimeType;
888
- if (uri.href.startsWith('http://') || uri.href.startsWith('https://')) {
889
- // Fetch the resource from the URL
890
- try {
891
- [resourceContent, mimeType] = await fetchCleanWebpage(uri.href);
874
+ let resource = {
875
+ uri: item.uri,
876
+ filename: '',
877
+ resource: server.registerResource(
878
+ // Name of the resource (used to call it)
879
+ `msxdocs_${sectionName}_${item.title.replace(/[^a-z0-9]+/gi, '_').toLowerCase()}`,
880
+ // Resource URI template
881
+ item.uri,
882
+ // Metadata for the resource
883
+ {
884
+ title: item.title || `MSX Documentation '${sectionName}': ${itemName}`,
885
+ description: item.description || `Documentation for MSX resource '${sectionName}': ${itemName}`,
886
+ mimeType: item.mimeType || 'text/markdown',
887
+ },
888
+ // Handler for the resource (function to be executed when the resource is called)
889
+ async (uri) => {
890
+ let resourceContent;
891
+ let mimeType;
892
+ if (uri.href.startsWith('http://') || uri.href.startsWith('https://')) {
893
+ // Fetch the resource from the URL
894
+ try {
895
+ [resourceContent, mimeType] = await fetchCleanWebpage(uri.href);
896
+ }
897
+ catch (error) {
898
+ // Throw exception (MCP protocol requirement)
899
+ throw error;
900
+ }
892
901
  }
893
- catch (error) {
894
- // Throw exception (MCP protocol requirement)
895
- throw error;
902
+ else {
903
+ // Read the resource from the local MCP server resources directory
904
+ try {
905
+ let resourceFile;
906
+ [mimeType, resourceFile] = await addFileExtension(path.join(resourcesDir, `${sectionName}/${itemName}`));
907
+ resourceContent = await fs.readFile(resourceFile, 'utf8');
908
+ }
909
+ catch (error) {
910
+ // Throw exception (MCP protocol requirement)
911
+ throw new Error(`Error reading resource ${sectionName}/${item.uri}: ${error instanceof Error ? error.message : String(error)}`);
912
+ }
896
913
  }
897
- }
898
- else {
899
- // Read the resource from the local MCP server resources directory
900
- try {
901
- let resourceFile;
902
- [mimeType, resourceFile] = await addFileExtension(path.join(resourcesDir, `${sectionName}/${itemName}`));
903
- resourceContent = await fs.readFile(resourceFile, 'utf8');
904
- }
905
- catch (error) {
906
- // Throw exception (MCP protocol requirement)
907
- throw new Error(`Error reading resource ${sectionName}/${item.uri}: ${error instanceof Error ? error.message : String(error)}`);
908
- }
909
- }
910
- return {
911
- contents: [{
912
- uri: uri.href,
913
- text: resourceContent,
914
- mimeType: mimeType || 'text/plain',
915
- }],
916
- };
917
- });
914
+ return {
915
+ contents: [{
916
+ uri: uri.href,
917
+ text: resourceContent,
918
+ mimeType: mimeType || 'text/plain',
919
+ }],
920
+ };
921
+ })
922
+ };
923
+ regResources.push(resource);
918
924
  });
919
925
  }
920
926
  ;
927
+ // Source: https://www.msx.org/wiki/Category:MSX-BASIC_Instructions
928
+ const basicInstructions = [
929
+ "ABS()", "AND", "ASC()", "ATN()", "AUTO", "BASE()", "BEEP", "BIN$()", "BLOAD", "BSAVE", "CALL", "CALL ADJUST", "CALL PAUSE", "CALL PCMPLAY", "CALL PCMREC",
930
+ "CDBL()", "CHR$()", "CINT()", "CIRCLE", "CLEAR", "CLOAD", "CLOAD?", "CLOSE", "CLS", "COLOR", "COLOR=", "COLOR SPRITE()", "COLOR SPRITE$()", "CONT", "COPY",
931
+ "COPY SCREEN", "COS()", "CSAVE", "CSNG()", "CSRLIN", "DATA", "DEFDBL", "DEF FN", "DEFINT", "DEFSNG", "DEFSTR", "DEF USR", "DELETE", "DIM", "DRAW", "ELSE",
932
+ "END", "EOF()", "EQV", "ERASE", "ERL", "ERR", "ERROR", "EXP()", "FIX()", "FN", "FOR...NEXT", "FRE()", "GET DATE", "GET TIME", "GOSUB", "GOTO", "HEX$()",
933
+ "IF...GOTO...ELSE", "IF...THEN...ELSE", "IMP", "INKEY$", "INP()", "INPUT", "INPUT$()", "INSTR()", "INT()", "INTERVAL", "KEY", "KEY()", "LEFT$()", "LEN()",
934
+ "LET", "LINE", "LINE INPUT", "LIST", "LLIST", "LOAD", "LOCATE", "LOG()", "LPOS()", "LPRINT", "MAXFILES", "MERGE", "MID$()", "MOD", "MOTOR", "NEW", "NOT",
935
+ "OCT$()", "ON...GOSUB", "ON...GOTO", "ON ERROR GOTO", "ON INTERVAL GOSUB", "ON KEY GOSUB", "ON SPRITE GOSUB", "ON STOP GOSUB", "ON STRIG GOSUB", "OPEN",
936
+ "OR", "OUT", "PAD()", "PAINT", "PDL()", "PEEK()", "PLAY", "PLAY()", "POINT", "POKE", "POS()", "PRESET", "PRINT", "PSET", "PUT KANJI", "PUT SPRITE", "READ",
937
+ "REM", "RENUM", "RESTORE", "RESUME", "RETURN", "RIGHT$()", "RND()", "RUN", "SAVE", "SCREEN", "SET ADJUST", "SET BEEP", "SET DATE", "SET PAGE", "SET PASSWORD",
938
+ "SET PROMPT", "SET SCREEN", "SET SCROLL", "SET TIME", "SET TITLE", "SET VIDEO", "SGN()", "SIN()", "SOUND", "SPACE$()", "SPC()", "SPRITE", "SPRITE$()",
939
+ "SQR()", "STICK()", "STOP", "STR$()", "STRIG()", "STRING$()", "SWAP", "TAB()", "TAN()", "TIME", "TROFF", "TRON", "USR()", "VAL()", "VARPTR()", "VDP()",
940
+ "VPEEK()", "VPOKE", "WAIT", "WIDTH", "XOR"
941
+ ];
921
942
  server.resource("msxdocs_basic_wiki", new ResourceTemplate("msxdocs://basic_wiki/{instruction}", {
922
943
  list: undefined,
923
944
  complete: {
924
- instruction: (value) => [
925
- "ABS()", "AND", "ASC()", "ATN()", "AUTO", "BASE()", "BEEP", "BIN$()", "BLOAD", "BSAVE", "CALL", "CALL ADJUST", "CALL PAUSE", "CALL PCMPLAY", "CALL PCMREC",
926
- "CDBL()", "CHR$()", "CINT()", "CIRCLE", "CLEAR", "CLOAD", "CLOAD?", "CLOSE", "CLS", "COLOR", "COLOR=", "COLOR", "COLOR", "CONT", "COPY", "COPY", "COS()", "CSAVE",
927
- "CSNG()", "CSRLIN", "DATA", "DEFDBL", "DEF FN", "DEFINT", "DEFSNG", "DEFSTR", "DEF USR", "DELETE", "DIM", "DRAW", "ELSE", "END", "EOF()", "EQV", "ERASE", "ERL", "ERR",
928
- "ERROR", "EXP()", "FIX()", "FN", "FOR...NEXT", "FRE()", "GET DATE", "GET TIME", "GOSUB", "GOTO", "HEX$()", "IF...GOTO...ELSE", "IF...THEN...ELSE", "IMP",
929
- "INKEY$", "INP()", "INPUT", "INPUT$()", "INSTR()", "INT()", "INTERVAL", "KEY", "KEY()", "LEFT$()", "LEN()", "LET", "LINE", "LINE INPUT", "LIST", "LLIST",
930
- "LOAD", "LOCATE", "LOG()", "LPOS()", "LPRINT", "MAXFILES", "MERGE", "MID$()", "MOD", "MOTOR", "NEW", "NOT", "OCT$()", "ON...GOSUB", "ON...GOTO",
931
- "ON ERROR GOTO", "ON INTERVAL GOSUB", "ON KEY GOSUB", "ON SPRITE GOSUB", "ON STOP GOSUB", "ON STRIG GOSUB", "OPEN", "OR", "OUT", "PAD()", "PAINT", "PDL()",
932
- "PEEK()", "PLAY", "PLAY()", "POINT", "POKE", "POS()", "PRESET", "PRINT", "PSET", "PUT KANJI", "PUT SPRITE", "READ", "REM", "RENUM", "RESTORE", "RESUME",
933
- "RETURN", "RIGHT$()", "RND()", "RUN", "SAVE", "SCREEN", "SET ADJUST", "SET BEEP", "SET DATE", "SET PAGE", "SET PASSWORD", "SET PROMPT", "SET SCREEN",
934
- "SET SCROLL", "SET TIME", "SET TITLE", "SET VIDEO", "SGN()", "SIN()", "SOUND", "SPACE$()", "SPC()", "SPRITE", "SPRITE$()", "SQR()", "STICK()", "STOP",
935
- "STR$()", "STRIG()", "STRING$()", "SWAP", "TAB()", "TAN()", "TIME", "TROFF", "TRON", "USR()", "VAL()", "VARPTR()", "VDP()", "VPEEK()", "VPOKE", "WAIT",
936
- "WIDTH", "XOR"
937
- ],
945
+ instruction: (value) => basicInstructions,
938
946
  },
939
947
  }), {
940
948
  title: "MSX BASIC Instructions Documentation",
941
949
  description: "Documentation about all the standard MSX BASIC instructions from www.msx.org",
942
950
  mimeType: "text/html",
943
951
  }, async (uri, variables) => {
944
- const instruction = variables.instruction.replace(/ /g, '_').replace(/\?/g, '%3F').replace(/=/g, '%3D');
952
+ const instruction = variables.instruction
953
+ .replace(/ /g, '_')
954
+ .replace(/\?/g, '%3F')
955
+ .replace(/=/g, '%3D');
945
956
  const url = `https://www.msx.org/wiki/${instruction}`;
946
957
  let resourceContent;
947
958
  let mimeType;
@@ -960,6 +971,73 @@ The parameter scrbasename is the name of the filename (without path) to save the
960
971
  }],
961
972
  };
962
973
  });
974
+ // Register the tool to get a specific MSX documentation resource
975
+ server.registerTool(
976
+ // Name of the tool (used to call it)
977
+ "msxdocs_resource_get", {
978
+ title: "Tool to get a resource",
979
+ // Description of the tool (what it does)
980
+ description: "Get a specific available MSX documentation resource from this MCP server resources.",
981
+ // Schema for the tool (input validation)
982
+ inputSchema: {
983
+ resourceName: z.enum(regResources.map(res => res.resource.name)).describe("Name of the resource to obtain, e.g. 'msxdocs_programming_interrupts'"),
984
+ },
985
+ },
986
+ // Handler for the tool (function to be executed when the tool is called)
987
+ async ({ resourceName }, extra) => {
988
+ const index = regResources.findIndex((res) => res.resource.name === resourceName);
989
+ const uriString = index !== -1 ? regResources[index].uri : undefined;
990
+ const resource = index !== -1 ? regResources[index].resource : undefined;
991
+ if (!resource || !uriString) {
992
+ return getResponseContent([
993
+ `Error: Resource '${resourceName}' not found.`
994
+ ]);
995
+ }
996
+ let documentationText = '';
997
+ try {
998
+ // If the resource is found, return its content
999
+ let resourceContent = await resource.readCallback(new URL(uriString), extra);
1000
+ if (!resourceContent.contents?.length) {
1001
+ return getResponseContent([
1002
+ `Error: Resource '${resourceName}' has no content available.`
1003
+ ]);
1004
+ }
1005
+ // Return the first content item (assuming it's the main content)
1006
+ const content = resourceContent.contents[0];
1007
+ if ('text' in content) {
1008
+ documentationText = content.text;
1009
+ }
1010
+ else {
1011
+ return getResponseContent([
1012
+ `Error: Resource '${resourceName}' has no content available.`
1013
+ ]);
1014
+ }
1015
+ }
1016
+ catch (error) {
1017
+ return getResponseContent([
1018
+ `Error: error reading resource '${resourceName}': ${error instanceof Error ? error.message : String(error)}`
1019
+ ]);
1020
+ }
1021
+ return {
1022
+ content: [{
1023
+ type: "text",
1024
+ text: `Content from resource: '${resourceName}'`,
1025
+ }, {
1026
+ type: "text",
1027
+ text: documentationText || 'No content available for this resource.',
1028
+ mimeType: resource.metadata?.mimeType || 'text/plain',
1029
+ } /*, {
1030
+ type: "resource",
1031
+ resource: {
1032
+ uri: resource.metadata?.uri || resourceName,
1033
+ title: resource.metadata?.title || `Resource: ${resourceName}`,
1034
+ mimeType: resource.metadata?.mimeType || 'text/plain',
1035
+ text: documentationText || 'No content available for this resource.',
1036
+ }
1037
+ }*/
1038
+ ],
1039
+ };
1040
+ });
963
1041
  }
964
1042
  // ============================================================================
965
1043
  // Cleanup handlers for graceful shutdown of MCP server
@@ -1071,8 +1149,8 @@ async function startHttpServer() {
1071
1149
  }
1072
1150
  await transport.handleRequest(req, res, req.body);
1073
1151
  });
1074
- // Handle GET requests for server-to-client notifications via SSE
1075
- app.get('/mcp', async (req, res) => {
1152
+ // Reusable handle GET / DELETE requests
1153
+ const handleSessionRequest = async (req, res) => {
1076
1154
  const sessionId = req.headers['mcp-session-id'];
1077
1155
  if (!sessionId || !transports[sessionId]) {
1078
1156
  res.status(400).send('Invalid or missing session ID');
@@ -1080,7 +1158,9 @@ async function startHttpServer() {
1080
1158
  }
1081
1159
  const transport = transports[sessionId];
1082
1160
  await transport.handleRequest(req, res);
1083
- });
1161
+ };
1162
+ app.get('/mcp', handleSessionRequest);
1163
+ app.delete('/mcp', handleSessionRequest);
1084
1164
  const port = process.env.MCP_HTTP_PORT || 3000;
1085
1165
  app.listen(port, () => {
1086
1166
  console.log(`MCP Server listening on port ${port}`);
package/dist/utils.js CHANGED
@@ -7,6 +7,9 @@
7
7
  import fs from 'fs/promises';
8
8
  import path from 'path';
9
9
  import mime from 'mime-types';
10
+ import { gunzipSync } from 'zlib';
11
+ import { PACKAGE_VERSION } from "./server.js";
12
+ import sanitizeHtml from 'sanitize-html';
10
13
  /**
11
14
  * Extract description from XML file
12
15
  * @param filePath - Full path to the XML file
@@ -77,12 +80,48 @@ export async function fetchCleanWebpage(url) {
77
80
  let resourceContent;
78
81
  let mimeType = 'text/plain';
79
82
  try {
80
- resourceContent = await fetch(url).then(response => {
83
+ const response = await fetch(url, {
84
+ headers: {
85
+ // Accept compressed content gzip/deflate
86
+ 'Accept-Encoding': 'gzip, deflate, br',
87
+ // User agent to avoid blocking by some servers
88
+ 'User-Agent': `Mozilla/5.0 (compatible; MCP-openMSX/${PACKAGE_VERSION})`
89
+ }
90
+ });
91
+ if (response.status === 200) {
81
92
  mimeType = response.headers.get('content-type') || 'text/plain';
82
- return response.text();
83
- }) || 'Error downloading content';
84
- // Remove script, style, and link tags from the content
85
- resourceContent = resourceContent.replace(/<script\b[^>]*>[\s\S]*?<\/script>|<style\b[^>]*>[\s\S]*?<\/style>|<link\b[^>]*\/?>/gi, '');
93
+ const contentType = response.headers.get('content-type') || '';
94
+ if (contentType.includes('x-gzip') || contentType.includes('gzip')) {
95
+ const arrayBuffer = await response.arrayBuffer();
96
+ const uint8Array = new Uint8Array(arrayBuffer);
97
+ if (uint8Array[0] === 0x1f && uint8Array[1] === 0x8b) {
98
+ try {
99
+ const decompressed = gunzipSync(Buffer.from(uint8Array));
100
+ resourceContent = decompressed.toString('utf8');
101
+ mimeType = 'text/html';
102
+ }
103
+ catch (error) {
104
+ resourceContent = new TextDecoder().decode(uint8Array);
105
+ mimeType = 'text/html';
106
+ }
107
+ }
108
+ else {
109
+ resourceContent = new TextDecoder().decode(uint8Array);
110
+ mimeType = 'text/html';
111
+ }
112
+ }
113
+ else {
114
+ // Normal case, use response.text() which automatically handles decompression
115
+ resourceContent = await response.text();
116
+ }
117
+ }
118
+ else {
119
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
120
+ }
121
+ // Remove script, style, form, and link tags from the content if it's HTML
122
+ if (mimeType.startsWith('text/html')) {
123
+ resourceContent = sanitizeHtml(resourceContent);
124
+ }
86
125
  }
87
126
  catch (error) {
88
127
  // Throw exception (MCP protocol requirement)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nataliapc/mcp-openmsx",
3
- "version": "1.1.14",
3
+ "version": "1.1.15",
4
4
  "description": "Model context protocol server for openMSX automation and control",
5
5
  "main": "dist/server.js",
6
6
  "type": "module",
@@ -38,9 +38,10 @@
38
38
  "test": "echo \"Error: no test specified\" && exit 1"
39
39
  },
40
40
  "dependencies": {
41
- "@modelcontextprotocol/sdk": "^1.11.2",
41
+ "@modelcontextprotocol/sdk": "^1.15.1",
42
42
  "@types/express": "^5.0.2",
43
43
  "express": "^5.1.0",
44
+ "sanitize-html": "^2.17.0",
44
45
  "tsx": "^4.7.1",
45
46
  "zod": "^3.24.4"
46
47
  },
@@ -48,6 +49,7 @@
48
49
  "@modelcontextprotocol/inspector": "^0.15.0",
49
50
  "@types/mime-types": "^3.0.1",
50
51
  "@types/node": "^22.15.27",
52
+ "@types/sanitize-html": "^2.16.0",
51
53
  "shx": "^0.4.0",
52
54
  "typescript": "^5.8.3"
53
55
  },