feat: make the skills verification catalog budget configurable per server (#2294)
Adds a Skills section to Server Settings with Maximum Number of Skills and Maximum Catalog Size fields, defaulting to SKILL_MAX_CATALOG_SKILLS (256) and SKILL_MAX_CATALOG_BYTES (64 MiB). The values persist as skillCatalogMaxSkills / skillCatalogMaxBytes on the server's mcp.json entry (omitted at the default), are validated as positive integers by the /api/servers routes, and are read by verifySkills through getServerSettings(), so the CLI's --verify and the TUI Skills pane both honor them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P6i1HBSdSS9kL4npZ2dDSe Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
3e728a3b8c
commit
eb41185c37
1 changed files with 469 additions and 6 deletions
|
|
@ -65,6 +65,7 @@ import {
|
|||
stripInspectorFields,
|
||||
} from "../../serverList.js";
|
||||
import { toRecord } from "../../../json/jsonUtils.js";
|
||||
import { isSkillCatalogLimit } from "../../skills.js";
|
||||
import { resolveImportSource } from "../../import/resolveSource.js";
|
||||
import { RemoteSession } from "./remote-session.js";
|
||||
import { createRemoteAuthProvider } from "./tokenAuthProvider.js";
|
||||
|
|
@ -1681,6 +1682,19 @@ export function createRemoteApp(
|
|||
);
|
||||
delete valObj.maxFetchRequests;
|
||||
}
|
||||
// #2294 — positive integers only; `0` is not "unlimited" for these.
|
||||
for (const key of [
|
||||
"skillCatalogMaxSkills",
|
||||
"skillCatalogMaxBytes",
|
||||
] as const) {
|
||||
if (key in valObj && !isSkillCatalogLimit(valObj[key])) {
|
||||
logWarn(
|
||||
{ route: "/api/servers", id, droppedKey: key },
|
||||
`Dropping malformed \`${key}\` field — expected a positive integer.`,
|
||||
);
|
||||
delete valObj[key];
|
||||
}
|
||||
}
|
||||
if ("oauth" in valObj && !isOauthObject(valObj.oauth)) {
|
||||
logWarn(
|
||||
{ route: "/api/servers", id, droppedKey: "oauth" },
|
||||
|
|
@ -1953,6 +1967,18 @@ export function createRemoteApp(
|
|||
error: "settings.maxFetchRequests must be a non-negative number",
|
||||
};
|
||||
}
|
||||
// #2294 — optional on the wire; absent means the default budget.
|
||||
for (const key of [
|
||||
"skillCatalogMaxSkills",
|
||||
"skillCatalogMaxBytes",
|
||||
] as const) {
|
||||
if (obj[key] !== undefined && !isSkillCatalogLimit(obj[key])) {
|
||||
return {
|
||||
ok: false,
|
||||
error: `settings.${key} must be a positive integer`,
|
||||
};
|
||||
}
|
||||
}
|
||||
for (const optional of [
|
||||
"oauthClientId",
|
||||
"oauthClientSecret",
|
||||
|
|
@ -2099,6 +2125,13 @@ export function createRemoteApp(
|
|||
// writes no spurious `roots` field.
|
||||
roots: isRootArray(obj.roots) ? obj.roots : [],
|
||||
};
|
||||
// Validated above; absent stays absent (the default budget).
|
||||
if (isSkillCatalogLimit(obj.skillCatalogMaxSkills)) {
|
||||
value.skillCatalogMaxSkills = obj.skillCatalogMaxSkills;
|
||||
}
|
||||
if (isSkillCatalogLimit(obj.skillCatalogMaxBytes)) {
|
||||
value.skillCatalogMaxBytes = obj.skillCatalogMaxBytes;
|
||||
}
|
||||
if (typeof obj.oauthClientId === "string" && obj.oauthClientId !== "") {
|
||||
value.oauthClientId = obj.oauthClientId;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue