@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.
Files changed (196) hide show
  1. package/README.md +37 -12
  2. package/dist/server.js +198 -69
  3. package/dist/utils.js +54 -7
  4. package/dist/vectordb.js +60 -0
  5. package/package.json +10 -3
  6. package/resources/audio/MGSC111.txt +669 -0
  7. package/resources/audio/msx-midi.md +872 -0
  8. package/resources/audio/opl4tech.txt +688 -0
  9. package/resources/audio/psg_registers.md +281 -0
  10. package/resources/audio/sound_cartridge_scc.md +123 -0
  11. package/resources/audio/sound_cartridge_scci.md +250 -0
  12. package/resources/audio/toc.json +18 -7
  13. package/resources/book--msx2-technical-handbook/toc.json +1 -1
  14. package/resources/msx-unapi/toc.json +2 -2
  15. package/resources/processors/toc.json +4 -4
  16. package/resources/processors/z80_detailed_instruction_set.md +80 -68
  17. package/resources/programming/basic_wiki/ABS().md +36 -0
  18. package/resources/programming/basic_wiki/AND.md +71 -0
  19. package/resources/programming/basic_wiki/ASC().md +38 -0
  20. package/resources/programming/basic_wiki/ATN().md +36 -0
  21. package/resources/programming/basic_wiki/AUTO.md +39 -0
  22. package/resources/programming/basic_wiki/BASE().md +147 -0
  23. package/resources/programming/basic_wiki/BEEP.md +27 -0
  24. package/resources/programming/basic_wiki/BIN$().md +36 -0
  25. package/resources/programming/basic_wiki/BLOAD.md +63 -0
  26. package/resources/programming/basic_wiki/BSAVE.md +61 -0
  27. package/resources/programming/basic_wiki/CALL.md +391 -0
  28. package/resources/programming/basic_wiki/CALL_ADJUST.md +40 -0
  29. package/resources/programming/basic_wiki/CALL_IMPOSE.md +28 -0
  30. package/resources/programming/basic_wiki/CALL_OPTIONS.md +26 -0
  31. package/resources/programming/basic_wiki/CALL_PAUSE.md +119 -0
  32. package/resources/programming/basic_wiki/CALL_PCMPLAY.md +60 -0
  33. package/resources/programming/basic_wiki/CALL_PCMREC.md +70 -0
  34. package/resources/programming/basic_wiki/CDBL().md +36 -0
  35. package/resources/programming/basic_wiki/CHR$().md +51 -0
  36. package/resources/programming/basic_wiki/CINT().md +36 -0
  37. package/resources/programming/basic_wiki/CIRCLE.md +51 -0
  38. package/resources/programming/basic_wiki/CLEAR.md +39 -0
  39. package/resources/programming/basic_wiki/CLOAD.md +27 -0
  40. package/resources/programming/basic_wiki/CLOAD?.md +31 -0
  41. package/resources/programming/basic_wiki/CLOSE.md +44 -0
  42. package/resources/programming/basic_wiki/CLS.md +51 -0
  43. package/resources/programming/basic_wiki/COLOR.md +143 -0
  44. package/resources/programming/basic_wiki/COLOR=.md +93 -0
  45. package/resources/programming/basic_wiki/COLOR_SPRITE$().md +83 -0
  46. package/resources/programming/basic_wiki/COLOR_SPRITE().md +85 -0
  47. package/resources/programming/basic_wiki/CONT.md +23 -0
  48. package/resources/programming/basic_wiki/COPY.md +215 -0
  49. package/resources/programming/basic_wiki/COPY_SCREEN.md +61 -0
  50. package/resources/programming/basic_wiki/COS().md +37 -0
  51. package/resources/programming/basic_wiki/CSAVE.md +35 -0
  52. package/resources/programming/basic_wiki/CSNG().md +36 -0
  53. package/resources/programming/basic_wiki/CSRLIN.md +33 -0
  54. package/resources/programming/basic_wiki/DATA.md +47 -0
  55. package/resources/programming/basic_wiki/DEFDBL.md +40 -0
  56. package/resources/programming/basic_wiki/DEFINT.md +40 -0
  57. package/resources/programming/basic_wiki/DEFSNG.md +40 -0
  58. package/resources/programming/basic_wiki/DEFSTR.md +40 -0
  59. package/resources/programming/basic_wiki/DEF_FN.md +49 -0
  60. package/resources/programming/basic_wiki/DEF_USR.md +33 -0
  61. package/resources/programming/basic_wiki/DELETE.md +49 -0
  62. package/resources/programming/basic_wiki/DIM.md +59 -0
  63. package/resources/programming/basic_wiki/DRAW.md +77 -0
  64. package/resources/programming/basic_wiki/ELSE.md +45 -0
  65. package/resources/programming/basic_wiki/END.md +32 -0
  66. package/resources/programming/basic_wiki/EOF().md +36 -0
  67. package/resources/programming/basic_wiki/EQV.md +76 -0
  68. package/resources/programming/basic_wiki/ERASE.md +35 -0
  69. package/resources/programming/basic_wiki/ERL.md +34 -0
  70. package/resources/programming/basic_wiki/ERR.md +143 -0
  71. package/resources/programming/basic_wiki/ERROR.md +145 -0
  72. package/resources/programming/basic_wiki/EXP().md +38 -0
  73. package/resources/programming/basic_wiki/FIELD.md +48 -0
  74. package/resources/programming/basic_wiki/FIX().md +44 -0
  75. package/resources/programming/basic_wiki/FN.md +61 -0
  76. package/resources/programming/basic_wiki/FOR...NEXT.md +80 -0
  77. package/resources/programming/basic_wiki/FRE().md +66 -0
  78. package/resources/programming/basic_wiki/GET_DATE.md +60 -0
  79. package/resources/programming/basic_wiki/GET_TIME.md +34 -0
  80. package/resources/programming/basic_wiki/GOSUB.md +41 -0
  81. package/resources/programming/basic_wiki/GOTO.md +41 -0
  82. package/resources/programming/basic_wiki/HEX$().md +36 -0
  83. package/resources/programming/basic_wiki/IF...GOTO...ELSE.md +55 -0
  84. package/resources/programming/basic_wiki/IF...THEN...ELSE.md +50 -0
  85. package/resources/programming/basic_wiki/IMP.md +83 -0
  86. package/resources/programming/basic_wiki/INKEY$.md +65 -0
  87. package/resources/programming/basic_wiki/INP().md +33 -0
  88. package/resources/programming/basic_wiki/INPUT$().md +51 -0
  89. package/resources/programming/basic_wiki/INPUT.md +93 -0
  90. package/resources/programming/basic_wiki/INSTR().md +44 -0
  91. package/resources/programming/basic_wiki/INT().md +44 -0
  92. package/resources/programming/basic_wiki/INTERVAL.md +57 -0
  93. package/resources/programming/basic_wiki/KEY().md +51 -0
  94. package/resources/programming/basic_wiki/KEY.md +254 -0
  95. package/resources/programming/basic_wiki/LEFT$().md +39 -0
  96. package/resources/programming/basic_wiki/LEN().md +36 -0
  97. package/resources/programming/basic_wiki/LET.md +68 -0
  98. package/resources/programming/basic_wiki/LINE.md +74 -0
  99. package/resources/programming/basic_wiki/LINE_INPUT.md +79 -0
  100. package/resources/programming/basic_wiki/LIST.md +58 -0
  101. package/resources/programming/basic_wiki/LLIST.md +43 -0
  102. package/resources/programming/basic_wiki/LOAD.md +56 -0
  103. package/resources/programming/basic_wiki/LOCATE.md +67 -0
  104. package/resources/programming/basic_wiki/LOG().md +36 -0
  105. package/resources/programming/basic_wiki/LPOS().md +31 -0
  106. package/resources/programming/basic_wiki/LPRINT.md +46 -0
  107. package/resources/programming/basic_wiki/MAXFILES.md +39 -0
  108. package/resources/programming/basic_wiki/MERGE.md +54 -0
  109. package/resources/programming/basic_wiki/MID$().md +72 -0
  110. package/resources/programming/basic_wiki/MOD.md +39 -0
  111. package/resources/programming/basic_wiki/MOTOR.md +46 -0
  112. package/resources/programming/basic_wiki/NEW.md +27 -0
  113. package/resources/programming/basic_wiki/NOT.md +61 -0
  114. package/resources/programming/basic_wiki/OCT$().md +36 -0
  115. package/resources/programming/basic_wiki/ON...GOSUB.md +45 -0
  116. package/resources/programming/basic_wiki/ON...GOTO.md +42 -0
  117. package/resources/programming/basic_wiki/ON_ERROR_GOTO.md +61 -0
  118. package/resources/programming/basic_wiki/ON_INTERVAL_GOSUB.md +54 -0
  119. package/resources/programming/basic_wiki/ON_KEY_GOSUB.md +56 -0
  120. package/resources/programming/basic_wiki/ON_SPRITE_GOSUB.md +41 -0
  121. package/resources/programming/basic_wiki/ON_STOP_GOSUB.md +56 -0
  122. package/resources/programming/basic_wiki/ON_STRIG_GOSUB.md +70 -0
  123. package/resources/programming/basic_wiki/OPEN.md +103 -0
  124. package/resources/programming/basic_wiki/OR.md +75 -0
  125. package/resources/programming/basic_wiki/OUT.md +35 -0
  126. package/resources/programming/basic_wiki/PAD().md +110 -0
  127. package/resources/programming/basic_wiki/PAINT.md +66 -0
  128. package/resources/programming/basic_wiki/PDL().md +53 -0
  129. package/resources/programming/basic_wiki/PEEK().md +44 -0
  130. package/resources/programming/basic_wiki/PLAY().md +58 -0
  131. package/resources/programming/basic_wiki/PLAY.md +196 -0
  132. package/resources/programming/basic_wiki/POINT.md +52 -0
  133. package/resources/programming/basic_wiki/POKE.md +51 -0
  134. package/resources/programming/basic_wiki/POS().md +36 -0
  135. package/resources/programming/basic_wiki/PRESET.md +61 -0
  136. package/resources/programming/basic_wiki/PRINT.md +179 -0
  137. package/resources/programming/basic_wiki/PSET.md +82 -0
  138. package/resources/programming/basic_wiki/PUT_KANJI.md +93 -0
  139. package/resources/programming/basic_wiki/PUT_SPRITE.md +143 -0
  140. package/resources/programming/basic_wiki/READ.md +45 -0
  141. package/resources/programming/basic_wiki/REM.md +42 -0
  142. package/resources/programming/basic_wiki/RENUM.md +78 -0
  143. package/resources/programming/basic_wiki/RESTORE.md +52 -0
  144. package/resources/programming/basic_wiki/RESUME.md +45 -0
  145. package/resources/programming/basic_wiki/RETURN.md +47 -0
  146. package/resources/programming/basic_wiki/RIGHT$().md +39 -0
  147. package/resources/programming/basic_wiki/RND().md +51 -0
  148. package/resources/programming/basic_wiki/RUN.md +56 -0
  149. package/resources/programming/basic_wiki/SAVE.md +65 -0
  150. package/resources/programming/basic_wiki/SCREEN.md +164 -0
  151. package/resources/programming/basic_wiki/SET_ADJUST.md +66 -0
  152. package/resources/programming/basic_wiki/SET_BEEP.md +76 -0
  153. package/resources/programming/basic_wiki/SET_DATE.md +103 -0
  154. package/resources/programming/basic_wiki/SET_PAGE.md +52 -0
  155. package/resources/programming/basic_wiki/SET_PASSWORD.md +75 -0
  156. package/resources/programming/basic_wiki/SET_PROMPT.md +61 -0
  157. package/resources/programming/basic_wiki/SET_SCREEN.md +100 -0
  158. package/resources/programming/basic_wiki/SET_SCROLL.md +55 -0
  159. package/resources/programming/basic_wiki/SET_TIME.md +83 -0
  160. package/resources/programming/basic_wiki/SET_TITLE.md +87 -0
  161. package/resources/programming/basic_wiki/SET_VIDEO.md +49 -0
  162. package/resources/programming/basic_wiki/SGN().md +38 -0
  163. package/resources/programming/basic_wiki/SIN().md +36 -0
  164. package/resources/programming/basic_wiki/SOUND.md +188 -0
  165. package/resources/programming/basic_wiki/SPACE$().md +38 -0
  166. package/resources/programming/basic_wiki/SPC().md +34 -0
  167. package/resources/programming/basic_wiki/SPRITE$().md +50 -0
  168. package/resources/programming/basic_wiki/SPRITE.md +31 -0
  169. package/resources/programming/basic_wiki/SQR().md +32 -0
  170. package/resources/programming/basic_wiki/STICK().md +70 -0
  171. package/resources/programming/basic_wiki/STOP.md +70 -0
  172. package/resources/programming/basic_wiki/STR$().md +37 -0
  173. package/resources/programming/basic_wiki/STRIG().md +82 -0
  174. package/resources/programming/basic_wiki/STRING$().md +42 -0
  175. package/resources/programming/basic_wiki/SWAP.md +62 -0
  176. package/resources/programming/basic_wiki/TAB().md +38 -0
  177. package/resources/programming/basic_wiki/TAN().md +36 -0
  178. package/resources/programming/basic_wiki/TIME.md +59 -0
  179. package/resources/programming/basic_wiki/TROFF.md +21 -0
  180. package/resources/programming/basic_wiki/TRON.md +39 -0
  181. package/resources/programming/basic_wiki/USR().md +66 -0
  182. package/resources/programming/basic_wiki/VAL().md +36 -0
  183. package/resources/programming/basic_wiki/VARPTR().md +50 -0
  184. package/resources/programming/basic_wiki/VDP().md +103 -0
  185. package/resources/programming/basic_wiki/VPEEK().md +46 -0
  186. package/resources/programming/basic_wiki/VPOKE.md +48 -0
  187. package/resources/programming/basic_wiki/WAIT.md +38 -0
  188. package/resources/programming/basic_wiki/WIDTH.md +76 -0
  189. package/resources/programming/basic_wiki/XOR.md +72 -0
  190. package/resources/programming/basic_wiki/_toc.json +871 -0
  191. package/resources/sdcc/sdccman.lyx +81574 -0
  192. package/resources/sdcc/toc.json +77 -0
  193. package/vector-db/index.json +1 -0
  194. /package/resources/msx-unapi/{Ethernet_UNAPI_specification_1.1.md → Ethernet_UNAPI_specification_1_1.md} +0 -0
  195. /package/resources/msx-unapi/{MSX_UNAPI_specification_1.1.md → MSX_UNAPI_specification_1_1.md} +0 -0
  196. /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
