r024: compact Docker inspect output
This commit is contained in:
BIN
Binary file not shown.
+16
-7
@@ -197,7 +197,7 @@ function sanitize_log_output(string $text, int $max_chars = 50000): string {
|
||||
/**
|
||||
* Kompakte Container-Inspect-Ausgabe.
|
||||
*/
|
||||
function compact_container_inspect(string $raw): string {
|
||||
function compact_container_inspect(string $raw, string $detail = 'summary'): string {
|
||||
$data = json_decode($raw, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE || !is_array($data)) {
|
||||
return $raw;
|
||||
@@ -218,17 +218,26 @@ function compact_container_inspect(string $raw): string {
|
||||
'Image' => $data['Config']['Image'] ?? '',
|
||||
'NetworkMode' => $data['HostConfig']['NetworkMode'] ?? '',
|
||||
'Ports' => $data['NetworkSettings']['Ports'] ?? [],
|
||||
'Env' => $data['Config']['Env'] ?? [],
|
||||
'Mounts' => array_map(function ($m) {
|
||||
'RestartCount' => $data['RestartCount'] ?? 0,
|
||||
'Created' => $data['Created'] ?? '',
|
||||
];
|
||||
if ($detail === 'full') {
|
||||
$compact['Env'] = array_map(function ($entry) {
|
||||
if (!is_string($entry) || !str_contains($entry, '=')) return $entry;
|
||||
[$name, $value] = explode('=', $entry, 2);
|
||||
if (preg_match('/(KEY|TOKEN|SECRET|PASS|AUTH|COOKIE|ARL)/i', $name)) {
|
||||
return $name . '=<redacted>';
|
||||
}
|
||||
return $name . '=' . $value;
|
||||
}, $data['Config']['Env'] ?? []);
|
||||
$compact['Mounts'] = array_map(function ($m) {
|
||||
return [
|
||||
'Type' => $m['Type'] ?? '',
|
||||
'Source' => $m['Source'] ?? '',
|
||||
'Destination' => $m['Destination'] ?? '',
|
||||
];
|
||||
}, $data['Mounts'] ?? []),
|
||||
'RestartCount' => $data['RestartCount'] ?? 0,
|
||||
'Created' => $data['Created'] ?? '',
|
||||
];
|
||||
}, $data['Mounts'] ?? []);
|
||||
}
|
||||
return json_encode($compact, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,24 @@ try {
|
||||
check('container_runtime_summary() funktioniert', false, $e->getMessage());
|
||||
}
|
||||
|
||||
// ── 3b. Kompakte Inspect-Ausgabe ─────────────────────────────────────────
|
||||
$fixture = json_encode([[
|
||||
'Id' => str_repeat('a', 64),
|
||||
'Name' => '/test',
|
||||
'State' => ['Status' => 'running', 'Running' => true, 'Pid' => 42, 'ExitCode' => 0],
|
||||
'Config' => ['Image' => 'example:latest', 'Env' => ['SECRET=value']],
|
||||
'HostConfig' => ['NetworkMode' => 'bridge'],
|
||||
'NetworkSettings' => ['Ports' => []],
|
||||
'Mounts' => [['Type' => 'bind', 'Source' => '/secret', 'Destination' => '/data']],
|
||||
'RestartCount' => 0,
|
||||
'Created' => 'now',
|
||||
]]);
|
||||
$summaryInspect = json_decode(compact_container_inspect($fixture), true);
|
||||
$fullInspect = json_decode(compact_container_inspect($fixture, 'full'), true);
|
||||
check('Inspect summary laesst Env aus', !isset($summaryInspect['Env']) && !isset($summaryInspect['Mounts']));
|
||||
check('Inspect full enthaelt Env und Mounts', isset($fullInspect['Env']) && isset($fullInspect['Mounts']));
|
||||
check('Inspect full maskiert Secrets', ($fullInspect['Env'][0] ?? '') === 'SECRET=<redacted>');
|
||||
|
||||
// ── 4. Netzwerk-Inventur ─────────────────────────────────────────────────
|
||||
echo "\n[4] Netzwerk-Inventur\n";
|
||||
try {
|
||||
|
||||
+5
-1
@@ -95,8 +95,12 @@ function call_tool(string $name, array $args): string {
|
||||
|
||||
case 'unraid_docker_inspect':
|
||||
$container = validate_name($args['container'] ?? null, 'container');
|
||||
$detail = $args['detail'] ?? 'summary';
|
||||
if (!in_array($detail, ['summary', 'full'], true)) {
|
||||
throw new InvalidArgumentException('detail must be summary or full');
|
||||
}
|
||||
$raw = docker_exec("inspect --type container -- $container");
|
||||
return compact_container_inspect($raw);
|
||||
return compact_container_inspect($raw, $detail);
|
||||
|
||||
case 'unraid_docker_logs':
|
||||
$container = validate_name($args['container'] ?? null, 'container');
|
||||
|
||||
+3
-1
@@ -27,13 +27,15 @@ function mua_tools(): array {
|
||||
],
|
||||
[
|
||||
'name' => 'unraid_docker_inspect',
|
||||
'description' => 'Inspect a single Docker container in detail (state, image, ports, env, mounts).',
|
||||
'description' => "Inspect one known Docker container directly. Default summary returns state, image, network mode and ports only; request detail='full' only when environment variables or mounts are required.",
|
||||
'inputSchema' => [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'container' => ['type' => 'string', 'description' => 'Container name or ID'],
|
||||
'detail' => ['type' => 'string', 'enum' => ['summary', 'full'], 'description' => "Use 'summary' by default. 'full' additionally returns redacted environment and mounts."],
|
||||
],
|
||||
'required' => ['container'],
|
||||
'additionalProperties' => false,
|
||||
],
|
||||
],
|
||||
[
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mua",
|
||||
"version": "2026.08.24.r023",
|
||||
"version": "2026.08.25.r024",
|
||||
"description": "Mikes Unraid Agent - MCP over HTTP (Streamable HTTP) for Unraid",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
+8
-4
@@ -2,13 +2,13 @@
|
||||
<!DOCTYPE PLUGIN [
|
||||
<!ENTITY name "mua">
|
||||
<!ENTITY author "Michael">
|
||||
<!ENTITY version "2026.08.24.r023">
|
||||
<!ENTITY version "2026.08.25.r024">
|
||||
<!ENTITY launch "Settings/mua">
|
||||
<!ENTITY pluginURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/plugin/mua.plg">
|
||||
<!ENTITY pluginLOC "/boot/config/plugins/&name;">
|
||||
<!ENTITY emhttpLOC "/usr/local/emhttp/plugins/&name;">
|
||||
<!ENTITY txzURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.24.r023-x86_64-1.txz">
|
||||
<!ENTITY txzSHA256 "acc41140d7fdc9d720843a50051e7a018767225f4165525f70fe83ff297a73a7">
|
||||
<!ENTITY txzURL "http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.25.r024-x86_64-1.txz">
|
||||
<!ENTITY txzSHA256 "f8101b3d24fc9ebb02ce01b6f6481281578910f7a0cbe11b05bc484387ae1a90">
|
||||
]>
|
||||
|
||||
<PLUGIN name="&name;"
|
||||
@@ -23,6 +23,10 @@
|
||||
>
|
||||
|
||||
<CHANGES>
|
||||
### 2026.08.25.r024
|
||||
- Container-Inspect liefert standardmäßig nur Status, Image, Netzwerkmodus und Ports; Umgebungsvariablennamen und Mounts sind nur noch über `detail=full` enthalten.
|
||||
- Reduziert große MCP-Ausgaben bei gezielten Containerdiagnosen und hält Secrets weiterhin vollständig aus den Antworten heraus.
|
||||
|
||||
### 2026.08.24.r023
|
||||
- Begrenzt die Ausgabe der Nur-Lese-Shell bereits serverseitig auf standardmäßig 12.000 Zeichen und erlaubt ein explizites Limit von 1.000 bis 30.000 Zeichen.
|
||||
- Präzisiert die Werkzeugbeschreibung für eine zielgerichtete Diagnosekette statt breiter Konfigurations- und Verzeichnisabfragen.
|
||||
@@ -149,7 +153,7 @@ Das .txz enthält:
|
||||
install/doinst.sh (läuft nach Installation)
|
||||
===========================================
|
||||
-->
|
||||
<FILE Name="/boot/config/plugins/&name;/mua-2026.08.24.r023-x86_64-1.txz" Run="upgradepkg --install-new" Mode="755" Min="7.0.0">
|
||||
<FILE Name="/boot/config/plugins/&name;/mua-2026.08.25.r024-x86_64-1.txz" Run="upgradepkg --install-new" Mode="755" Min="7.0.0">
|
||||
<URL>&txzURL;</URL>
|
||||
<SHA256>&txzSHA256;</SHA256>
|
||||
</FILE>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mua",
|
||||
"author": "Michael",
|
||||
"version": "2026.08.24.r023",
|
||||
"version": "2026.08.25.r024",
|
||||
"minver": "7.0.0",
|
||||
"pluginDirectory": "/usr/local/emhttp/plugins/mua",
|
||||
"configDirectory": "/boot/config/plugins/mua",
|
||||
|
||||
+18
-13
@@ -15,7 +15,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync }
|
||||
|
||||
// ── Konstanten ──────────────────────────────────────────────────────────
|
||||
export const MUA_SERVER_NAME = "mua";
|
||||
export const MUA_VERSION = "2026.08.24.r023";
|
||||
export const MUA_VERSION = "2026.08.25.r024";
|
||||
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";
|
||||
@@ -475,7 +475,10 @@ export function sanitizeLogOutput(text: string, maxChars = 50000): string {
|
||||
}
|
||||
|
||||
// ── Compact Container Inspect ───────────────────────────────────────────
|
||||
export async function compactContainerInspect(container: string): Promise<string> {
|
||||
export async function compactContainerInspect(
|
||||
container: string,
|
||||
detail: "summary" | "full" = "summary",
|
||||
): Promise<string> {
|
||||
const raw = await dockerExec(`inspect --type container -- ${container}`);
|
||||
let data: unknown;
|
||||
try {
|
||||
@@ -485,7 +488,7 @@ export async function compactContainerInspect(container: string): Promise<string
|
||||
}
|
||||
if (Array.isArray(data)) data = data[0];
|
||||
const d = data as Record<string, any>;
|
||||
const compact = {
|
||||
const compact: Record<string, unknown> = {
|
||||
Id: (d.Id ?? "").slice(0, 12),
|
||||
Name: d.Name ?? "",
|
||||
State: {
|
||||
@@ -497,19 +500,21 @@ export async function compactContainerInspect(container: string): Promise<string
|
||||
Image: d.Config?.Image ?? "",
|
||||
NetworkMode: d.HostConfig?.NetworkMode ?? "",
|
||||
Ports: d.NetworkSettings?.Ports ?? [],
|
||||
// Environment-Werte enthalten sehr häufig API-Keys, Passwörter und
|
||||
// interne URLs. Für Diagnosezwecke reichen die vorhandenen Variablennamen.
|
||||
EnvNames: (d.Config?.Env ?? []).map((entry: unknown) =>
|
||||
typeof entry === "string" ? entry.split("=", 1)[0] : "",
|
||||
).filter((name: string) => name !== ""),
|
||||
Mounts: (d.Mounts ?? []).map((m: any) => ({
|
||||
Type: m.Type ?? "",
|
||||
Source: m.Source ?? "",
|
||||
Destination: m.Destination ?? "",
|
||||
})),
|
||||
RestartCount: d.RestartCount ?? 0,
|
||||
Created: d.Created ?? "",
|
||||
};
|
||||
if (detail === "full") {
|
||||
// Environment-Werte enthalten sehr häufig API-Keys, Passwörter und
|
||||
// interne URLs. Für Diagnosezwecke reichen die Variablennamen.
|
||||
compact.EnvNames = (d.Config?.Env ?? []).map((entry: unknown) =>
|
||||
typeof entry === "string" ? entry.split("=", 1)[0] : "",
|
||||
).filter((name: string) => name !== "");
|
||||
compact.Mounts = (d.Mounts ?? []).map((m: any) => ({
|
||||
Type: m.Type ?? "",
|
||||
Source: m.Source ?? "",
|
||||
Destination: m.Destination ?? "",
|
||||
}));
|
||||
}
|
||||
return JSON.stringify(compact);
|
||||
}
|
||||
|
||||
|
||||
+14
-3
@@ -107,13 +107,24 @@ export const TOOLS: ToolDef[] = [
|
||||
{
|
||||
name: "unraid_docker_inspect",
|
||||
description:
|
||||
"Inspect a single Docker container in detail (state, image, ports, redacted environment variable names, mounts). Secret values are never returned.",
|
||||
"Inspect one known Docker container directly. Default summary returns state, image, network mode and ports only. Use detail='full' only when redacted environment variable names or mounts are required. Secret values are never returned.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: { container: str("Container name or ID") },
|
||||
properties: {
|
||||
container: str("Container name or ID"),
|
||||
detail: {
|
||||
type: "string",
|
||||
enum: ["summary", "full"],
|
||||
description: "summary is compact; full additionally returns environment variable names and mounts. Default: summary.",
|
||||
},
|
||||
},
|
||||
required: ["container"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
handler: (a) => compactContainerInspect(validateName(a["container"], "container")),
|
||||
handler: (a) => compactContainerInspect(
|
||||
validateName(a["container"], "container"),
|
||||
(a["detail"] as "summary" | "full" | undefined),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "unraid_docker_logs",
|
||||
|
||||
Reference in New Issue
Block a user