fix(policy): ensure MCP policies match unqualified names in non-interactive mode (#16490)

This commit is contained in:
N. Taylor Mullen
2026-01-12 23:25:11 -08:00
committed by GitHub
parent 6adae9f775
commit 7bbfaabffa
4 changed files with 125 additions and 11 deletions
@@ -109,6 +109,37 @@ describe('PolicyEngine', () => {
);
});
it('should match unqualified tool names with qualified rules when serverName is provided', async () => {
const rules: PolicyRule[] = [
{
toolName: 'my-server__tool',
decision: PolicyDecision.ALLOW,
},
];
engine = new PolicyEngine({ rules });
// Match with qualified name (standard)
expect(
(await engine.check({ name: 'my-server__tool' }, 'my-server')).decision,
).toBe(PolicyDecision.ALLOW);
// Match with unqualified name + serverName (the fix)
expect((await engine.check({ name: 'tool' }, 'my-server')).decision).toBe(
PolicyDecision.ALLOW,
);
// Should NOT match with unqualified name but NO serverName
expect((await engine.check({ name: 'tool' }, undefined)).decision).toBe(
PolicyDecision.ASK_USER,
);
// Should NOT match with unqualified name but WRONG serverName
expect(
(await engine.check({ name: 'tool' }, 'wrong-server')).decision,
).toBe(PolicyDecision.ASK_USER);
});
it('should match by args pattern', async () => {
const rules: PolicyRule[] = [
{
+15 -9
View File
@@ -310,16 +310,22 @@ export class PolicyEngine {
let matchedRule: PolicyRule | undefined;
let decision: PolicyDecision | undefined;
// For tools with a server name, we want to try matching both the
// original name and the fully qualified name (server__tool).
const toolCallsToTry: FunctionCall[] = [toolCall];
if (serverName && toolCall.name && !toolCall.name.includes('__')) {
toolCallsToTry.push({
...toolCall,
name: `${serverName}__${toolCall.name}`,
});
}
for (const rule of this.rules) {
if (
ruleMatches(
rule,
toolCall,
stringifiedArgs,
serverName,
this.approvalMode,
)
) {
const match = toolCallsToTry.some((tc) =>
ruleMatches(rule, tc, stringifiedArgs, serverName, this.approvalMode),
);
if (match) {
debugLogger.debug(
`[PolicyEngine.check] MATCHED rule: toolName=${rule.toolName}, decision=${rule.decision}, priority=${rule.priority}, argsPattern=${rule.argsPattern?.source || 'none'}`,
);