diff --git a/README.md b/README.md index 4d8d8ea..bb6410e 100644 --- a/README.md +++ b/README.md @@ -76,10 +76,10 @@ Oder manuell: ```bash # .txz von Gitea laden -curl -O http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.23.r016-x86_64-1.txz +curl -O http://192.168.1.2:4000/michael/MUA-Mikes-Unraid-Agent/raw/branch/main/dist/mua-2026.08.24.r017-x86_64-1.txz # Installieren -upgradepkg --install-new mua-2026.08.23.r016-x86_64-1.txz +upgradepkg --install-new mua-2026.08.24.r017-x86_64-1.txz ``` ### 2. Service starten @@ -94,7 +94,7 @@ Der Service startet automatisch bei jedem Boot (SysVinit). ```bash curl http://192.168.1.2:3002/health -# → {"status":"ok","version":"2026.08.23.r016","auth":"required"} +# → {"status":"ok","version":"2026.08.24.r017","auth":"required"} ``` --- diff --git a/dist/mua-2026.08.24.r017-x86_64-1.txz b/dist/mua-2026.08.24.r017-x86_64-1.txz new file mode 100644 index 0000000..485a556 Binary files /dev/null and b/dist/mua-2026.08.24.r017-x86_64-1.txz differ diff --git a/mcp/helpers.php b/mcp/helpers.php index acc29ea..349ccb9 100644 --- a/mcp/helpers.php +++ b/mcp/helpers.php @@ -10,7 +10,7 @@ error_reporting(E_ALL); ini_set('display_errors', 0); // Fehler werden als JSON-RPC-Error zurückgegeben, nicht als HTML -const MUA_VERSION = '2026.08.23.r016'; +const MUA_VERSION = '2026.08.24.r017'; const MUA_SERVER_NAME = 'mua'; /** diff --git a/mcp/server.php b/mcp/server.php index 1ee07f1..d42c2d8 100644 --- a/mcp/server.php +++ b/mcp/server.php @@ -21,7 +21,7 @@ const MUA_PORT = 3002; const MUA_BIND = '0.0.0.0'; const MUA_SESSION_TTL = 3600; // Sekunden const MUA_SERVER_NAME = 'mua'; -const MUA_VERSION = '2026.08.23.r016'; +const MUA_VERSION = '2026.08.24.r017'; // ── Session-Management (in-memory) ─────────────────────────────────────── $sessions = []; // session_id => ['created' => time, 'initialized' => bool] diff --git a/package.json b/package.json index 250ac1f..3132ccf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mua", - "version": "2026.08.23.r016", + "version": "2026.08.24.r017", "description": "Mikes Unraid Agent - MCP over HTTP (Streamable HTTP) for Unraid", "type": "module", "main": "src/index.ts", diff --git a/plugin/mua.plg b/plugin/mua.plg index f5b5c44..b92319c 100644 --- a/plugin/mua.plg +++ b/plugin/mua.plg @@ -2,13 +2,13 @@ - + - - + + ]> +### 2026.08.24.r017 +- Community-Applications-Suche akzeptiert bis zu fünf mit `|` getrennte Namens-, Schreib- oder Zweckvarianten in einem einzigen begrenzten Feed-Durchlauf. +- Treffer nennen die tatsächlich passenden Varianten; kleine Agenten müssen Synonyme nicht mehr in einzelnen Werkzeugrunden abfragen. ### 2026.08.23.r016 - Docker-Update-Status wird primär über Unraids nativen Containernamen-Schlüssel aufgelöst; Image-, `library/`- und `:latest`-Varianten bleiben als kompatible Fallbacks erhalten. - Alternative Cacheformate und boolesche oder textuelle Statuswerte werden global normalisiert, statt einzelne Container wie Windows11 und WindowsXP als `unknown` zu verlieren. @@ -119,7 +122,7 @@ Das .txz enthält: install/doinst.sh (läuft nach Installation) =========================================== --> - + &txzURL; &txzSHA256; diff --git a/plugin/plugin.json b/plugin/plugin.json index d9c0463..b351711 100644 --- a/plugin/plugin.json +++ b/plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "mua", "author": "Michael", - "version": "2026.08.23.r016", + "version": "2026.08.24.r017", "minver": "7.0.0", "pluginDirectory": "/usr/local/emhttp/plugins/mua", "configDirectory": "/boot/config/plugins/mua", diff --git a/src/helpers.ts b/src/helpers.ts index 315f581..7a667f2 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -14,7 +14,7 @@ import { createHash, randomUUID } from "node:crypto"; // ── Konstanten ────────────────────────────────────────────────────────── export const MUA_SERVER_NAME = "mua"; -export const MUA_VERSION = "2026.08.23.r016"; +export const MUA_VERSION = "2026.08.24.r017"; 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"; @@ -826,8 +826,17 @@ function caConfigSummary(app: CaApp): Record[] { } export async function searchCommunityApps(query: string, limit = 10): Promise { - const needle = query.trim().toLowerCase(); - if (needle.length < 2 || needle.length > 100) throw new Error("query must contain 2-100 characters"); + // A pipe-separated query lets an agent check a small set of spelling or + // product-name variants in one bounded feed pass instead of burning one MCP + // round per synonym. Keep the schema itself a plain string; some local + // function-call parsers are unreliable with nested array/object schemas. + const needles = query.split("|").map((term) => term.trim().toLowerCase()).filter(Boolean); + if (needles.length < 1 || needles.length > 5) { + throw new Error("query must contain 1-5 terms separated by |"); + } + if (query.length > 300 || needles.some((needle) => needle.length < 2 || needle.length > 100)) { + throw new Error("each query term must contain 2-100 characters (300 total)"); + } limit = Math.max(1, Math.min(25, Math.floor(limit))); const feed = await caFeed(); const scored = feed.apps.map((app) => { @@ -835,13 +844,17 @@ export async function searchCommunityApps(query: string, limit = 10): Promise { + let score = 0; + if (lower === needle) score += 100; + else if (lower.startsWith(needle)) score += 60; + else if (lower.includes(needle)) score += 40; + if (repo.toLowerCase().includes(needle)) score += 20; + if (extra.includes(needle)) score += 5; + return { needle, score }; + }).filter((match) => match.score > 0); + const score = Math.max(0, ...matches.map((match) => match.score)); + return { app, score, name, repo, matches }; }).filter((entry) => entry.score > 0) .sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)) .slice(0, limit); @@ -850,10 +863,12 @@ export async function searchCommunityApps(query: string, limit = 10): Promise ({ + results: scored.map(({ app, name, repo, matches }) => ({ app_id: caAppId(app), name, + matched_terms: matches.map((match) => match.needle), image: repo, network: caText(app.Network, 80), privileged: String(app.Privileged ?? "false").toLowerCase() === "true", diff --git a/src/tools.ts b/src/tools.ts index 370504e..caa483d 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -286,11 +286,11 @@ export const TOOLS: ToolDef[] = [ { name: "unraid_ca_search", description: - "Search the official Unraid Community Applications feed. Returns compact app metadata and a stable app_id; does not install anything.", + "Search the official Unraid Community Applications feed in one bounded pass. Returns compact app metadata and a stable app_id; does not install anything. Put up to five useful name/spelling/purpose variants in one query separated by | instead of making repeated synonym calls.", inputSchema: { type: "object", properties: { - query: str("Application name, image, repository or purpose"), + query: str("One to five application names, images, repositories or purposes separated by |"), limit: int("Maximum results (1-25, default 10)"), }, required: ["query"],