@dinglebear/unraid 0.2.5 → 0.6.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 +46 -15
- package/lib/platform.js +1 -1
- package/package.json +2 -2
- package/scripts/install.js +18 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Rust MCP server and CLI for Unraid GraphQL operations across NAS, Docker, VM, and storage workflows.
|
|
4
4
|
|
|
5
|
-
It
|
|
5
|
+
It supports legacy and atomic MCP projections plus the `runraid` CLI. Legacy mode exposes the single `unraid` tool; atomic mode exposes one focused `unraid_<action>` tool per enabled action. Agents can inspect
|
|
6
6
|
array health, disks, Docker containers and logs, VMs, shares, notifications,
|
|
7
7
|
system metrics, UPS, logs, network settings, plugins, parity history, rclone,
|
|
8
8
|
remote access, and Unraid Connect through stdio MCP, Streamable HTTP MCP, or
|
|
@@ -57,7 +57,7 @@ through MCP tool arguments.
|
|
|
57
57
|
| crates.io package | `unraid-rmcp` |
|
|
58
58
|
| CRGX command | `crgx unraid-rmcp -- <runraid args>` |
|
|
59
59
|
| Legacy npm package | `unraid-rmcp` |
|
|
60
|
-
| MCP
|
|
60
|
+
| MCP tools | `unraid` (legacy), `unraid_<action>` (atomic) |
|
|
61
61
|
| Config home | `~/.unraid` on hosts, `/data` in containers |
|
|
62
62
|
| Env prefixes | `UNRAID_*`, `UNRAID_RMCP_*` |
|
|
63
63
|
|
|
@@ -120,7 +120,7 @@ cargo build --release
|
|
|
120
120
|
./target/release/runraid --help
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
-
Minimum supported Rust version: 1.
|
|
123
|
+
Minimum supported Rust version: 1.97.1 (edition 2024).
|
|
124
124
|
|
|
125
125
|
## Quickstart
|
|
126
126
|
|
|
@@ -242,8 +242,13 @@ as action arguments.
|
|
|
242
242
|
|
|
243
243
|
## MCP Tool Reference
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
MCP projection is configurable with `UNRAID_RMCP_PROJECTION` (or `mcp.projection`):
|
|
246
|
+
|
|
247
|
+
- `legacy` (default) exposes the compatibility tool `unraid`; pass its required `action` argument.
|
|
248
|
+
- `atomic` exposes one focused `unraid_<action>` tool for each enabled action. Atomic schemas contain only that action's parameters and mark dispatcher-required fields as required.
|
|
249
|
+
- `both` exposes the legacy tool plus all enabled atomic tools for migration. Generated prompts prefer atomic calls in this mode.
|
|
250
|
+
|
|
251
|
+
Selectors are applied to canonical actions before projection, so the same `[mcp.tools]` policy controls all three modes. Calls from either surface normalize to the same canonical action before scope checks, destructive confirmation, and dispatch.
|
|
247
252
|
|
|
248
253
|
### Core Actions
|
|
249
254
|
|
|
@@ -289,7 +294,7 @@ the operation.
|
|
|
289
294
|
| `connect` | Unraid Connect dynamic remote access state. |
|
|
290
295
|
| `plugins` | Installed community plugins with versions. |
|
|
291
296
|
| `status` | Server observability: version, PID, uptime, and counters. |
|
|
292
|
-
| `help` | Markdown reference for
|
|
297
|
+
| `help` | Markdown reference for the actions enabled by server policy. |
|
|
293
298
|
|
|
294
299
|
Pagination and filtering are MCP-only. List actions return a paginated envelope
|
|
295
300
|
with `items`, `total`, `limit`, `offset`, `has_more`, and `next_offset`.
|
|
@@ -332,16 +337,24 @@ runraid setup repair [--json]
|
|
|
332
337
|
|
|
333
338
|
## Configuration
|
|
334
339
|
|
|
335
|
-
|
|
336
|
-
|
|
340
|
+
When `UNRAID_HOME` is non-empty, the binary reads `<UNRAID_HOME>/.env`.
|
|
341
|
+
Otherwise host installs read `~/.unraid/.env` and containers read `/data/.env`.
|
|
342
|
+
Non-empty process environment values override `.env`; empty plugin placeholders
|
|
343
|
+
inherit persisted `.env` values instead of clearing them. A present but malformed
|
|
344
|
+
`.env` is rejected so parsing cannot silently skip a later deny rule or credential.
|
|
337
345
|
|
|
338
346
|
| Variable | Default | Purpose |
|
|
339
347
|
|---|---|---|
|
|
340
348
|
| `UNRAID_API_URL` | unset | Full Unraid GraphQL endpoint URL. |
|
|
341
349
|
| `UNRAID_API_KEY` | unset | API key for the `x-api-key` header. |
|
|
342
350
|
| `UNRAID_API_SKIP_TLS_VERIFY` | `false` | Skip TLS certificate verification for self-signed endpoints. |
|
|
351
|
+
| `UNRAID_API_CA_BUNDLE` | _(unset)_ | Path to a PEM CA bundle to trust. Prefer this over skipping verification for a private CA; startup fails if the file is unreadable or not a PEM bundle. |
|
|
352
|
+
| `UNRAID_HOME` | platform default | Exact data directory for `.env`, auth DB, and JWT key; overrides `/data` or `~/.unraid`. |
|
|
343
353
|
| `UNRAID_RMCP_HOST` | `0.0.0.0` | HTTP bind host. |
|
|
344
354
|
| `UNRAID_RMCP_PORT` | `40010` | HTTP bind port. |
|
|
355
|
+
| `UNRAID_RMCP_PROJECTION` | `legacy` | MCP tool projection: `legacy`, `atomic`, or `both`. |
|
|
356
|
+
| `UNRAID_RMCP_ENABLED_TOOLS` | unset | Comma-separated MCP tool/action allowlist; empty means all. |
|
|
357
|
+
| `UNRAID_RMCP_DISABLED_TOOLS` | unset | Comma-separated MCP tool/action denylist; deny rules win. |
|
|
345
358
|
| `UNRAID_RMCP_SERVER_NAME` | `unraid-rmcp` | Advertised MCP server name. |
|
|
346
359
|
| `UNRAID_RMCP_TOKEN` | unset | Static bearer token for HTTP MCP. |
|
|
347
360
|
| `UNRAID_RMCP_NO_AUTH` | `false` | Disable auth only for loopback development. |
|
|
@@ -354,6 +367,18 @@ Host installs read `~/.unraid/.env` before loading config. Containers read
|
|
|
354
367
|
| `UNRAID_RMCP_GOOGLE_CLIENT_ID` | unset | Google OAuth client ID. |
|
|
355
368
|
| `UNRAID_RMCP_GOOGLE_CLIENT_SECRET` | unset | Google OAuth client secret. |
|
|
356
369
|
| `UNRAID_RMCP_AUTH_ADMIN_EMAIL` | unset | Admin email for OAuth bootstrap. |
|
|
370
|
+
| `UNRAID_RMCP_AUTH_ALLOWED_REDIRECT_URIS` | unset | Comma-separated HTTPS redirect URI patterns allowed during OAuth dynamic client registration. Loopback HTTP callbacks are allowed automatically. |
|
|
371
|
+
|
|
372
|
+
Tool selectors may target the entire surface (`*`, `unraid`, or `unraid.*`) or a single canonical action (`docker_logs` or `unraid.vm_reset`). Selectors do not change between projection modes. Tool discovery and `unraid://schema/mcp-tool` reflect the active projection and expose only enabled actions and their parameters; disabled calls are still rejected when a client uses a stale cached schema. Atomic tool annotations derive read-only and destructive hints from the same canonical scope and elicitation metadata. Invalid selectors, projection values, and unknown TOML fields fail configuration loading.
|
|
373
|
+
|
|
374
|
+
A set, non-empty `UNRAID_RMCP_ENABLED_TOOLS` / `UNRAID_RMCP_DISABLED_TOOLS`
|
|
375
|
+
**replaces** the corresponding `[mcp.tools]` list from `config.toml` — env and
|
|
376
|
+
toml are never merged, so the env var is the complete policy for that list.
|
|
377
|
+
An env var set to the empty string is treated as a placeholder: a matching
|
|
378
|
+
value from `<UNRAID_HOME>/.env`, `~/.unraid/.env`, or `/data/.env` is loaded when
|
|
379
|
+
present, otherwise it behaves as unset. A non-empty value that parses to zero selectors (for example
|
|
380
|
+
`","`) fails startup. This policy governs the MCP surface only; the `runraid`
|
|
381
|
+
CLI is not filtered by `[mcp.tools]`.
|
|
357
382
|
|
|
358
383
|
## Authentication
|
|
359
384
|
|
|
@@ -370,7 +395,7 @@ HTTP MCP auth policy:
|
|
|
370
395
|
|
|
371
396
|
Read actions require `unraid:read`; mutating actions require `unraid:admin`.
|
|
372
397
|
Configured static bearer tokens are operator credentials and receive admin scope.
|
|
373
|
-
OAuth clients receive the scopes granted by the authorization flow.
|
|
398
|
+
OAuth clients receive the scopes granted by the authorization flow. Hosted clients that use dynamic client registration must register an HTTPS callback present in `UNRAID_RMCP_AUTH_ALLOWED_REDIRECT_URIS`; loopback HTTP callbacks are accepted automatically. Prefer exact callback URLs, for example `https://grok.com/connectors/oauth/callback`, and use wildcard patterns only when the client requires them.
|
|
374
399
|
|
|
375
400
|
## Safety And Trust Model
|
|
376
401
|
|
|
@@ -414,8 +439,8 @@ CLI shim (src/cli.rs) argv -> service -> stdout
|
|
|
414
439
|
- The npm package remains a compatibility surface only. Automatic npm publishing
|
|
415
440
|
is enabled only when `NPM_TRUSTED_PUBLISHING_ENABLED=true`.
|
|
416
441
|
- Docker/OCI metadata uses `ghcr.io/dinglebear-ai/unraid-rmcp:<version>`.
|
|
417
|
-
- `agents/unraid-rs/.mcp.json` must launch `
|
|
418
|
-
clients resolve the
|
|
442
|
+
- `agents/unraid-rs/.mcp.json` must launch `npx -y @dinglebear/unraid mcp` so
|
|
443
|
+
stdio clients resolve the published npm package.
|
|
419
444
|
- The root README is curated. `docs/INVENTORY.md` is the curated inventory for
|
|
420
445
|
actions, CLI commands, env vars, HTTP endpoints, and dependencies.
|
|
421
446
|
|
|
@@ -431,18 +456,24 @@ npm --prefix packages/unraid-rmcp run check
|
|
|
431
456
|
|
|
432
457
|
## Verification
|
|
433
458
|
|
|
459
|
+
Run from `unraid-rs/`. The shared checker lives in this repository; no personal
|
|
460
|
+
checkout or external documentation script is required. The npm `check` command
|
|
461
|
+
also performs a packed-install smoke test and requires the supported Linux x64
|
|
462
|
+
platform; `npm --prefix packages/unraid-rmcp test` runs the portable wrapper tests.
|
|
463
|
+
|
|
434
464
|
```bash
|
|
435
|
-
python3 /
|
|
465
|
+
python3 ../.github/scripts/check_documentation.py
|
|
436
466
|
npm --prefix packages/unraid-rmcp run check
|
|
437
467
|
cargo check
|
|
438
468
|
cargo test
|
|
439
469
|
git diff --check
|
|
440
470
|
```
|
|
441
471
|
|
|
442
|
-
Runtime smoke
|
|
472
|
+
Runtime smoke requires an explicitly selected disposable test server and
|
|
473
|
+
privately supplied credentials. It is not part of the offline documentation gate.
|
|
443
474
|
|
|
444
475
|
```bash
|
|
445
|
-
UNRAID_API_URL=https://
|
|
476
|
+
UNRAID_API_URL=https://your-test-server.example/graphql \
|
|
446
477
|
UNRAID_API_KEY=... \
|
|
447
478
|
runraid server --json
|
|
448
479
|
```
|
|
@@ -478,7 +509,7 @@ gateway.
|
|
|
478
509
|
| Symptom | Check |
|
|
479
510
|
|---|---|
|
|
480
511
|
| `UNRAID_API_URL` or `UNRAID_API_KEY` is missing | Set it in env or `~/.unraid/.env`. |
|
|
481
|
-
| TLS errors against Unraid |
|
|
512
|
+
| TLS errors against Unraid | Point `UNRAID_API_CA_BUNDLE` at your CA's PEM bundle to keep verification on. Use `UNRAID_API_SKIP_TLS_VERIFY=true` only when you have no bundle. |
|
|
482
513
|
| HTTP `/mcp` returns unauthorized | Set `UNRAID_RMCP_TOKEN` and send `Authorization: Bearer <token>`. |
|
|
483
514
|
| Stdio client hangs or logs JSON errors | Ensure client config runs `unraid-rmcp mcp`, not the default HTTP server mode. |
|
|
484
515
|
| Large list response is truncated | Use `limit`, `offset`, `name`, or `state` filters on MCP list actions. |
|
package/lib/platform.js
CHANGED
|
@@ -12,7 +12,7 @@ function binaryVersion() {
|
|
|
12
12
|
|
|
13
13
|
function targetFor(platform = process.platform, arch = process.arch) {
|
|
14
14
|
if (platform === "linux" && arch === "x64") {
|
|
15
|
-
return { asset: "runraid-x86_64.tar.gz", binary: "runraid" };
|
|
15
|
+
return { asset: "runraid-x86_64.tar.gz", archiveBinary: "runraid-linux-x86_64", binary: "runraid" };
|
|
16
16
|
}
|
|
17
17
|
throw new Error(`Unsupported platform ${platform}/${arch}. Supported target: linux/x64.`);
|
|
18
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dinglebear/unraid",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Rust MCP server and CLI for Unraid GraphQL operations across NAS, Docker, VM, and storage workflows.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/dinglebear-ai/unraid#readme",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"oauth"
|
|
56
56
|
],
|
|
57
57
|
"mcpName": "ai.dinglebear/unraid",
|
|
58
|
-
"binaryVersion": "0.2
|
|
58
|
+
"binaryVersion": "0.6.2",
|
|
59
59
|
"author": {
|
|
60
60
|
"name": "dinglebear.ai",
|
|
61
61
|
"url": "https://dinglebear.ai"
|
package/scripts/install.js
CHANGED
|
@@ -71,6 +71,18 @@ function extract(archive, destination) {
|
|
|
71
71
|
const result = spawnSync("tar", ["-xzf", archive, "-C", destination], { encoding: "utf8" });
|
|
72
72
|
if (result.status !== 0) throw new Error((result.stderr || result.stdout || "tar extraction failed").trim());
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
function promoteExtractedBinary(target, destination, root = installRoot()) {
|
|
76
|
+
const extracted = path.join(root, target.archiveBinary || target.binary);
|
|
77
|
+
if (!fs.existsSync(extracted)) {
|
|
78
|
+
throw new Error(`archive did not contain expected binary ${path.basename(extracted)}`);
|
|
79
|
+
}
|
|
80
|
+
if (path.resolve(extracted) !== path.resolve(destination)) {
|
|
81
|
+
fs.rmSync(destination, { force: true });
|
|
82
|
+
fs.renameSync(extracted, destination);
|
|
83
|
+
}
|
|
84
|
+
fs.chmodSync(destination, 0o755);
|
|
85
|
+
}
|
|
74
86
|
async function main() {
|
|
75
87
|
if (process.env.UNRAID_RMCP_SKIP_DOWNLOAD === "1") { log("skipping binary download because UNRAID_RMCP_SKIP_DOWNLOAD=1"); return; }
|
|
76
88
|
const target = targetFor();
|
|
@@ -84,8 +96,12 @@ async function main() {
|
|
|
84
96
|
await download(url, archive);
|
|
85
97
|
await verifyChecksum(url, archive);
|
|
86
98
|
extract(archive, installRoot());
|
|
87
|
-
|
|
99
|
+
promoteExtractedBinary(target, destination);
|
|
88
100
|
log(`installed ${destination}`);
|
|
89
101
|
} finally { fs.rmSync(tempDir, { recursive: true, force: true }); }
|
|
90
102
|
}
|
|
91
|
-
|
|
103
|
+
if (require.main === module) {
|
|
104
|
+
main().catch((error) => { log(error.message); process.exitCode = 1; });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = { checksumFromText, promoteExtractedBinary };
|