How the ToolMonitor Validates Claude Tool Calls Against Predefined Allowlists
The ToolMonitor class in src/claude/monitor.py validates Claude tool calls through a three-stage process: checking allowlists, checking disallowlists, and providing a fast-path boolean helper, with all violations logged to security_violations and returned as error messages.
The RichardAtCT/claude-code-telegram repository implements a security layer that intercepts AI-generated tool executions before they reach the system. Understanding how to validate Claude tool calls against predefined allowlists is essential for securing autonomous coding agents against unauthorized file system or shell access.
Three-Stage Validation Process
The ToolMonitor class orchestrates validation through sequential checks defined in the validate_tool_call() method. Each stage operates independently, with early exits triggered only when the DISABLE_TOOL_VALIDATION environment variable is set to true.
Allowlist Enforcement
If the claude_allowed_tools configuration is defined, the monitor strictly permits only tools present in that list. In src/claude/monitor.py at lines 75–81, the guard clause checks if not self.disable_tool_validation and hasattr(self.config, "claude_allowed_tools"). The actual membership test occurs on line 80: if tool_name not in self.config.claude_allowed_tools:. When a tool is absent from the allowlist, the monitor records a disallowed_tool violation in self.security_violations and returns (False, error_message).
Disallowlist Blocking
Conversely, if claude_disallowed_tools is configured, the monitor ensures the requested tool name is not present in that list. Lines 84–90 implement this check with the condition if tool_name in self.config.claude_disallowed_tools:. A match here generates an explicitly_disallowed_tool violation and rejects the call immediately. This dual-list approach allows administrators to operate in either explicit-allow or explicit-deny modes depending on security requirements.
Fast-Path Helper for Pre-flight Checks
The is_tool_allowed() method (lines 106–122) provides a lightweight boolean check that reproduces the allowlist and disallowlist lookups without invoking the full validation flow. Other components use this helper when they need to determine permission status without processing path safety checks or logging full violations.
# Quick boolean check without full validation
if monitor.is_tool_allowed("read_file"):
print("read_file is permitted by the allowlist")
Configuration and Environment Variables
Validation behavior is controlled through the Settings class defined in src/config/settings.py. The system recognizes two primary configuration vectors:
- Allowlist:
CLAUDE_ALLOWED_TOOLSenvironment variable (e.g.,"read_file,write_file,bash") - Disallowlist:
CLAUDE_DISALLOWED_TOOLSenvironment variable (e.g.,"shell")
When DISABLE_TOOL_VALIDATION is set to true, the monitor sets self.disable_tool_validation to True, causing both list checks to be skipped entirely (lines 66–73). Note that path and command safety validations remain active regardless of this setting, as they protect the file system independently of tool-name validation.
# Example: configuring allow- and disallow-lists via Settings (env-vars or .env)
# CLAUDE_ALLOWED_TOOLS="read_file,write_file,bash"
# CLAUDE_DISALLOWED_TOOLS="shell"
from src.config.settings import Settings
from src.claude.monitor import ToolMonitor
settings = Settings() # loads env-vars
monitor = ToolMonitor(settings) # instantiate the monitor
# Simulate a Claude tool call
tool_name = "bash"
tool_input = {"command": "ls -l /tmp"}
allowed, error = await monitor.validate_tool_call(
tool_name,
tool_input,
working_directory=Path("/app/workdir"),
user_id=12345,
)
if allowed:
print("Tool call approved")
else:
print(f"Rejected: {error}")
Violation Handling and Logging
All validation failures are appended to self.security_violations (see lines 82–90 and 94–101) and logged using structlog for audit trails. The validate_tool_call() method returns a tuple of (bool, Optional[str]), where the boolean indicates approval status and the optional string contains the specific rejection reason. This design allows calling code to distinguish between security violations and system errors while maintaining detailed forensic logs.
Summary
- The
ToolMonitorclass insrc/claude/monitor.pyimplements allowlist and disallowlist validation for Claude-generated tool calls. - Allowlist checks (lines 75–81) reject any tool not explicitly listed in
claude_allowed_tools. - Disallowlist checks (lines 84–90) reject tools specifically forbidden in
claude_disallowed_tools. - Setting
DISABLE_TOOL_VALIDATION=truebypasses name-based checks (lines 66–73) but preserves path safety validations. - The
is_tool_allowed()method (lines 106–122) offers a lightweight boolean pre-check for components needing quick permission lookups. - All violations are stored in
security_violationsand logged viastructlog, with errors returned as(False, error_message)tuples.
Frequently Asked Questions
How do I configure which tools Claude is allowed to execute?
Set the CLAUDE_ALLOWED_TOOLS environment variable to a comma-separated list of permitted tool names (e.g., read_file,write_file,bash). The Settings class in src/config/settings.py loads this value, and the ToolMonitor enforces it during the allowlist check at lines 75–81 of src/claude/monitor.py.
What happens if a tool appears on both the allowlist and disallowlist?
The disallowlist check (lines 84–90) executes after the allowlist check (lines 75–81). If a tool name appears in both lists, the allowlist check passes first, but the subsequent disallowlist check detects the conflict and rejects the call with an explicitly_disallowed_tool violation. The disallowlist acts as a final override.
Can I disable tool validation entirely without removing the configuration?
Yes. Set the environment variable DISABLE_TOOL_VALIDATION=true. This sets self.disable_tool_validation to True, causing the monitor to skip both the allowlist and disallowlist checks (lines 66–73). However, path and command safety validations in src/security/validators.py remain active to protect the file system.
What is the difference between validate_tool_call() and is_tool_allowed()?
validate_tool_call() performs complete validation including allowlist/disallowlist checks, path safety validation, and violation logging, returning a (bool, Optional[str]) tuple. is_tool_allowed() (lines 106–122) provides a fast-path boolean check that only evaluates the list memberships without side effects, suitable for UI pre-checks or permission previews.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →