* Check if the entire tool matches a rule * For example, this matches "Bash" but not "Bash(prefix:*)" for BashTool * This also matches MCP tools with a server name, e.g. the rule "mcp__server1"
( tool: Pick<Tool, 'name' | 'mcpInfo'>, rule: PermissionRule, )
| 236 | * This also matches MCP tools with a server name, e.g. the rule "mcp__server1" |
| 237 | */ |
| 238 | function toolMatchesRule( |
| 239 | tool: Pick<Tool, 'name' | 'mcpInfo'>, |
| 240 | rule: PermissionRule, |
| 241 | ): boolean { |
| 242 | // Rule must not have content to match the entire tool |
| 243 | if (rule.ruleValue.ruleContent !== undefined) { |
| 244 | return false |
| 245 | } |
| 246 | |
| 247 | // MCP tools are matched by their fully qualified mcp__server__tool name. In |
| 248 | // skip-prefix mode (CLAUDE_AGENT_SDK_MCP_NO_PREFIX), MCP tools have unprefixed |
| 249 | // display names (e.g., "Write") that collide with builtin names; rules targeting |
| 250 | // builtins should not match their MCP replacements. |
| 251 | const nameForRuleMatch = getToolNameForPermissionCheck(tool) |
| 252 | |
| 253 | // Direct tool name match |
| 254 | if (rule.ruleValue.toolName === nameForRuleMatch) { |
| 255 | return true |
| 256 | } |
| 257 | |
| 258 | // MCP server-level permission: rule "mcp__server1" matches tool "mcp__server1__tool1" |
| 259 | // Also supports wildcard: rule "mcp__server1__*" matches all tools from server1 |
| 260 | const ruleInfo = mcpInfoFromString(rule.ruleValue.toolName) |
| 261 | const toolInfo = mcpInfoFromString(nameForRuleMatch) |
| 262 | |
| 263 | return ( |
| 264 | ruleInfo !== null && |
| 265 | toolInfo !== null && |
| 266 | (ruleInfo.toolName === undefined || ruleInfo.toolName === '*') && |
| 267 | ruleInfo.serverName === toolInfo.serverName |
| 268 | ) |
| 269 | } |
| 270 | |
| 271 | /** |
| 272 | * Check if the entire tool is listed in the always allow rules |
no test coverage detected