Skip to content

Permission Decisions Example

Use structured ALLOW/DENY/ASK decisions for tool authorization.

File Access Control

Python
from pydantic_ai_middleware import (
    AgentMiddleware,
    ToolDecision,
    ToolPermissionResult,
)

class FileAccessControl(AgentMiddleware[None]):
    """Control file operations with structured permissions."""

    SAFE_DIRS = {"/tmp", "/workspace"}
    DANGEROUS_TOOLS = {"delete_file", "write_file"}

    async def before_tool_call(self, tool_name, tool_args, deps, ctx):
        path = tool_args.get("path", "")

        # Always allow reads
        if tool_name == "read_file":
            return ToolPermissionResult(decision=ToolDecision.ALLOW)

        # Block writes outside safe directories
        if not any(path.startswith(d) for d in self.SAFE_DIRS):
            return ToolPermissionResult(
                decision=ToolDecision.DENY,
                reason=f"Cannot write to {path} - not in safe directories",
            )

        # Ask for confirmation on destructive operations
        if tool_name in self.DANGEROUS_TOOLS:
            return ToolPermissionResult(
                decision=ToolDecision.ASK,
                reason=f"{tool_name}: {path}",
            )

        return tool_args

Permission Handler

Python
async def cli_approval(tool_name, tool_args, reason):
    """Simple CLI-based approval."""
    print(f"\n--- Permission Required ---")
    print(f"Tool: {tool_name}")
    print(f"Reason: {reason}")
    response = input("Allow? (y/n): ")
    return response.lower() == "y"

agent = MiddlewareAgent(
    agent=base_agent,
    middleware=[FileAccessControl()],
    permission_handler=cli_approval,
)

Role-Based Permissions

Python
from dataclasses import dataclass

@dataclass
class UserDeps:
    user_id: str
    roles: set[str]

class RoleBasedAccess(AgentMiddleware[UserDeps]):
    TOOL_ROLES = {
        "send_email": {"admin", "support"},
        "delete_file": {"admin"},
        "execute_code": {"admin", "developer"},
    }

    async def before_tool_call(self, tool_name, tool_args, deps, ctx):
        if deps is None:
            return ToolPermissionResult(
                decision=ToolDecision.DENY,
                reason="No user context",
            )

        required = self.TOOL_ROLES.get(tool_name)
        if required is None:
            return tool_args  # no restrictions

        if deps.roles & required:
            return ToolPermissionResult(decision=ToolDecision.ALLOW)

        return ToolPermissionResult(
            decision=ToolDecision.DENY,
            reason=f"User {deps.user_id} lacks roles: {required}",
        )

Source: examples/permission_decisions.py