release: r017 batch Community Apps search
This commit is contained in:
+26
-11
@@ -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<string, unknown>[] {
|
||||
}
|
||||
|
||||
export async function searchCommunityApps(query: string, limit = 10): Promise<string> {
|
||||
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<st
|
||||
const repo = caText(app.Repository, 300);
|
||||
const extra = `${caText(app.Overview, 1000)} ${caText(app.Repo, 200)}`.toLowerCase();
|
||||
const lower = name.toLowerCase();
|
||||
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 { app, score, name, repo };
|
||||
const matches = needles.map((needle) => {
|
||||
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<st
|
||||
source: "Unraid Community Applications official feed",
|
||||
feed_updated: feed.updated ?? null,
|
||||
query,
|
||||
query_terms: needles,
|
||||
result_count: scored.length,
|
||||
results: scored.map(({ app, name, repo }) => ({
|
||||
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",
|
||||
|
||||
+2
-2
@@ -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"],
|
||||
|
||||
Reference in New Issue
Block a user