docs: clarify malformed plugin tool guards

This commit is contained in:
Peter Steinberger
2026-04-27 13:27:13 +01:00
parent 41d5c27894
commit 3c6d178f4e
2 changed files with 5 additions and 0 deletions

View File

@@ -396,6 +396,8 @@ function convertAnthropicTools(tools: Context["tools"], isOAuthToken: boolean) {
return []; return [];
} }
return tools.flatMap((tool) => { return tools.flatMap((tool) => {
// Main quarantine happens when plugin tools materialize; this keeps Anthropic
// safe for direct/custom tool arrays that bypass the plugin registry.
const parameters = const parameters =
tool.parameters && typeof tool.parameters === "object" && !Array.isArray(tool.parameters) tool.parameters && typeof tool.parameters === "object" && !Array.isArray(tool.parameters)
? (tool.parameters as Record<string, unknown>) ? (tool.parameters as Record<string, unknown>)

View File

@@ -66,6 +66,7 @@ function readPluginToolName(tool: unknown): string {
if (!isRecord(tool)) { if (!isRecord(tool)) {
return ""; return "";
} }
// Optional-tool allowlists need a best-effort name before full shape validation.
return typeof tool.name === "string" ? tool.name.trim() : ""; return typeof tool.name === "string" ? tool.name.trim() : "";
} }
@@ -189,6 +190,8 @@ export function resolvePluginTools(params: {
} }
const nameSet = new Set<string>(); const nameSet = new Set<string>();
for (const toolRaw of list) { for (const toolRaw of list) {
// Plugin factories run at request time and can return arbitrary values; isolate
// malformed tools here so one bad plugin tool cannot poison every provider.
const malformedReason = describeMalformedPluginTool(toolRaw); const malformedReason = describeMalformedPluginTool(toolRaw);
if (malformedReason) { if (malformedReason) {
const message = `plugin tool is malformed (${entry.pluginId}): ${malformedReason}`; const message = `plugin tool is malformed (${entry.pluginId}): ${malformedReason}`;