@nataliapc/mcp-openmsx 1.1.14 → 1.2.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 +37 -12
- package/dist/server.js +198 -69
- package/dist/utils.js +54 -7
- package/dist/vectordb.js +60 -0
- package/package.json +10 -3
- package/resources/audio/MGSC111.txt +669 -0
- package/resources/audio/msx-midi.md +872 -0
- package/resources/audio/opl4tech.txt +688 -0
- package/resources/audio/psg_registers.md +281 -0
- package/resources/audio/sound_cartridge_scc.md +123 -0
- package/resources/audio/sound_cartridge_scci.md +250 -0
- package/resources/audio/toc.json +18 -7
- package/resources/book--msx2-technical-handbook/toc.json +1 -1
- package/resources/msx-unapi/toc.json +2 -2
- package/resources/processors/toc.json +4 -4
- package/resources/processors/z80_detailed_instruction_set.md +80 -68
- package/resources/programming/basic_wiki/ABS().md +36 -0
- package/resources/programming/basic_wiki/AND.md +71 -0
- package/resources/programming/basic_wiki/ASC().md +38 -0
- package/resources/programming/basic_wiki/ATN().md +36 -0
- package/resources/programming/basic_wiki/AUTO.md +39 -0
- package/resources/programming/basic_wiki/BASE().md +147 -0
- package/resources/programming/basic_wiki/BEEP.md +27 -0
- package/resources/programming/basic_wiki/BIN$().md +36 -0
- package/resources/programming/basic_wiki/BLOAD.md +63 -0
- package/resources/programming/basic_wiki/BSAVE.md +61 -0
- package/resources/programming/basic_wiki/CALL.md +391 -0
- package/resources/programming/basic_wiki/CALL_ADJUST.md +40 -0
- package/resources/programming/basic_wiki/CALL_IMPOSE.md +28 -0
- package/resources/programming/basic_wiki/CALL_OPTIONS.md +26 -0
- package/resources/programming/basic_wiki/CALL_PAUSE.md +119 -0
- package/resources/programming/basic_wiki/CALL_PCMPLAY.md +60 -0
- package/resources/programming/basic_wiki/CALL_PCMREC.md +70 -0
- package/resources/programming/basic_wiki/CDBL().md +36 -0
- package/resources/programming/basic_wiki/CHR$().md +51 -0
- package/resources/programming/basic_wiki/CINT().md +36 -0
- package/resources/programming/basic_wiki/CIRCLE.md +51 -0
- package/resources/programming/basic_wiki/CLEAR.md +39 -0
- package/resources/programming/basic_wiki/CLOAD.md +27 -0
- package/resources/programming/basic_wiki/CLOAD?.md +31 -0
- package/resources/programming/basic_wiki/CLOSE.md +44 -0
- package/resources/programming/basic_wiki/CLS.md +51 -0
- package/resources/programming/basic_wiki/COLOR.md +143 -0
- package/resources/programming/basic_wiki/COLOR=.md +93 -0
- package/resources/programming/basic_wiki/COLOR_SPRITE$().md +83 -0
- package/resources/programming/basic_wiki/COLOR_SPRITE().md +85 -0
- package/resources/programming/basic_wiki/CONT.md +23 -0
- package/resources/programming/basic_wiki/COPY.md +215 -0
- package/resources/programming/basic_wiki/COPY_SCREEN.md +61 -0
- package/resources/programming/basic_wiki/COS().md +37 -0
- package/resources/programming/basic_wiki/CSAVE.md +35 -0
- package/resources/programming/basic_wiki/CSNG().md +36 -0
- package/resources/programming/basic_wiki/CSRLIN.md +33 -0
- package/resources/programming/basic_wiki/DATA.md +47 -0
- package/resources/programming/basic_wiki/DEFDBL.md +40 -0
- package/resources/programming/basic_wiki/DEFINT.md +40 -0
- package/resources/programming/basic_wiki/DEFSNG.md +40 -0
- package/resources/programming/basic_wiki/DEFSTR.md +40 -0
- package/resources/programming/basic_wiki/DEF_FN.md +49 -0
- package/resources/programming/basic_wiki/DEF_USR.md +33 -0
- package/resources/programming/basic_wiki/DELETE.md +49 -0
- package/resources/programming/basic_wiki/DIM.md +59 -0
- package/resources/programming/basic_wiki/DRAW.md +77 -0
- package/resources/programming/basic_wiki/ELSE.md +45 -0
- package/resources/programming/basic_wiki/END.md +32 -0
- package/resources/programming/basic_wiki/EOF().md +36 -0
- package/resources/programming/basic_wiki/EQV.md +76 -0
- package/resources/programming/basic_wiki/ERASE.md +35 -0
- package/resources/programming/basic_wiki/ERL.md +34 -0
- package/resources/programming/basic_wiki/ERR.md +143 -0
- package/resources/programming/basic_wiki/ERROR.md +145 -0
- package/resources/programming/basic_wiki/EXP().md +38 -0
- package/resources/programming/basic_wiki/FIELD.md +48 -0
- package/resources/programming/basic_wiki/FIX().md +44 -0
- package/resources/programming/basic_wiki/FN.md +61 -0
- package/resources/programming/basic_wiki/FOR...NEXT.md +80 -0
- package/resources/programming/basic_wiki/FRE().md +66 -0
- package/resources/programming/basic_wiki/GET_DATE.md +60 -0
- package/resources/programming/basic_wiki/GET_TIME.md +34 -0
- package/resources/programming/basic_wiki/GOSUB.md +41 -0
- package/resources/programming/basic_wiki/GOTO.md +41 -0
- package/resources/programming/basic_wiki/HEX$().md +36 -0
- package/resources/programming/basic_wiki/IF...GOTO...ELSE.md +55 -0
- package/resources/programming/basic_wiki/IF...THEN...ELSE.md +50 -0
- package/resources/programming/basic_wiki/IMP.md +83 -0
- package/resources/programming/basic_wiki/INKEY$.md +65 -0
- package/resources/programming/basic_wiki/INP().md +33 -0
- package/resources/programming/basic_wiki/INPUT$().md +51 -0
- package/resources/programming/basic_wiki/INPUT.md +93 -0
- package/resources/programming/basic_wiki/INSTR().md +44 -0
- package/resources/programming/basic_wiki/INT().md +44 -0
- package/resources/programming/basic_wiki/INTERVAL.md +57 -0
- package/resources/programming/basic_wiki/KEY().md +51 -0
- package/resources/programming/basic_wiki/KEY.md +254 -0
- package/resources/programming/basic_wiki/LEFT$().md +39 -0
- package/resources/programming/basic_wiki/LEN().md +36 -0
- package/resources/programming/basic_wiki/LET.md +68 -0
- package/resources/programming/basic_wiki/LINE.md +74 -0
- package/resources/programming/basic_wiki/LINE_INPUT.md +79 -0
- package/resources/programming/basic_wiki/LIST.md +58 -0
- package/resources/programming/basic_wiki/LLIST.md +43 -0
- package/resources/programming/basic_wiki/LOAD.md +56 -0
- package/resources/programming/basic_wiki/LOCATE.md +67 -0
- package/resources/programming/basic_wiki/LOG().md +36 -0
- package/resources/programming/basic_wiki/LPOS().md +31 -0
- package/resources/programming/basic_wiki/LPRINT.md +46 -0
- package/resources/programming/basic_wiki/MAXFILES.md +39 -0
- package/resources/programming/basic_wiki/MERGE.md +54 -0
- package/resources/programming/basic_wiki/MID$().md +72 -0
- package/resources/programming/basic_wiki/MOD.md +39 -0
- package/resources/programming/basic_wiki/MOTOR.md +46 -0
- package/resources/programming/basic_wiki/NEW.md +27 -0
- package/resources/programming/basic_wiki/NOT.md +61 -0
- package/resources/programming/basic_wiki/OCT$().md +36 -0
- package/resources/programming/basic_wiki/ON...GOSUB.md +45 -0
- package/resources/programming/basic_wiki/ON...GOTO.md +42 -0
- package/resources/programming/basic_wiki/ON_ERROR_GOTO.md +61 -0
- package/resources/programming/basic_wiki/ON_INTERVAL_GOSUB.md +54 -0
- package/resources/programming/basic_wiki/ON_KEY_GOSUB.md +56 -0
- package/resources/programming/basic_wiki/ON_SPRITE_GOSUB.md +41 -0
- package/resources/programming/basic_wiki/ON_STOP_GOSUB.md +56 -0
- package/resources/programming/basic_wiki/ON_STRIG_GOSUB.md +70 -0
- package/resources/programming/basic_wiki/OPEN.md +103 -0
- package/resources/programming/basic_wiki/OR.md +75 -0
- package/resources/programming/basic_wiki/OUT.md +35 -0
- package/resources/programming/basic_wiki/PAD().md +110 -0
- package/resources/programming/basic_wiki/PAINT.md +66 -0
- package/resources/programming/basic_wiki/PDL().md +53 -0
- package/resources/programming/basic_wiki/PEEK().md +44 -0
- package/resources/programming/basic_wiki/PLAY().md +58 -0
- package/resources/programming/basic_wiki/PLAY.md +196 -0
- package/resources/programming/basic_wiki/POINT.md +52 -0
- package/resources/programming/basic_wiki/POKE.md +51 -0
- package/resources/programming/basic_wiki/POS().md +36 -0
- package/resources/programming/basic_wiki/PRESET.md +61 -0
- package/resources/programming/basic_wiki/PRINT.md +179 -0
- package/resources/programming/basic_wiki/PSET.md +82 -0
- package/resources/programming/basic_wiki/PUT_KANJI.md +93 -0
- package/resources/programming/basic_wiki/PUT_SPRITE.md +143 -0
- package/resources/programming/basic_wiki/READ.md +45 -0
- package/resources/programming/basic_wiki/REM.md +42 -0
- package/resources/programming/basic_wiki/RENUM.md +78 -0
- package/resources/programming/basic_wiki/RESTORE.md +52 -0
- package/resources/programming/basic_wiki/RESUME.md +45 -0
- package/resources/programming/basic_wiki/RETURN.md +47 -0
- package/resources/programming/basic_wiki/RIGHT$().md +39 -0
- package/resources/programming/basic_wiki/RND().md +51 -0
- package/resources/programming/basic_wiki/RUN.md +56 -0
- package/resources/programming/basic_wiki/SAVE.md +65 -0
- package/resources/programming/basic_wiki/SCREEN.md +164 -0
- package/resources/programming/basic_wiki/SET_ADJUST.md +66 -0
- package/resources/programming/basic_wiki/SET_BEEP.md +76 -0
- package/resources/programming/basic_wiki/SET_DATE.md +103 -0
- package/resources/programming/basic_wiki/SET_PAGE.md +52 -0
- package/resources/programming/basic_wiki/SET_PASSWORD.md +75 -0
- package/resources/programming/basic_wiki/SET_PROMPT.md +61 -0
- package/resources/programming/basic_wiki/SET_SCREEN.md +100 -0
- package/resources/programming/basic_wiki/SET_SCROLL.md +55 -0
- package/resources/programming/basic_wiki/SET_TIME.md +83 -0
- package/resources/programming/basic_wiki/SET_TITLE.md +87 -0
- package/resources/programming/basic_wiki/SET_VIDEO.md +49 -0
- package/resources/programming/basic_wiki/SGN().md +38 -0
- package/resources/programming/basic_wiki/SIN().md +36 -0
- package/resources/programming/basic_wiki/SOUND.md +188 -0
- package/resources/programming/basic_wiki/SPACE$().md +38 -0
- package/resources/programming/basic_wiki/SPC().md +34 -0
- package/resources/programming/basic_wiki/SPRITE$().md +50 -0
- package/resources/programming/basic_wiki/SPRITE.md +31 -0
- package/resources/programming/basic_wiki/SQR().md +32 -0
- package/resources/programming/basic_wiki/STICK().md +70 -0
- package/resources/programming/basic_wiki/STOP.md +70 -0
- package/resources/programming/basic_wiki/STR$().md +37 -0
- package/resources/programming/basic_wiki/STRIG().md +82 -0
- package/resources/programming/basic_wiki/STRING$().md +42 -0
- package/resources/programming/basic_wiki/SWAP.md +62 -0
- package/resources/programming/basic_wiki/TAB().md +38 -0
- package/resources/programming/basic_wiki/TAN().md +36 -0
- package/resources/programming/basic_wiki/TIME.md +59 -0
- package/resources/programming/basic_wiki/TROFF.md +21 -0
- package/resources/programming/basic_wiki/TRON.md +39 -0
- package/resources/programming/basic_wiki/USR().md +66 -0
- package/resources/programming/basic_wiki/VAL().md +36 -0
- package/resources/programming/basic_wiki/VARPTR().md +50 -0
- package/resources/programming/basic_wiki/VDP().md +103 -0
- package/resources/programming/basic_wiki/VPEEK().md +46 -0
- package/resources/programming/basic_wiki/VPOKE.md +48 -0
- package/resources/programming/basic_wiki/WAIT.md +38 -0
- package/resources/programming/basic_wiki/WIDTH.md +76 -0
- package/resources/programming/basic_wiki/XOR.md +72 -0
- package/resources/programming/basic_wiki/_toc.json +871 -0
- package/resources/sdcc/sdccman.lyx +81574 -0
- package/resources/sdcc/toc.json +77 -0
- package/vector-db/index.json +1 -0
- /package/resources/msx-unapi/{Ethernet_UNAPI_specification_1.1.md → Ethernet_UNAPI_specification_1_1.md} +0 -0
- /package/resources/msx-unapi/{MSX_UNAPI_specification_1.1.md → MSX_UNAPI_specification_1_1.md} +0 -0
- /package/resources/processors/{Z80_R800_instruction_set.md → z80_r800_instruction_set.md} +0 -0
package/README.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
*"Orchestrating a binary opera where AI conducts, MCP interprets, and openMSX acts as the 8-bit diva."*
|
|
4
4
|
|
|
5
|
+
[](https://github.com/nataliapc)
|
|
6
|
+
[](https://github.com/nataliapc/mcp-openmsx/blob/main/LICENSE)
|
|
7
|
+
[
|
|
8
|
+
](https://github.com/nataliapc/mcp-openmsx/stargazers/)
|
|
9
|
+
[](https://www.npmjs.com/package/@nataliapc/mcp-openmsx?activeTab=versions)
|
|
10
|
+
[]()
|
|
11
|
+
|
|
5
12
|
A [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) server for automating [openMSX emulator](https://github.com/openMSX/openMSX) instances.
|
|
6
13
|
|
|
7
14
|
This server provides comprehensive tools for MSX software development, testing, and automation through standardized MCP protocols.
|
|
@@ -17,7 +24,8 @@ This project creates a bridge between modern AI-assisted development (e.g. GitHu
|
|
|
17
24
|
- **Video Control**: VDP register manipulation and screen capture.
|
|
18
25
|
- **Memory Operations**: Read/write RAM, VRAM, and I/O port access.
|
|
19
26
|
- **Automation**: Keyboard input simulation and savestate management.
|
|
20
|
-
- **
|
|
27
|
+
- **Vector DB Integration**: Query an embedded vector database with MSX resources for development support.
|
|
28
|
+
- **Hybrid Mode**: This MCP server supports hybrid access mode (_STDIO_ and _HTTP_ transports).
|
|
21
29
|
|
|
22
30
|
## 🏗️ Architecture
|
|
23
31
|
|
|
@@ -64,6 +72,11 @@ 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.
|
|
76
|
+
|
|
77
|
+
### Documentation Tools
|
|
78
|
+
- `vector_db_query`: Query the Vector DB resources to obtain information about MSX systems, cartridges, and other development resources.
|
|
79
|
+
- `msxdocs_resource_get`: Retrieve MCP resources for MCP clients that don't support MCP resources.
|
|
67
80
|
|
|
68
81
|
## 📚 Available MCP Resources
|
|
69
82
|
|
|
@@ -85,10 +98,11 @@ There are more than 60 resources available, some included directly in the MCP an
|
|
|
85
98
|
- `MSX-UNAPI`
|
|
86
99
|
- `MSX BASIC`
|
|
87
100
|
|
|
88
|
-
And
|
|
101
|
+
And books and manuals:
|
|
89
102
|
|
|
90
103
|
- `MSX2 Technical Handbook`
|
|
91
104
|
- `The MSX Red Book`
|
|
105
|
+
- `SDCC Compiler`
|
|
92
106
|
|
|
93
107
|
### Resources from:
|
|
94
108
|
|
|
@@ -98,8 +112,9 @@ And two books:
|
|
|
98
112
|
- [MSX2 Technical Handbook](https://github.com/Konamiman/MSX2-Technical-Handbook)
|
|
99
113
|
- [Konamiman MSX-UNAPI-specification](https://github.com/Konamiman/MSX-UNAPI-specification)
|
|
100
114
|
- [BiFi MSX Net](http://bifi.msxnet.org/msxnet/)
|
|
101
|
-
- [
|
|
115
|
+
- [MRC Wiki](https://www.msx.org/wiki/Main_Page)
|
|
102
116
|
- [MSX Banzai!](http://msxbanzai.tni.nl/)
|
|
117
|
+
- [SDCC](https://sdcc.sourceforge.net/)
|
|
103
118
|
|
|
104
119
|
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
120
|
|
|
@@ -107,12 +122,16 @@ The rights to these resources belong to their respective authors and are distrib
|
|
|
107
122
|
|
|
108
123
|
## 🚀 Quick Start
|
|
109
124
|
|
|
110
|
-
You can use this MCP server in this basic way with the [precompiled NPM package](https://www.npmjs.com/package/@nataliapc/mcp-openmsx).
|
|
125
|
+
You can use this MCP server in this basic way with the [precompiled NPM package](https://www.npmjs.com/package/@nataliapc/mcp-openmsx).
|
|
126
|
+
|
|
127
|
+
### 🟢 Quick installation with VSCode
|
|
111
128
|
|
|
112
|
-
|
|
129
|
+
[](vscode:mcp/install?%7B%22name%22%3A%22mcp-openmsx%22%2C%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40nataliapc%2Fmcp-openmsx%22%5D%7D)
|
|
113
130
|
|
|
131
|
+
Steps to install the MCP server in VSCode:
|
|
114
132
|
* Install [Github Copilot extension](https://code.visualstudio.com/docs/copilot/overview)
|
|
115
|
-
*
|
|
133
|
+
* Use the **Install in VS Code** button above to install the MCP server in your VSCode workspace.
|
|
134
|
+
* Or add to your workspace a file `.vscode/mcp.json` with the json configuration below.
|
|
116
135
|
|
|
117
136
|
### STDIO mode (recommended)
|
|
118
137
|
|
|
@@ -123,7 +142,7 @@ You can use this MCP server in this basic way with the [precompiled NPM package]
|
|
|
123
142
|
"command": "npx",
|
|
124
143
|
"args": ["@nataliapc/mcp-openmsx"],
|
|
125
144
|
"env": {
|
|
126
|
-
"OPENMSX_SHARE_DIR": "
|
|
145
|
+
"OPENMSX_SHARE_DIR": "C:\the\location\of\your\openmsx\share\folder"
|
|
127
146
|
}
|
|
128
147
|
}
|
|
129
148
|
}
|
|
@@ -150,7 +169,9 @@ You can use this MCP server in this basic way with the [precompiled NPM package]
|
|
|
150
169
|
|
|
151
170
|
### 🟢 Basic Usage with Claude Desktop
|
|
152
171
|
|
|
153
|
-
|
|
172
|
+
Follow [these instrutions](https://modelcontextprotocol.io/quickstart/user#for-claude-desktop-users) to access Claude's `claude_desktop_config.json` file.
|
|
173
|
+
|
|
174
|
+
Edit it to include the following JSON entry:
|
|
154
175
|
|
|
155
176
|
```json
|
|
156
177
|
{
|
|
@@ -159,13 +180,15 @@ Add to your `claude_desktop_config.json`:
|
|
|
159
180
|
"command": "npx",
|
|
160
181
|
"args": ["@nataliapc/mcp-openmsx"],
|
|
161
182
|
"env": {
|
|
162
|
-
"OPENMSX_SHARE_DIR": "
|
|
183
|
+
"OPENMSX_SHARE_DIR": "C:\the\location\of\your\openmsx\share\folder"
|
|
163
184
|
}
|
|
164
185
|
}
|
|
165
186
|
}
|
|
166
187
|
}
|
|
167
188
|
```
|
|
168
189
|
|
|
190
|
+
**Note:** Environment variables are optional. Customize them as you need.
|
|
191
|
+
|
|
169
192
|
### 🟢 Environment Variables
|
|
170
193
|
|
|
171
194
|
| Variable | Description | Default Value | Example |
|
|
@@ -181,6 +204,8 @@ Add to your `claude_desktop_config.json`:
|
|
|
181
204
|
|
|
182
205
|
## 🧑💻 Advanced Manual Usage
|
|
183
206
|
|
|
207
|
+
This is not needed for using the MCP server, but if you want to install it manually, follow these steps.
|
|
208
|
+
|
|
184
209
|
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.
|
|
185
210
|
|
|
186
211
|
### Manual installation
|
|
@@ -216,10 +241,11 @@ MCP_TRANSPORT=http mcp-openmsx
|
|
|
216
241
|
mcp-openmsx http
|
|
217
242
|
```
|
|
218
243
|
|
|
219
|
-
|
|
220
244
|
## 💡 Development
|
|
221
245
|
|
|
222
|
-
|
|
246
|
+
This is not needed for using the MCP server, but if you want to contribute or modify the code, follow these steps.
|
|
247
|
+
|
|
248
|
+
### Prerequisites to build
|
|
223
249
|
|
|
224
250
|
- Node.js >= 18.0.0
|
|
225
251
|
- TypeScript
|
|
@@ -240,7 +266,6 @@ npm run build
|
|
|
240
266
|
npm run dev
|
|
241
267
|
```
|
|
242
268
|
|
|
243
|
-
|
|
244
269
|
## 🪪 License
|
|
245
270
|
|
|
246
271
|
GPL2 License - see [LICENSE](LICENSE) file for details.
|
package/dist/server.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
* through TCL commands via stdio.
|
|
7
7
|
*
|
|
8
8
|
* @package @nataliapc/mcp-openmsx
|
|
9
|
-
* @version 1.1.14
|
|
10
9
|
* @author Natalia Pujol Cremades (@nataliapc)
|
|
11
10
|
* @license GPL2
|
|
12
11
|
*/
|
|
@@ -20,10 +19,14 @@ import express from "express";
|
|
|
20
19
|
import fs from "fs/promises";
|
|
21
20
|
import path from "path";
|
|
22
21
|
import { openMSXInstance } from "./openmsx.js";
|
|
22
|
+
import { VectorDB } from "./vectordb.js";
|
|
23
23
|
import { fetchCleanWebpage, addFileExtension, listResourcesDirectory, encodeTypeText, isErrorResponse, getResponseContent } from "./utils.js";
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
import { createRequire } from 'module';
|
|
25
|
+
// Dynamically obtain PACKAGE_VERSION from package.json at runtime
|
|
26
|
+
const require = createRequire(import.meta.url);
|
|
27
|
+
export const PACKAGE_VERSION = require('../package.json').version;
|
|
26
28
|
const resourcesDir = path.join(path.dirname(new URL(import.meta.url).pathname), "../resources");
|
|
29
|
+
const vectorDbDir = path.join(path.dirname(new URL(import.meta.url).pathname), "../vector-db");
|
|
27
30
|
// Defaults for openMSX paths
|
|
28
31
|
var OPENMSX_EXECUTABLE = 'openmsx';
|
|
29
32
|
var OPENMSX_SHARE_DIR = '/usr/share/openmsx';
|
|
@@ -861,96 +864,152 @@ The parameter scrbasename is the name of the filename (without path) to save the
|
|
|
861
864
|
response !== undefined ? response : `Error: No response for command "${command}".`
|
|
862
865
|
]);
|
|
863
866
|
});
|
|
867
|
+
server.registerTool(
|
|
868
|
+
// Name of the tool (used to call it)
|
|
869
|
+
"vector_db_query", {
|
|
870
|
+
title: "Vector DB query from resources",
|
|
871
|
+
// Description of the tool (what it does)
|
|
872
|
+
description: `Query the Vector DB resources to obtain information about MSX system, cartridges, programming, and other development resources.
|
|
873
|
+
The query is a string used to search within the Vector DB resources; it is case-insensitive and may contain spaces.
|
|
874
|
+
The response is the list of the top 10 result resources that match the query, including their score, title, and resource URI, and are sorted in descending order by proximity score to the query.
|
|
875
|
+
**Important Note**: The Vector DB resources are in english, japanese, or dutch.
|
|
876
|
+
`,
|
|
877
|
+
// Schema for the tool (input validation)
|
|
878
|
+
inputSchema: {
|
|
879
|
+
query: z.string().min(2).max(100).describe("Query string to search in the Vector DB resources, case-insensitive and may contain spaces."),
|
|
880
|
+
},
|
|
881
|
+
// outputSchema: {
|
|
882
|
+
// results: z.array(z.object({
|
|
883
|
+
// score: z.number().describe("Proximity score of the result to the query, higher is better."),
|
|
884
|
+
// title: z.string().describe("Title of the resource."),
|
|
885
|
+
// uri: z.string().describe("URI of the resource, which can be used to access the resource."),
|
|
886
|
+
// document: z.string().describe("Document chunk of the resource, retrieved from the Vector DB."),
|
|
887
|
+
// id: z.string().describe("Unique resource chunk ID, used internally by the Vector DB."),
|
|
888
|
+
// }))
|
|
889
|
+
// },
|
|
890
|
+
},
|
|
891
|
+
// Handler for the tool (function to be executed when the tool is called)
|
|
892
|
+
async ({ query }) => {
|
|
893
|
+
const results = await VectorDB.getInstance().query(query);
|
|
894
|
+
return {
|
|
895
|
+
content: [{
|
|
896
|
+
type: "text",
|
|
897
|
+
text: JSON.stringify(results),
|
|
898
|
+
}],
|
|
899
|
+
results: results,
|
|
900
|
+
isError: false,
|
|
901
|
+
};
|
|
902
|
+
});
|
|
864
903
|
// ============================================================================
|
|
865
904
|
// MSX Documentation resources
|
|
866
905
|
const resdocs = (await listResourcesDirectory(resourcesDir)).sort();
|
|
906
|
+
const regResources = [];
|
|
867
907
|
for (let index = 0; index < resdocs.length; index++) {
|
|
908
|
+
// Read the toc.json file if exists, otherwise skip this section
|
|
868
909
|
const sectionName = resdocs[index];
|
|
869
910
|
const tocFile = path.join(resourcesDir, `${sectionName}/toc.json`);
|
|
870
|
-
|
|
911
|
+
let tocContent = { toc: [] };
|
|
912
|
+
try {
|
|
913
|
+
tocContent = JSON.parse(await fs.readFile(tocFile, 'utf8'));
|
|
914
|
+
}
|
|
915
|
+
catch (error) {
|
|
916
|
+
// The toc.json file does not exist or is invalid, skip this section
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
// Register each item in the toc.json as a resource
|
|
871
920
|
tocContent.toc.forEach((item, itemIndex) => {
|
|
872
|
-
const itemName = path.parse(item.uri.split('/').pop()).
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
921
|
+
const itemName = path.parse(item.uri.split('/').pop()).base || '';
|
|
922
|
+
let resource = {
|
|
923
|
+
uri: item.uri,
|
|
924
|
+
filename: '',
|
|
925
|
+
resource: server.registerResource(
|
|
926
|
+
// Name of the resource (used to call it)
|
|
927
|
+
`msxdocs_${sectionName}_${item.title.replace(/[^a-z0-9]+/gi, '_').toLowerCase()}`,
|
|
928
|
+
// Resource URI template
|
|
929
|
+
item.uri,
|
|
930
|
+
// Metadata for the resource
|
|
931
|
+
{
|
|
932
|
+
title: item.title || `MSX Documentation '${sectionName}': ${itemName}`,
|
|
933
|
+
description: item.description || `Documentation for MSX resource '${sectionName}': ${itemName}`,
|
|
934
|
+
mimeType: item.mimeType || 'text/markdown',
|
|
935
|
+
},
|
|
936
|
+
// Handler for the resource (function to be executed when the resource is called)
|
|
937
|
+
async (uri) => {
|
|
938
|
+
let resourceContent;
|
|
939
|
+
let mimeType;
|
|
940
|
+
if (uri.href.startsWith('http://') || uri.href.startsWith('https://')) {
|
|
941
|
+
// Fetch the resource from the URL
|
|
942
|
+
try {
|
|
943
|
+
[resourceContent, mimeType] = await fetchCleanWebpage(uri.href);
|
|
944
|
+
}
|
|
945
|
+
catch (error) {
|
|
946
|
+
// Throw exception (MCP protocol requirement)
|
|
947
|
+
throw error;
|
|
948
|
+
}
|
|
892
949
|
}
|
|
893
|
-
|
|
894
|
-
//
|
|
895
|
-
|
|
950
|
+
else {
|
|
951
|
+
// Read the resource from the local MCP server resources directory
|
|
952
|
+
try {
|
|
953
|
+
let resourceFile;
|
|
954
|
+
[mimeType, resourceFile] = await addFileExtension(path.join(resourcesDir, `${sectionName}/${itemName}`));
|
|
955
|
+
resourceContent = await fs.readFile(resourceFile, 'utf8');
|
|
956
|
+
}
|
|
957
|
+
catch (error) {
|
|
958
|
+
// Throw exception (MCP protocol requirement)
|
|
959
|
+
throw new Error(`Error reading resource ${sectionName}/${item.uri}: ${error instanceof Error ? error.message : String(error)}`);
|
|
960
|
+
}
|
|
896
961
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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
|
-
});
|
|
962
|
+
return {
|
|
963
|
+
contents: [{
|
|
964
|
+
uri: uri.href,
|
|
965
|
+
text: resourceContent,
|
|
966
|
+
mimeType: mimeType || 'text/plain',
|
|
967
|
+
}],
|
|
968
|
+
};
|
|
969
|
+
})
|
|
970
|
+
};
|
|
971
|
+
regResources.push(resource);
|
|
918
972
|
});
|
|
919
973
|
}
|
|
920
974
|
;
|
|
975
|
+
// Source: https://www.msx.org/wiki/Category:MSX-BASIC_Instructions
|
|
976
|
+
const basicInstructions = [
|
|
977
|
+
"ABS()", "AND", "ASC()", "ATN()", "AUTO", "BASE()", "BEEP", "BIN$()", "BLOAD", "BSAVE", "CALL", "CALL ADJUST", "CALL IMPOSE", "CALL OPTIONS", "CALL PAUSE",
|
|
978
|
+
"CALL PCMPLAY", "CALL PCMREC", "CDBL()", "CHR$()", "CINT()", "CIRCLE", "CLEAR", "CLOAD", "CLOAD?", "CLOSE", "CLS", "COLOR", "COLOR=", "COLOR SPRITE()",
|
|
979
|
+
"COLOR SPRITE$()", "CONT", "COPY", "COPY SCREEN", "COS()", "CSAVE", "CSNG()", "CSRLIN", "DATA", "DEFDBL", "DEF FN", "DEFINT", "DEFSNG", "DEFSTR", "DEF USR",
|
|
980
|
+
"DELETE", "DIM", "DRAW", "ELSE", "END", "EOF()", "EQV", "ERASE", "ERL", "ERR", "ERROR", "EXP()", "FIELD", "FIX()", "FN", "FOR...NEXT", "FRE()", "GET DATE",
|
|
981
|
+
"GET TIME", "GOSUB", "GOTO", "HEX$()", "IF...GOTO...ELSE", "IF...THEN...ELSE", "IMP", "INKEY$", "INP()", "INPUT", "INPUT$()", "INSTR()", "INT()", "INTERVAL",
|
|
982
|
+
"KEY", "KEY()", "LEFT$()", "LEN()", "LET", "LINE", "LINE INPUT", "LIST", "LLIST", "LOAD", "LOCATE", "LOG()", "LPOS()", "LPRINT", "MAXFILES", "MERGE",
|
|
983
|
+
"MID$()", "MOD", "MOTOR", "NEW", "NOT", "OCT$()", "ON...GOSUB", "ON...GOTO", "ON ERROR GOTO", "ON INTERVAL GOSUB", "ON KEY GOSUB", "ON SPRITE GOSUB",
|
|
984
|
+
"ON STOP GOSUB", "ON STRIG GOSUB", "OPEN", "OR", "OUT", "PAD()", "PAINT", "PDL()", "PEEK()", "PLAY", "PLAY()", "POINT", "POKE", "POS()", "PRESET", "PRINT",
|
|
985
|
+
"PSET", "PUT KANJI", "PUT SPRITE", "READ", "REM", "RENUM", "RESTORE", "RESUME", "RETURN", "RIGHT$()", "RND()", "RUN", "SAVE", "SCREEN", "SET ADJUST",
|
|
986
|
+
"SET BEEP", "SET DATE", "SET PAGE", "SET PASSWORD", "SET PROMPT", "SET SCREEN", "SET SCROLL", "SET TIME", "SET TITLE", "SET VIDEO", "SGN()", "SIN()",
|
|
987
|
+
"SOUND", "SPACE$()", "SPC()", "SPRITE", "SPRITE$()", "SQR()", "STICK()", "STOP", "STR$()", "STRIG()", "STRING$()", "SWAP", "TAB()", "TAN()", "TIME", "TROFF",
|
|
988
|
+
"TRON", "USR()", "VAL()", "VARPTR()", "VDP()", "VPEEK()", "VPOKE", "WAIT", "WIDTH", "XOR"
|
|
989
|
+
];
|
|
921
990
|
server.resource("msxdocs_basic_wiki", new ResourceTemplate("msxdocs://basic_wiki/{instruction}", {
|
|
922
991
|
list: undefined,
|
|
923
992
|
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
|
-
],
|
|
993
|
+
instruction: (value) => basicInstructions,
|
|
938
994
|
},
|
|
939
995
|
}), {
|
|
940
996
|
title: "MSX BASIC Instructions Documentation",
|
|
941
997
|
description: "Documentation about all the standard MSX BASIC instructions from www.msx.org",
|
|
942
998
|
mimeType: "text/html",
|
|
943
999
|
}, async (uri, variables) => {
|
|
944
|
-
|
|
945
|
-
const url = `https://www.msx.org/wiki/${instruction}`;
|
|
1000
|
+
let instruction = variables.instruction;
|
|
946
1001
|
let resourceContent;
|
|
947
1002
|
let mimeType;
|
|
1003
|
+
// urldecode the instruction to avoid issues with special characters
|
|
1004
|
+
instruction = decodeURIComponent(instruction).replaceAll(' ', '_');
|
|
948
1005
|
try {
|
|
949
|
-
|
|
1006
|
+
let resourceFile;
|
|
1007
|
+
[mimeType, resourceFile] = await addFileExtension(path.join(resourcesDir, 'programming', 'basic_wiki', instruction));
|
|
1008
|
+
resourceContent = await fs.readFile(resourceFile, 'utf8');
|
|
950
1009
|
}
|
|
951
1010
|
catch (error) {
|
|
952
1011
|
// Throw exception (MCP protocol requirement)
|
|
953
|
-
throw error;
|
|
1012
|
+
throw new Error(`Error reading resource programming/basic_wiki/${instruction}: ${error instanceof Error ? error.message : String(error)}`);
|
|
954
1013
|
}
|
|
955
1014
|
return {
|
|
956
1015
|
contents: [{
|
|
@@ -960,6 +1019,73 @@ The parameter scrbasename is the name of the filename (without path) to save the
|
|
|
960
1019
|
}],
|
|
961
1020
|
};
|
|
962
1021
|
});
|
|
1022
|
+
// Register the tool to get a specific MSX documentation resource
|
|
1023
|
+
server.registerTool(
|
|
1024
|
+
// Name of the tool (used to call it)
|
|
1025
|
+
"msxdocs_resource_get", {
|
|
1026
|
+
title: "Tool to get a resource",
|
|
1027
|
+
// Description of the tool (what it does)
|
|
1028
|
+
description: "Get a specific available MSX documentation resource from this MCP server resources.",
|
|
1029
|
+
// Schema for the tool (input validation)
|
|
1030
|
+
inputSchema: {
|
|
1031
|
+
resourceName: z.enum(regResources.map(res => res.resource.name)).describe("Name of the resource to obtain, e.g. 'msxdocs_programming_interrupts'"),
|
|
1032
|
+
},
|
|
1033
|
+
},
|
|
1034
|
+
// Handler for the tool (function to be executed when the tool is called)
|
|
1035
|
+
async ({ resourceName }, extra) => {
|
|
1036
|
+
const index = regResources.findIndex((res) => res.resource.name === resourceName);
|
|
1037
|
+
const uriString = index !== -1 ? regResources[index].uri : undefined;
|
|
1038
|
+
const resource = index !== -1 ? regResources[index].resource : undefined;
|
|
1039
|
+
if (!resource || !uriString) {
|
|
1040
|
+
return getResponseContent([
|
|
1041
|
+
`Error: Resource '${resourceName}' not found.`
|
|
1042
|
+
]);
|
|
1043
|
+
}
|
|
1044
|
+
let documentationText = '';
|
|
1045
|
+
try {
|
|
1046
|
+
// If the resource is found, return its content
|
|
1047
|
+
let resourceContent = await resource.readCallback(new URL(uriString), extra);
|
|
1048
|
+
if (!resourceContent.contents?.length) {
|
|
1049
|
+
return getResponseContent([
|
|
1050
|
+
`Error: Resource '${resourceName}' has no content available.`
|
|
1051
|
+
]);
|
|
1052
|
+
}
|
|
1053
|
+
// Return the first content item (assuming it's the main content)
|
|
1054
|
+
const content = resourceContent.contents[0];
|
|
1055
|
+
if ('text' in content) {
|
|
1056
|
+
documentationText = content.text;
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
return getResponseContent([
|
|
1060
|
+
`Error: Resource '${resourceName}' has no content available.`
|
|
1061
|
+
]);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
catch (error) {
|
|
1065
|
+
return getResponseContent([
|
|
1066
|
+
`Error: error reading resource '${resourceName}': ${error instanceof Error ? error.message : String(error)}`
|
|
1067
|
+
]);
|
|
1068
|
+
}
|
|
1069
|
+
return {
|
|
1070
|
+
content: [{
|
|
1071
|
+
type: "text",
|
|
1072
|
+
text: `Content from resource: '${resourceName}'`,
|
|
1073
|
+
}, {
|
|
1074
|
+
type: "text",
|
|
1075
|
+
text: documentationText || 'No content available for this resource.',
|
|
1076
|
+
mimeType: resource.metadata?.mimeType || 'text/plain',
|
|
1077
|
+
} /*, {
|
|
1078
|
+
type: "resource",
|
|
1079
|
+
resource: {
|
|
1080
|
+
uri: resource.metadata?.uri || resourceName,
|
|
1081
|
+
title: resource.metadata?.title || `Resource: ${resourceName}`,
|
|
1082
|
+
mimeType: resource.metadata?.mimeType || 'text/plain',
|
|
1083
|
+
text: documentationText || 'No content available for this resource.',
|
|
1084
|
+
}
|
|
1085
|
+
}*/
|
|
1086
|
+
],
|
|
1087
|
+
};
|
|
1088
|
+
});
|
|
963
1089
|
}
|
|
964
1090
|
// ============================================================================
|
|
965
1091
|
// Cleanup handlers for graceful shutdown of MCP server
|
|
@@ -1071,8 +1197,8 @@ async function startHttpServer() {
|
|
|
1071
1197
|
}
|
|
1072
1198
|
await transport.handleRequest(req, res, req.body);
|
|
1073
1199
|
});
|
|
1074
|
-
//
|
|
1075
|
-
|
|
1200
|
+
// Reusable handle GET / DELETE requests
|
|
1201
|
+
const handleSessionRequest = async (req, res) => {
|
|
1076
1202
|
const sessionId = req.headers['mcp-session-id'];
|
|
1077
1203
|
if (!sessionId || !transports[sessionId]) {
|
|
1078
1204
|
res.status(400).send('Invalid or missing session ID');
|
|
@@ -1080,7 +1206,9 @@ async function startHttpServer() {
|
|
|
1080
1206
|
}
|
|
1081
1207
|
const transport = transports[sessionId];
|
|
1082
1208
|
await transport.handleRequest(req, res);
|
|
1083
|
-
}
|
|
1209
|
+
};
|
|
1210
|
+
app.get('/mcp', handleSessionRequest);
|
|
1211
|
+
app.delete('/mcp', handleSessionRequest);
|
|
1084
1212
|
const port = process.env.MCP_HTTP_PORT || 3000;
|
|
1085
1213
|
app.listen(port, () => {
|
|
1086
1214
|
console.log(`MCP Server listening on port ${port}`);
|
|
@@ -1134,6 +1262,7 @@ async function main() {
|
|
|
1134
1262
|
MACHINES_DIR = `${OPENMSX_SHARE_DIR}machines`;
|
|
1135
1263
|
EXTENSIONS_DIR = `${OPENMSX_SHARE_DIR}extensions`;
|
|
1136
1264
|
}
|
|
1265
|
+
VectorDB.setIndexDirectory(vectorDbDir);
|
|
1137
1266
|
// Detect transport type from environment or command line
|
|
1138
1267
|
const transportType = process.env.MCP_TRANSPORT || process.argv[2] || 'stdio';
|
|
1139
1268
|
if (transportType === 'http') {
|
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
|
|
@@ -51,13 +54,13 @@ export async function addFileExtension(filePath) {
|
|
|
51
54
|
return ['text/plain', filePath];
|
|
52
55
|
}
|
|
53
56
|
/**
|
|
54
|
-
* List all folders in the resources directory
|
|
57
|
+
* List all folders and subfolders in the resources directory
|
|
55
58
|
* @param resourcesDir - Path to the resources directory
|
|
56
59
|
* @returns Promise<string[]> - List of folder names in the resources directory
|
|
57
60
|
*/
|
|
58
61
|
export async function listResourcesDirectory(resourcesDir) {
|
|
59
62
|
try {
|
|
60
|
-
const directories = await fs.readdir(resourcesDir, { withFileTypes: true });
|
|
63
|
+
const directories = await fs.readdir(resourcesDir, { withFileTypes: true, recursive: true });
|
|
61
64
|
const folderNames = directories
|
|
62
65
|
.filter(dirent => dirent.isDirectory())
|
|
63
66
|
.map(dirent => dirent.name);
|
|
@@ -77,12 +80,48 @@ export async function fetchCleanWebpage(url) {
|
|
|
77
80
|
let resourceContent;
|
|
78
81
|
let mimeType = 'text/plain';
|
|
79
82
|
try {
|
|
80
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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)
|
|
@@ -190,3 +229,11 @@ export function getResponseContent(response, isError = false) {
|
|
|
190
229
|
isError: hasError
|
|
191
230
|
};
|
|
192
231
|
}
|
|
232
|
+
/*
|
|
233
|
+
* Sleep for a specified number of milliseconds
|
|
234
|
+
* @param ms - Number of milliseconds to sleep
|
|
235
|
+
* @returns Promise that resolves after the specified time
|
|
236
|
+
*/
|
|
237
|
+
export function sleep(ms) {
|
|
238
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
239
|
+
}
|
package/dist/vectordb.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector Database wrapper class
|
|
3
|
+
*
|
|
4
|
+
* @author Natalia Pujol Cremades (@nataliapc)
|
|
5
|
+
* @license GPL2
|
|
6
|
+
*/
|
|
7
|
+
import { LocalIndex } from 'vectra';
|
|
8
|
+
import embeddings from '@themaximalist/embeddings.js';
|
|
9
|
+
export class VectorDB {
|
|
10
|
+
static instance;
|
|
11
|
+
static vectorDbDir = '../vector-db';
|
|
12
|
+
index;
|
|
13
|
+
static getInstance() {
|
|
14
|
+
if (!VectorDB.instance) {
|
|
15
|
+
VectorDB.instance = new VectorDB();
|
|
16
|
+
}
|
|
17
|
+
return VectorDB.instance;
|
|
18
|
+
}
|
|
19
|
+
static setIndexDirectory(dbDir) {
|
|
20
|
+
VectorDB.vectorDbDir = dbDir;
|
|
21
|
+
}
|
|
22
|
+
constructor(dbDir = VectorDB.vectorDbDir) {
|
|
23
|
+
this.index = new LocalIndex(dbDir);
|
|
24
|
+
if (!this.initDatabase()) {
|
|
25
|
+
throw new Error('Failed to initialize VectorDB: Index does not exist');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async initDatabase() {
|
|
29
|
+
return await this.index.isIndexCreated();
|
|
30
|
+
}
|
|
31
|
+
async getVector(text) {
|
|
32
|
+
const response = await embeddings(text);
|
|
33
|
+
if (!response || !Array.isArray(response) || response.length === 0) {
|
|
34
|
+
throw new Error('Failed to generate embedding vector');
|
|
35
|
+
}
|
|
36
|
+
return response;
|
|
37
|
+
}
|
|
38
|
+
async query(text) {
|
|
39
|
+
const vector = await this.getVector(text);
|
|
40
|
+
let results = [];
|
|
41
|
+
try {
|
|
42
|
+
results = await this.index.queryItems(vector, text, 10);
|
|
43
|
+
if (results.length > 0) {
|
|
44
|
+
return results.map(result => {
|
|
45
|
+
return {
|
|
46
|
+
score: result.score.toFixed(4),
|
|
47
|
+
uri: result.item.metadata?.uri || 'unknown',
|
|
48
|
+
title: result.item.metadata?.title || 'unknown',
|
|
49
|
+
document: String(result.item.metadata?.document || ''),
|
|
50
|
+
id: result.item.metadata?.id || 'unknown',
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new Error(`Query failed: ${error}`);
|
|
57
|
+
}
|
|
58
|
+
return results;
|
|
59
|
+
}
|
|
60
|
+
}
|