@iobroker/mcp-server 1.7.1 → 1.8.0
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 +38 -4
- package/build/inProcessClient.d.ts +1 -1
- package/build/inProcessClient.js +1 -0
- package/build/inProcessClient.js.map +1 -1
- package/build/index.d.ts +72 -2
- package/build/index.js +447 -19
- package/build/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,6 +157,27 @@ All options that change the system are **off** by default; turn on only what the
|
|
|
157
157
|
`.tgz` on a test system. An installed package runs with full rights on the host, so never enable it on a
|
|
158
158
|
productive system or for clients you do not fully trust.
|
|
159
159
|
|
|
160
|
+
**Rights of the ioBroker user**
|
|
161
|
+
The options above decide which tools exist; what a call may do is decided by the rights of the ioBroker user the
|
|
162
|
+
MCP session runs as (the logged-in user, or the Default User without authentication), like in ioBroker.admin:
|
|
163
|
+
- the ACLs of objects, states and files for reading and writing them,
|
|
164
|
+
- the "other" rights of the user's groups: **execute** for commands on a host (`install_package`, `add_instance`,
|
|
165
|
+
`delete_instance`, `upgrade_adapter`) and **sendto** for messages to adapters (`send_to`, `run_script`).
|
|
166
|
+
Without them the call is refused before anything runs. Administrators have all rights.
|
|
167
|
+
|
|
168
|
+
A session is not even offered the tools that need a right the groups of its user lack: without the write right for
|
|
169
|
+
states no `set_state`/`set_states`, without the write right for objects no `set_object`, `create_state`,
|
|
170
|
+
`start_instance`, ..., without the write right for files no `write_file`, without **execute** no `install_package`,
|
|
171
|
+
without **sendto** no `send_to`. The rights are read when the session starts; every call is checked anyway.
|
|
172
|
+
|
|
173
|
+
`check_permissions` shows these rights and the ACLs of given objects.
|
|
174
|
+
|
|
175
|
+
**Log**
|
|
176
|
+
Every call of a changing tool is logged at info level with the user, the tool and its arguments
|
|
177
|
+
(`MCP system.user.admin: set_state(id="hue.0.light.state", value=true)`), and every failed call as a warning with
|
|
178
|
+
the same details. Long values are shortened; file contents, script code and `send_to` payloads are replaced by
|
|
179
|
+
their size.
|
|
180
|
+
|
|
160
181
|
### SSL/TLS Configuration
|
|
161
182
|
- **Enable HTTPS**: Enable HTTPS/SSL for secure connections
|
|
162
183
|
- **Public Certificate**: Path to the public certificate file
|
|
@@ -178,6 +199,8 @@ Point your MCP client at:
|
|
|
178
199
|
| Tool | Description |
|
|
179
200
|
|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
180
201
|
| `get_states` | Retrieve the current value of one or multiple states; IDs may contain wildcards (e.g. `hue.0.*.brightness`); an ID without a state is returned with `exists: false` or a hint |
|
|
202
|
+
| `wait_for_state` | Wait until a state has a `value` (or, without `value`, until it is updated), optionally only with `ack`; gives up after `timeout` seconds (default 30, at most 50) with `reached: false` |
|
|
203
|
+
| `find_orphan_states` | Find states without object (e.g. `system.adapter.vis.0.alive` of a deleted instance) by an ID `pattern`; delete them on the host with `iobroker state del <id>` |
|
|
181
204
|
| `get_object` | Read a single object by its ID |
|
|
182
205
|
| `check_permissions` | Show the user and groups of the MCP session, the rights of its groups and, for given object IDs, whether it may read/write/delete the object and read/write the state |
|
|
183
206
|
| `search_objects` | Search objects/states by keyword (matched against ID and name); optional filters for object `type`, `role`, `room` and source `adapter` instance |
|
|
@@ -192,7 +215,7 @@ Point your MCP client at:
|
|
|
192
215
|
| `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 |
|
|
193
216
|
| `list_files` | List a directory in an adapter file storage |
|
|
194
217
|
| `file_exists` | Check whether a file exists in an adapter file storage |
|
|
195
|
-
| `get_logs` | Retrieve recent ioBroker log lines; optional filters by `level` (error/warn/info/debug), source `adapter
|
|
218
|
+
| `get_logs` | Retrieve recent ioBroker log lines; optional filters by `level` (error/warn/info/debug), source `adapter`, start time (`from_ts`) and `search` in the message (text, or `/regex/flags`); `logFile` tells the time of the newest line and the file size |
|
|
196
219
|
| `write_log` | Write a message to the ioBroker log |
|
|
197
220
|
| `system_info` | Get system and js-controller information |
|
|
198
221
|
| `list_adapter_updates` | List installed adapters that have a newer version in the active repository |
|
|
@@ -200,7 +223,7 @@ Point your MCP client at:
|
|
|
200
223
|
| `list_scripts` | List the scripts of the javascript adapter (without source) |
|
|
201
224
|
| `get_script` | Read a script including its source |
|
|
202
225
|
| `ping_host` | Diagnose connectivity to a network device: ICMP ping to `host` plus an optional TCP connect to `port` — useful to investigate adapter `ETIMEDOUT`/connection errors |
|
|
203
|
-
| `set_state` | Set the value of a state (value coerced to the state type) — requires *Allow setting states*
|
|
226
|
+
| `set_state` | Set the value of a state (value coerced to the state type); with `wait_ack` (seconds) it waits until the device confirms the command (`ack: true`) and returns the confirmed state — requires *Allow setting states* |
|
|
204
227
|
| `set_states` | Set multiple states in one call (for scenes/group actions like "all lights off") — requires *Allow setting states* |
|
|
205
228
|
| `clear_notifications` | Acknowledge system notifications, optionally of one scope/category/instance — requires *Allow setting states* |
|
|
206
229
|
| `set_object` | Create/update an object (merges common/native) — requires *Allow object/file changes* |
|
|
@@ -227,8 +250,9 @@ Point your MCP client at:
|
|
|
227
250
|
| `run_script` | Run code once in the javascript adapter and return its log output (javascript ≥ 10.1.0) — requires *Allow changing scripts* |
|
|
228
251
|
| `send_to` | Send a message to an adapter instance (`sendTo`) and return the answer — only instances in *Allow messages to adapters* |
|
|
229
252
|
|
|
230
|
-
All object/state access runs with the permissions of the
|
|
231
|
-
registered when their respective permission option is
|
|
253
|
+
All object/state access runs with the permissions of the user of the session (the logged-in user or the
|
|
254
|
+
configured **Default User**). The write tools are only registered when their respective permission option is
|
|
255
|
+
enabled, and only offered to users whose groups have the rights they need.
|
|
232
256
|
|
|
233
257
|
Every tool declares a JSON input schema and all four MCP behaviour hints (`readOnlyHint`, `destructiveHint`,
|
|
234
258
|
`idempotentHint`, `openWorldHint`), so a host can tell what a tool does before calling it and can ask the user
|
|
@@ -311,6 +335,16 @@ client calls it and its result is verified:
|
|
|
311
335
|
Placeholder for the next version (at the beginning of the line):
|
|
312
336
|
### **WORK IN PROGRESS**
|
|
313
337
|
-->
|
|
338
|
+
### 1.8.0 (2026-10-26)
|
|
339
|
+
* (@GermanBluefox) Security: commands on a host (`install_package`, `add_instance`, `delete_instance`, `upgrade_adapter`) require the "execute" right of the user, messages to adapters (`send_to`, `run_script`) the "sendto" right, like in admin. Before, they ran with the rights of the adapter
|
|
340
|
+
* (@GermanBluefox) `install_package` reports a done installation even if the instances could not be restarted afterwards (`restartError`), instead of failing
|
|
341
|
+
* (@GermanBluefox) Every call of a changing tool is logged with the user, the tool and its arguments; a failed call names the tool and its arguments
|
|
342
|
+
* (@GermanBluefox) `get_logs` searches the messages for a text or a regular expression (`search`)
|
|
343
|
+
* (@GermanBluefox) A session is only offered the tools its user has the rights for, e.g. no `install_package` without "execute"
|
|
344
|
+
* (@GermanBluefox) `set_state` waits for the confirmation of the device with `wait_ack` and returns the confirmed state
|
|
345
|
+
* (@GermanBluefox) Added the `wait_for_state` tool: waits until a state has a value or is updated
|
|
346
|
+
* (@GermanBluefox) Added the `find_orphan_states` tool: finds states without object, e.g. of deleted instances
|
|
347
|
+
|
|
314
348
|
### 1.7.1 (2026-10-26)
|
|
315
349
|
* (@GermanBluefox) Fixed: `get_logs` always returned `size: null` for the log file, because the host sends the size as a string
|
|
316
350
|
|
|
@@ -79,7 +79,7 @@ export interface InProcessMcpOptions {
|
|
|
79
79
|
}
|
|
80
80
|
/** A ready-to-use in-process MCP connection: list tools, call them, and close everything. */
|
|
81
81
|
export interface InProcessMcp {
|
|
82
|
-
/** List the available tools (already filtered by the configured permission toggles). */
|
|
82
|
+
/** List the available tools (already filtered by the configured permission toggles and the user rights). */
|
|
83
83
|
listTools(): Promise<InProcessToolInfo[]>;
|
|
84
84
|
/** Call a tool by name with the given arguments; returns its text result. */
|
|
85
85
|
callTool(name: string, args?: Record<string, unknown>): Promise<InProcessToolResult>;
|
package/build/inProcessClient.js
CHANGED
|
@@ -26,6 +26,7 @@ async function createInProcessMcp(options) {
|
|
|
26
26
|
const { clientName, clientVersion, ...embedOptions } = options;
|
|
27
27
|
const mcp = index_1.default.createEmbedded(embedOptions);
|
|
28
28
|
const server = mcp.createInProcessServer();
|
|
29
|
+
await mcp.hideToolsWithoutRights(server);
|
|
29
30
|
const [clientTransport, serverTransport] = inMemory_js_1.InMemoryTransport.createLinkedPair();
|
|
30
31
|
await server.connect(serverTransport);
|
|
31
32
|
const client = new index_js_1.Client({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inProcessClient.js","sourceRoot":"","sources":["../src/inProcessClient.ts"],"names":[],"mappings":";;;;;AAsHA,
|
|
1
|
+
{"version":3,"file":"inProcessClient.js","sourceRoot":"","sources":["../src/inProcessClient.ts"],"names":[],"mappings":";;;;;AAsHA,gDAoDC;AA1KD;;;;;;;;GAQG;AACH,wEAAmE;AACnE,uEAA0E;AAC1E,oDAAgC;AAqGhC;;;;;GAKG;AACI,KAAK,UAAU,kBAAkB,CAAC,OAA4B;IACjE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC;IAC/D,MAAM,GAAG,GAAG,eAAS,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IAEnD,MAAM,MAAM,GAAG,GAAG,CAAC,qBAAqB,EAAE,CAAC;IAC3C,MAAM,GAAG,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,CAAC,eAAe,EAAE,eAAe,CAAC,GAAG,+BAAiB,CAAC,gBAAgB,EAAE,CAAC;IAChF,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAEtC,MAAM,MAAM,GAAG,IAAI,iBAAM,CACrB;QACI,IAAI,EAAE,UAAU,IAAI,2BAA2B;QAC/C,OAAO,EAAE,aAAa,IAAI,OAAO;KACpC,EACD,EAAE,YAAY,EAAE,EAAE,EAAE,CACvB,CAAC;IACF,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAEtC,OAAO;QACH,KAAK,CAAC,SAAS;YACX,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBAClC,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,WAAW,EAAG,IAAI,CAAC,WAAuC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;gBAChG,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,WAAW,EAAE,IAAI,CAAC,WAAW;aAChC,CAAC,CAAC,CAAC;QACR,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,IAA8B;YACvD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;YACnE,MAAM,MAAM,GAAI,GAAG,CAAC,OAA0C,IAAI,EAAE,CAAC;YACrE,MAAM,IAAI,GAAG,MAAM;iBACd,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;iBACxE,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAc,CAAC;iBAClC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QAC5C,CAAC;QACD,KAAK,CAAC,KAAK;YACP,IAAI,CAAC;gBACD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACL,SAAS;YACb,CAAC;YACD,IAAI,CAAC;gBACD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACL,SAAS;YACb,CAAC;YACD,GAAG,CAAC,MAAM,EAAE,CAAC;QACjB,CAAC;KACJ,CAAC;AACN,CAAC"}
|
package/build/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export default class McpServer {
|
|
|
14
14
|
private readonly subscriptionCounts;
|
|
15
15
|
/** Recent log lines kept for `ioblog://` resource reads (filled while there are log subscribers). */
|
|
16
16
|
private readonly logBuffer;
|
|
17
|
+
/** The tools of every server created by {@link createServer}, for {@link hideToolsWithoutRights}. */
|
|
18
|
+
private readonly registeredTools;
|
|
17
19
|
private config;
|
|
18
20
|
private readonly extension;
|
|
19
21
|
private readonly routerPrefix;
|
|
@@ -68,9 +70,21 @@ export default class McpServer {
|
|
|
68
70
|
* Build a fresh MCP SDK server with all ioBroker tools registered, ready to be connected to an
|
|
69
71
|
* arbitrary transport. Used for in-process embedding (e.g. by ioBroker.admin) over an
|
|
70
72
|
* InMemoryTransport, where there is no HTTP server/session layer. The caller owns the lifecycle
|
|
71
|
-
* of the returned server (connect it to a transport, and `close()` it when done).
|
|
73
|
+
* of the returned server (connect it to a transport, and `close()` it when done). Call
|
|
74
|
+
* {@link hideToolsWithoutRights} before connecting it to offer only the tools the user may use.
|
|
72
75
|
*/
|
|
73
76
|
createInProcessServer(): McpSdkServer;
|
|
77
|
+
/**
|
|
78
|
+
* Do not offer the tools the user cannot use, because its groups lack a general right the tool needs (e.g.
|
|
79
|
+
* `install_package` without "execute"), so the client does not try them in vain. Administrators get all
|
|
80
|
+
* tools. The rights are read once, before the server is connected: changed rights apply to new sessions,
|
|
81
|
+
* and every call is checked anyway.
|
|
82
|
+
*
|
|
83
|
+
* @param server a server returned by {@link createInProcessServer}
|
|
84
|
+
* @param user the ioBroker user of the session (default: the configured default user)
|
|
85
|
+
* @returns the names of the hidden tools
|
|
86
|
+
*/
|
|
87
|
+
hideToolsWithoutRights(server: McpSdkServer, user?: `system.user.${string}`): Promise<string[]>;
|
|
74
88
|
/**
|
|
75
89
|
* Convenience factory for embedding the MCP server inside another adapter's process (no HTTP).
|
|
76
90
|
* The returned instance is not wired to any Express app; obtain a tool server via
|
|
@@ -212,6 +226,22 @@ export default class McpServer {
|
|
|
212
226
|
private refUnsubscribe;
|
|
213
227
|
/** Release all subscriptions held by a session (on close). */
|
|
214
228
|
private cleanupSession;
|
|
229
|
+
/**
|
|
230
|
+
* Remember every tool registered on `server` afterwards (for {@link hideToolsWithoutRights}) and log its
|
|
231
|
+
* calls: a failed call as a warning with the tool and its arguments, a successful call of a writing tool
|
|
232
|
+
* (not `readOnlyHint`) at info level, so the log shows who changed what.
|
|
233
|
+
*
|
|
234
|
+
* @param server the server whose `registerTool` is wrapped
|
|
235
|
+
*/
|
|
236
|
+
private trackTools;
|
|
237
|
+
/**
|
|
238
|
+
* The arguments of a tool call for the log: long values are shortened, and contents that may hold secrets
|
|
239
|
+
* or large data (file contents, script code, message payloads) are replaced by their size.
|
|
240
|
+
*
|
|
241
|
+
* @param tool name of the tool
|
|
242
|
+
* @param args arguments of the call
|
|
243
|
+
*/
|
|
244
|
+
private static describeArgs;
|
|
215
245
|
/**
|
|
216
246
|
* Create a new MCP SDK server with all ioBroker tools registered.
|
|
217
247
|
*
|
|
@@ -230,6 +260,16 @@ export default class McpServer {
|
|
|
230
260
|
* written. Other failures of single states are reported per item and do not abort the rest.
|
|
231
261
|
*/
|
|
232
262
|
private setStates;
|
|
263
|
+
/**
|
|
264
|
+
* Verify a right of the "other" category of the user, like admin does for `cmdExec` (`execute`) and
|
|
265
|
+
* `sendTo` (`sendto`). js-controller does not check them for messages, so without this an MCP user
|
|
266
|
+
* could run what the ioBroker user may not.
|
|
267
|
+
*
|
|
268
|
+
* @param operation `execute` for CLI commands on a host, `sendto` for messages to adapters
|
|
269
|
+
* @param action what was denied, for the error message (e.g. `run "iobroker url ..." on host "x"`)
|
|
270
|
+
* @throws {Error} a permissionError before anything was done
|
|
271
|
+
*/
|
|
272
|
+
private assertOtherPermission;
|
|
233
273
|
/**
|
|
234
274
|
* Verify that the user of the request may write every given state, mirroring the checks js-controller runs in
|
|
235
275
|
* `setForeignState`: the user must be able to read the object (needed for the type coercion in
|
|
@@ -327,6 +367,31 @@ export default class McpServer {
|
|
|
327
367
|
private readEnums;
|
|
328
368
|
/** Write a state, coercing the value to the state's declared type (boolean/number/string). */
|
|
329
369
|
private setState;
|
|
370
|
+
/**
|
|
371
|
+
* Read a state until `reached` accepts it, the time is up or the client cancels the call.
|
|
372
|
+
*
|
|
373
|
+
* @param id state ID
|
|
374
|
+
* @param reached the condition
|
|
375
|
+
* @param timeoutSeconds how long to wait at most
|
|
376
|
+
* @param signal aborted when the client cancels the call
|
|
377
|
+
* @returns whether the condition was met, the last read state and how long it took (ms)
|
|
378
|
+
*/
|
|
379
|
+
private waitForState;
|
|
380
|
+
/**
|
|
381
|
+
* Whether a state value is the expected one, also if one of them is stored as a string ("21.5" and 21.5,
|
|
382
|
+
* '{"a":1}' and {a: 1}).
|
|
383
|
+
*/
|
|
384
|
+
private static sameValue;
|
|
385
|
+
/**
|
|
386
|
+
* The states without object. For a pattern, js-controller returns only the states of existing state objects,
|
|
387
|
+
* except in its maintenance mode (only for the admin user), which reads the keys of the states database. The
|
|
388
|
+
* objects are read with the rights of the adapter, as an object the user may not see is still an object. A
|
|
389
|
+
* state without object has no ACL, so js-controller lets every user read it anyway.
|
|
390
|
+
*
|
|
391
|
+
* @param pattern IDs of the states to check, wildcards allowed
|
|
392
|
+
* @returns how many states were checked, and the states without object
|
|
393
|
+
*/
|
|
394
|
+
private findOrphanStates;
|
|
330
395
|
/** Coerce an arbitrary value to the given ioBroker state type. */
|
|
331
396
|
private static coerceValue;
|
|
332
397
|
/**
|
|
@@ -384,7 +449,12 @@ export default class McpServer {
|
|
|
384
449
|
private runHostCommand;
|
|
385
450
|
/** Request something from a host (`getNotifications`, ...) and wait for the answer. */
|
|
386
451
|
private hostRequest;
|
|
387
|
-
/**
|
|
452
|
+
/**
|
|
453
|
+
* Like admin after an installation: touching the instance objects makes the controller restart them.
|
|
454
|
+
*
|
|
455
|
+
* @param adapterName adapter whose enabled instances are restarted
|
|
456
|
+
* @param restarted receives the ids of the restarted instances, also those before an error
|
|
457
|
+
*/
|
|
388
458
|
private restartAdapterInstances;
|
|
389
459
|
/** Read an instance object by its short id (`hue.0`) or full id (`system.adapter.hue.0`). */
|
|
390
460
|
private getInstance;
|