diff --git a/src/agents/tool-policy-declared-context.ts b/src/agents/tool-policy-declared-context.ts index 4904ec78e6e..9f7648ca65f 100644 --- a/src/agents/tool-policy-declared-context.ts +++ b/src/agents/tool-policy-declared-context.ts @@ -25,32 +25,27 @@ function denylistBlocksName(name: string, denylist: ToolDenylist): boolean { return normalized ? matchesAnyGlobPattern(normalized, denylist) : false; } -function denylistContainsMcpServerEntry(params: { +function denylistBlocksMcpServerNamespace(params: { safeServerName: string; - rawDenylist?: string[]; + denylist: ToolDenylist; }): boolean { const serverPrefix = normalizeToolName(params.safeServerName + TOOL_NAME_SEPARATOR); if (!serverPrefix) { return false; } - const serverWideEntry = serverPrefix + "*"; - return (params.rawDenylist ?? []).some((entry) => { - const normalized = normalizeToolName(entry); - return normalized === serverWideEntry; - }); + return matchesAnyGlobPattern(serverPrefix, params.denylist); } function denylistBlocksMcpServer(params: { safeServerName: string; - rawDenylist?: string[]; denylist: ToolDenylist; }): boolean { return ( denylistBlocksName("bundle-mcp", params.denylist) || matchesAnyGlobPattern("group:plugins", params.denylist) || - denylistContainsMcpServerEntry({ + denylistBlocksMcpServerNamespace({ safeServerName: params.safeServerName, - rawDenylist: params.rawDenylist, + denylist: params.denylist, }) ); } @@ -89,7 +84,6 @@ function collectConfiguredMcpServerNames(params: { if ( denylistBlocksMcpServer({ safeServerName, - rawDenylist: params.toolDenylist, denylist, }) ) { diff --git a/src/agents/tool-policy-pipeline.test.ts b/src/agents/tool-policy-pipeline.test.ts index af3b5303c05..497aa9a6acf 100644 --- a/src/agents/tool-policy-pipeline.test.ts +++ b/src/agents/tool-policy-pipeline.test.ts @@ -336,6 +336,35 @@ describe("tool-policy-pipeline", () => { ]); }); + test("warns when broad MCP server wildcard deny covers an allowlisted namespace", () => { + const warnings: string[] = []; + const declared = buildDeclaredToolAllowlistContext({ + config: { + mcp: { servers: { paperless: { command: "paperless-mcp" } } }, + }, + workspaceDir: process.cwd(), + toolDenylist: ["paperless*"], + }); + + applyToolPolicyPipeline({ + tools: [{ name: "exec" }] as any, + toolMeta: () => undefined, + warn: (msg) => warnings.push(msg), + declaredToolAllowlist: declared, + steps: [ + { + policy: { allow: ["paperless__*"] }, + label: "tools.allow", + stripPluginOnlyAllowlist: true, + }, + ], + }); + + expect(warnings).toEqual([ + "tools: tools.allow allowlist contains unknown entries (paperless__*). These entries won't match any tool unless the plugin is enabled.", + ]); + }); + test("does not warn for MCP server namespace allowlist when one exact server tool is denied", () => { const warnings: string[] = []; const declared = buildDeclaredToolAllowlistContext({