+ [![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
+
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
- - **Dual Transport**: Support for both stdio and HTTP transports.
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 two books:
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
- - [MSX Wiki](https://www.msx.org/wiki/Main_Page)
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). You may need to have `nodejs` installed for this to work.
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
- ### 🟢 Basic Usage with VSCode
129
+ [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP_Server-0098FF?style=flat&logo=visualstudiocode&logoColor=ffffff)](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
- * Add to your workspace a file `.vscode/mcp.json` with:
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": "/usr/share/openmsx"
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
- Add to your `claude_desktop_config.json`:
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": "/usr/share/openmsx"
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
- ### Prerequisites
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
- // Version info for CLI
25
- const PACKAGE_VERSION = "1.1.14";
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
- const tocContent = JSON.parse(await fs.readFile(tocFile, 'utf8'));
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()).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);
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
- catch (error) {
894
- // Throw exception (MCP protocol requirement)
895
- throw error;
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
- 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
- });
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
- const instruction = variables.instruction.replace(/ /g, '_').replace(/\?/g, '%3F').replace(/=/g, '%3D');
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
- [resourceContent, mimeType] = await fetchCleanWebpage(url);
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
- // Handle GET requests for server-to-client notifications via SSE
1075
- app.get('/mcp', async (req, res) => {
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
- 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)
@@ -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
+ }
@@ -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
+ }