release: r019 idempotent batch container updates

This commit is contained in:
Mikei386
2026-08-24 11:21:19 +02:00
parent 47c27b06c4
commit d90874ed18
11 changed files with 174 additions and 14 deletions
+11
View File
@@ -93,6 +93,17 @@ export function parseConfig(content: string): MUAConfig {
}
}
}
// r019 replaces repeated single-container update calls with an idempotent
// batch variant. If an administrator already enabled the older update
// capability, expose the safer equivalent automatically; this does not
// grant a new class of operation.
if (
!cfg.allToolsEnabled
&& cfg.enabledTools.includes("unraid_docker_update")
&& !cfg.enabledTools.includes("unraid_docker_update_verified_batch")
) {
cfg.enabledTools.push("unraid_docker_update_verified_batch");
}
return cfg;
}
+1 -1
View File
@@ -14,7 +14,7 @@ import { createHash, randomUUID } from "node:crypto";
// ── Konstanten ──────────────────────────────────────────────────────────
export const MUA_SERVER_NAME = "mua";
export const MUA_VERSION = "2026.08.24.r018";
export const MUA_VERSION = "2026.08.24.r019";
export const MUA_PROTOCOL_VERSION = "2025-03-26";
export const PHP_HELPER = "/usr/local/bin/unraid-docker-mcp-helper.php";
export const STATUS_HELPER = "/usr/local/bin/unraid-mcp-status-helper.php";
+9
View File
@@ -22,6 +22,14 @@ describe("secure tool configuration", () => {
expect(cfg.allToolsEnabled).toBe(true);
expect(cfg.enabledTools).toEqual([]);
});
test("an enabled legacy update also exposes the safer batch update", () => {
const cfg = parseConfig(
"MUA_API_KEY=test\nMUA_ENABLED_TOOLS=unraid_docker_update\n",
);
expect(cfg.enabledTools).toContain("unraid_docker_update");
expect(cfg.enabledTools).toContain("unraid_docker_update_verified_batch");
});
});
describe("secret handling", () => {
@@ -40,6 +48,7 @@ describe("risk classification", () => {
test("classifies root shell and container changes as critical", () => {
expect(getToolRisk("unraid_system_shell")).toBe("critical");
expect(getToolRisk("unraid_docker_modify")).toBe("critical");
expect(getToolRisk("unraid_docker_update_verified_batch")).toBe("critical");
expect(getToolRisk("unraid_docker_restart")).toBe("write");
expect(getToolRisk("unraid_network_lan_probe")).toBe("active");
expect(getToolRisk("unraid_docker_list")).toBe("read");
+27 -1
View File
@@ -40,6 +40,7 @@ const CRITICAL_TOOLS = new Set([
"unraid_docker_create",
"unraid_docker_modify",
"unraid_docker_update",
"unraid_docker_update_verified_batch",
"unraid_docker_rebuild",
"unraid_system_shell",
"unraid_ca_install",
@@ -281,7 +282,8 @@ export const TOOLS: ToolDef[] = [
},
{
name: "unraid_docker_update",
description: "Update a container (pull latest image, rebuild).",
description:
"Update one explicitly named container (pull latest image and rebuild). For two or more containers, or when update status may be stale, use unraid_docker_update_verified_batch instead.",
inputSchema: {
type: "object",
properties: { container: str("Container/template name") },
@@ -289,6 +291,30 @@ export const TOOLS: ToolDef[] = [
},
handler: (a) => runPhpHelper("update", validateName(a["container"], "container")),
},
{
name: "unraid_docker_update_verified_batch",
description:
"Authoritative one-call Docker image update workflow for 1-25 explicitly named Unraid containers. Pulls each configured image, compares immutable image IDs, skips already-current containers even if Unraid's cached status is stale, rebuilds only when the image actually changed, preserves every original running/stopped state, and returns compact post-verification (container ID change, state, restart count and health). Use this after read-only update discovery when the user explicitly requested the updates.",
inputSchema: {
type: "object",
properties: {
containers: str(
"One to 25 exact container names separated by |, as returned by Unraid inventory/update status",
),
},
required: ["containers"],
additionalProperties: false,
},
handler: (a) => {
const raw = String(a["containers"] ?? "");
const containers = raw.split("|").map((name) => name.trim()).filter(Boolean);
if (containers.length < 1 || containers.length > 25) {
throw new Error("containers must contain 1-25 names separated by |");
}
const unique = [...new Set(containers.map((name) => validateName(name, "container")))];
return runPhpHelper("update-verified-batch", unique.join("|"));
},
},
{
name: "unraid_docker_rebuild",
description: