@iobroker/mcp-server 1.4.0 → 1.5.1

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
@@ -143,6 +143,8 @@ All options that change the system are **off** by default; turn on only what the
143
143
  object or instance is deleted, an instance stopped, an adapter upgraded or installed, a script saved or run, or a
144
144
  message sent. Clients without elicitation rely on the `destructiveHint` of the tools and their own confirmation.
145
145
  Default: **on**.
146
+ - **Allow download/upload links** (`transferLinks`): Tools may hand out short-lived links for data that is too large
147
+ for an MCP message, see [Large results and files](#large-results-and-files). Default: **on**.
146
148
 
147
149
  **Developers**
148
150
  - **Allow installing packages (developers only)** (`allowPackageInstall`): Offer the `install_package` tool, which
@@ -183,7 +185,7 @@ Point your MCP client at:
183
185
  | `list_rooms` | List rooms (`enum.rooms.*`) with localized names and member details; optional `language` and `withIcons` |
184
186
  | `list_functions` | List functions (`enum.functions.*`) with localized names and member details; optional `language` and `withIcons` |
185
187
  | `history_query` | Query historical values (requires a history adapter); aggregations: `raw`, `min`, `max`, `avg`, `sum`, `count`, `minmax`, `percentile`, `quantile`, `integral` |
186
- | `read_file` | Read a file from an adapter file storage (optional base64); large files are read in chunks via `offset`/`length` (default 512 KiB per call) — the result carries `size`, `truncated` and `nextOffset` |
188
+ | `read_file` | Read a file from an adapter file storage (optional base64); large files are read in chunks via `offset`/`length` (default 512 KiB per call) — the result carries `size`, `truncated` and `nextOffset`; `download: true` returns a download link instead |
187
189
  | `list_files` | List a directory in an adapter file storage |
188
190
  | `file_exists` | Check whether a file exists in an adapter file storage |
189
191
  | `get_logs` | Retrieve recent ioBroker log lines; optional filters by `level` (error/warn/info/debug), source `adapter` and start time (`from_ts`) |
@@ -202,11 +204,12 @@ Point your MCP client at:
202
204
  | `create_state` | Create a new state object with type/role/unit/min/max and optional initial value — requires *Allow object/file changes* |
203
205
  | `create_scene` | Create or update a scene for the ioBroker `scenes` adapter (state/value pairs applied together) — requires *Allow object/file changes* |
204
206
  | `create_alias` | Create an alias (`alias.0.*`) pointing to a state, optionally with a separate write state — requires *Allow object/file changes* |
205
- | `write_file` | Write a file to an adapter file storage — requires *Allow object/file changes* |
207
+ | `write_file` | Write a file to an adapter file storage, from `content` or from a file uploaded via `prepare_upload` (`upload_id`) — requires *Allow object/file changes* |
206
208
  | `delete_file` | Delete a file from an adapter file storage — requires *Allow object/file changes* |
207
209
  | `rename_file` | Rename/move a file within the same adapter file storage — requires *Allow object/file changes* |
208
210
  | `mkdir` | Create a directory in an adapter file storage — requires *Allow object/file changes* |
209
- | `install_package` | Install an adapter package (npm, GitHub, URL, `.tgz` from the file storage or as base64) on a host and restart its enabled instances — requires *Allow installing packages*, developers only |
211
+ | `prepare_upload` | Get a one-time upload link for a file that is too large for a tool argument (e.g. a `.tgz`), for clients that can run shell commands — requires *Allow download/upload links* and *Allow object/file changes* or *Allow installing packages* |
212
+ | `install_package` | Install an adapter package (npm, GitHub, URL, `.tgz` from the file storage, as base64 or uploaded via `prepare_upload`) on a host and restart its enabled instances — requires *Allow installing packages*, developers only |
210
213
  | `start_instance` | Start (enable) an instance — requires *Allow controlling instances* |
211
214
  | `stop_instance` | Stop (disable) an instance — requires *Allow controlling instances* |
212
215
  | `restart_instance` | Restart a running instance — requires *Allow controlling instances* |
@@ -228,6 +231,25 @@ Every tool declares a JSON input schema and all four MCP behaviour hints (`readO
228
231
  for confirmation before a writing tool runs. All reading tools are `readOnlyHint: true`; `ping_host` is the only
229
232
  tool with `openWorldHint: true`, because it talks to hosts outside of ioBroker.
230
233
 
234
+ ### Large results and files
235
+ MCP clients reject tool results above 1 MiB, and a model cannot pass megabytes of base64 as a tool argument. So:
236
+
237
+ - **Every result stays below the limit.** Lists (`get_states`, `search_objects`, `list_files`, `list_scripts`,
238
+ `history_query`) return as many entries as fit and add `truncated`, `total`, `nextOffset` and a `hint`; call
239
+ again with `offset` set to `nextOffset` for the next part. `get_logs` keeps the newest lines. Any other result
240
+ that is too large is replaced by a download link, or by an error asking to narrow the request down.
241
+ - **Download links** (`download_url`): a cut list carries a link to all of its entries as JSON, and `read_file`
242
+ with `download: true` returns a link to the whole file. Clients that can run shell commands (e.g. Claude Code)
243
+ fetch it with `curl -o <file> "<download_url>"`.
244
+ - **Upload links**: `prepare_upload` returns a link and an `upload_id`. The client uploads the file with
245
+ `curl -T <file> "<url>"` (`PUT` or `POST`, raw body, up to 100 MB) and passes the `upload_id` to `write_file` or
246
+ `install_package`. A link can be used once.
247
+
248
+ The links are `<mcp endpoint>/transfer/<token>`: the random token is the credential, so they work without the
249
+ login of the MCP endpoint, and they expire after 15 minutes. They are built from the address the client used, or
250
+ from *Public URL*. They are not available in-process (ioBroker.admin) and as a web extension with authentication
251
+ enabled in the web adapter, because the web adapter asks for a login first; the tools then say so.
252
+
231
253
  ### Prompts
232
254
  MCP clients offer these as ready-made commands (e.g. as slash commands):
233
255
 
@@ -285,6 +307,13 @@ client calls it and its result is verified:
285
307
  Placeholder for the next version (at the beginning of the line):
286
308
  ### **WORK IN PROGRESS**
287
309
  -->
310
+ ### 1.5.1 (2026-10-24)
311
+ * (@GermanBluefox) Transfer folders left behind by a killed process are removed at startup
312
+
313
+ ### 1.5.0 (2026-10-24)
314
+ * (@GermanBluefox) Tool results are kept below the 1 MiB limit of MCP clients: lists are paged with `offset`, other results are replaced by a download link
315
+ * (@GermanBluefox) Added download links (`read_file` with `download: true`, complete lists) and upload links (`prepare_upload`, used by `write_file` and `install_package`) for large data (option `transferLinks`)
316
+
288
317
  ### 1.4.0 (2026-10-23)
289
318
  * (@GermanBluefox) Added tools to control instances, manage adapters, read/change/run scripts, read and acknowledge notifications, create aliases and send messages to adapters, each behind its own option (off by default)
290
319
  * (@GermanBluefox) Added the prompts `diagnose_adapter`, `room_overview` and `create_automation`
package/build/index.d.ts CHANGED
@@ -41,6 +41,20 @@ export default class McpServer {
41
41
  private oauth2?;
42
42
  /** Installations started by `install_package`, keyed by the `cmdExec` session id. */
43
43
  private readonly cmdSessions;
44
+ /** Path of the MCP endpoint, e.g. `/mcp`. */
45
+ private readonly mcpPath;
46
+ /**
47
+ * Whether tools may hand out download/upload links (`<mcp>/transfer/<token>`). Needs our own HTTP
48
+ * routes; not as a web extension with authentication, where the web adapter asks for a login first.
49
+ */
50
+ private readonly linksAvailable;
51
+ /** Why links are not available, for the answer of the tools. */
52
+ private readonly linksUnavailableReason;
53
+ /** Active download and upload links, keyed by their token. */
54
+ private readonly transfers;
55
+ /** Temporary folder for the files of the links, created on first use. */
56
+ private transferDir?;
57
+ private transferTimer?;
44
58
  constructor(server: HttpServer | HttpsServer | null, webSettings: {
45
59
  secure?: boolean;
46
60
  port?: number | string;
@@ -175,6 +189,7 @@ export default class McpServer {
175
189
  * Create a new MCP SDK server with all ioBroker tools registered.
176
190
  *
177
191
  * @param subscriptions the per-session set of subscribed state ids (mutated by subscribe/unsubscribe)
192
+ * @param baseUrl public address of this server, for download/upload links (none in-process)
178
193
  */
179
194
  private createServer;
180
195
  /** Build the result entry for one state. */
@@ -375,6 +390,31 @@ export default class McpServer {
375
390
  * from the target unless given.
376
391
  */
377
392
  private createAlias;
393
+ /**
394
+ * Create a download link for `data`, or an upload link when `data` is undefined, and return its
395
+ * token. The file lives in a temporary folder until the link expires (or the upload is used).
396
+ */
397
+ private createTransfer;
398
+ /**
399
+ * Remove the transfer folders that a process left behind when it was killed. Other MCP servers on this
400
+ * host (web extension, standalone, admin) use the same temp folder, so only folders unchanged for longer
401
+ * than a link lives are removed: every new link creates a subfolder and so touches its folder.
402
+ */
403
+ private removeStaleTransferDirs;
404
+ /** The public URL of a download or upload link. */
405
+ private transferUrl;
406
+ /** Remove a link and its file. */
407
+ private removeTransfer;
408
+ /** Remove the expired links. */
409
+ private cleanupTransfers;
410
+ /** `GET <mcp>/transfer/<token>`: send the file of a download link. */
411
+ private handleDownload;
412
+ /** `PUT`/`POST <mcp>/transfer/<token>`: receive the file of an upload link (raw body, e.g. `curl -T`). */
413
+ private handleUpload;
414
+ /** Write the raw body of a request to a file and return its size. */
415
+ private static receiveBody;
416
+ /** Get a received upload by its id, failing if it is unknown, expired or still empty. */
417
+ private getUpload;
378
418
  /** Resolve the member ids of a room/function enum matched by id or (localized) name. */
379
419
  private getEnumMembers;
380
420
  /** Normalize an ioBroker name (string or {en, de, ...}) to a plain string. */