Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions src/mcp-server/tools/pipeshubSources.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as z from "zod";
import { knowledgeHubGetKnowledgeHubRootNodes } from "../../funcs/knowledgeHubGetKnowledgeHubRootNodes.js";
import { aiModelsProvidersGetAvailableModelsByType } from "../../funcs/aiModelsProvidersGetAvailableModelsByType.js";
import { APIError } from "../../models/errors/apierror.js";
import { ToolDefinition } from "../tools.js";
import { errorResult, jsonResult, readJson } from "./_helpers.js";

Expand All @@ -26,14 +27,13 @@ Returns up to three sections:
\`pipeshub_search\`'s \`apps\` filter.
- \`llmModels\` — chat / generation models. Each item's \`modelKey\`
is the value to pass on \`pipeshub_chat\` / \`pipeshub_search\` as
\`modelKey\`. Pick \`isDefault: true\` unless the user asks for a
specific model.
- \`embeddingModels\` — vector embedding models (only fetched when
explicitly requested via \`include\`).
\`modelKey\`. Pick \`isDefault: true\` unless specified. Missing
\`config:read\` returns \`[]\` plus \`llmModelsRestricted: true\`.
- \`embeddingModels\` — vector embedding models (fetched only via
\`include\`). Same restriction applies.

Call this once at the start of a session and cache the result —
sources and models change infrequently. \`sources\` and \`llmModels\`
are returned by default; pass \`include\` to override.`,
Call once per session and cache — sources/models change rarely.
\`sources\`/\`llmModels\` return by default; pass \`include\` to override.`,
scopes: ["read"],
annotations: {
title: "List PipesHub sources and AI models",
Expand Down Expand Up @@ -78,7 +78,22 @@ are returned by default; pass \`include\` to override.`,
const [r] = await aiModelsProvidersGetAvailableModelsByType(client, {
modelType,
}, { fetchOptions }).$inspect();
if (!r.ok) return errorResult(`${key}: ${r.error.message}`);
if (!r.ok) {
// `llmModels`/`embeddingModels` are gated by config:read, which a
// caller's token may not have. That denial degrades silently to an
// empty array rather than a 403. Reporting the distinction keeps
// "nothing configured" from looking the same as "not authorized to
// see it" (see issue #2942).
const status = r.error instanceof APIError
? r.error.httpMeta.response.status
: undefined;
if (status === 403) {
result[key] = [];
result[`${key}Restricted`] = true;
continue;
}
return errorResult(`${key}: ${r.error.message}`);
}
const parsed = await readJson<{ models?: any[] }>(r.value, "Model listing");
if (!parsed.ok) return parsed.result;
result[key] = (parsed.value.models ?? []).map((m: any) => ({
Expand All @@ -93,4 +108,4 @@ are returned by default; pass \`include\` to override.`,

return jsonResult(result);
},
};
};
Loading