How to Use ToolPolicyContext for Policy Decisions in AISuite

ToolPolicyContext is the data structure that AISuite passes to every tool policy before executing a tool call, exposing fields like tool_name, tool_arguments, and agent_state so your policy can return a boolean or a ToolPolicyDecision to allow or deny the operation.

When building AI agents with AISuite, controlling which tools the LLM can invoke is critical for security and compliance. The framework provides a robust policy evaluation mechanism centered around the ToolPolicyContext class defined in aisuite/agents/types.py, which aggregates all relevant metadata about an impending tool execution. By leveraging this context object, developers can implement fine-grained access controls that inspect everything from the tool's arguments to the current conversation state.

What Is ToolPolicyContext?

In the AISuite architecture, ToolPolicyContext serves as the single source of truth for policy decisions. According to the type definitions in aisuite/agents/types.py, this dataclass encapsulates the complete execution context available to a policy at decision time.

The context exposes the following fields:

  • tool_name: The identifier of the tool being invoked.
  • tool_metadata: Static configuration from the tool definition, including description, parameters, and custom policy tags.
  • tool_arguments: The actual arguments generated by the LLM for this specific call.
  • agent_state: A snapshot of the current agent context, including conversation history and variables.
  • request_id: A unique identifier for the LLM request that triggered the tool call.
  • user_id: An optional identifier for the end-user, enabling multi-tenant policy enforcement.

How Policy Evaluation Works

When a tool call is imminent, AISuite constructs a ToolPolicyContext instance and routes it through the internal _evaluate_tool_policy function in aisuite/utils/tools.py. As implemented in andrewyng/aisuite, this evaluation follows a strict precedence:

  1. No policy configured: The tool executes immediately without checks.
  2. Callable policy: The function receives the context and must return a bool (True allows, False denies).
  3. Object-based policy: The object’s evaluate(self, context: ToolPolicyContext) method is called, which may return either a boolean or a ToolPolicyDecision object.

If the policy denies the call, AISuite inserts the error message "Tool call denied by policy" into the LLM response instead of executing the tool. Every decision is recorded as a policy event in last_policy_events, enabling downstream tracing and audit trails.

Implementing Tool Policies with ToolPolicyContext

Simple Callable Policies

The fastest way to restrict tool access is a plain function that inspects the tool_name field. This approach works well for allowlists or simple denials.

import aisuite as ai

def allow_only_read_file(context: ai.ToolPolicyContext) -> bool:
    """Allow only the read_file tool; reject all others."""
    return context.tool_name == "read_file"

# Apply when creating a runner

runner = ai.AgentRunner(
    agent=my_agent,
    tool_policy=allow_only_read_file,
)

Rich Decisions with ToolPolicyDecision

For policies requiring explanatory messages or complex logic, implement a class that returns ToolPolicyDecision. This is defined alongside ToolPolicyContext in aisuite/agents/types.py.

import aisuite as ai

class ApprovalPolicy:
    """Requires human approval for tools tagged as sensitive."""
    def __init__(self, callback):
        self.callback = callback
    
    def evaluate(self, context: ai.ToolPolicyContext) -> ai.ToolPolicyDecision:
        # Check metadata for policy tags

        if context.tool_metadata.get("policy") == "requires_approval":
            approved = self.callback(context)
            return ai.ToolPolicyDecision(
                allowed=approved,
                reason="approved by user" if approved else "approval denied"
            )
        return ai.ToolPolicyDecision(allowed=True)

# Usage

client = ai.Client(tool_policy=ApprovalPolicy(my_approval_fn))

Inspecting Agent State and Arguments

Sophisticated policies can enforce role-based access control by examining agent_state or validate inputs via tool_arguments.

import aisuite as ai

def admin_only_shell(context: ai.ToolPolicyContext) -> bool:
    """Block shell commands unless the user has admin role."""
    if context.tool_name == "exec_shell":
        user_role = context.agent_state.get("user_role", "guest")
        return user_role == "admin"
    return True

client = ai.Client(tool_policy=admin_only_shell)

Core Implementation Files

Understanding the source layout helps when debugging policy behavior or extending the framework:

Summary

  • ToolPolicyContext aggregates tool metadata, arguments, agent state, and request IDs into a single object passed to every policy check.
  • Policies can be simple callables returning booleans or class-based implementations returning ToolPolicyDecision objects with explicit reasons.
  • The evaluation logic in aisuite/utils/tools.py automatically records all decisions as policy events for auditing.
  • When denied, AISuite returns the specific error message "Tool call denied by policy" to the LLM instead of executing the tool.
  • Access agent_state for conversation-aware policies and tool_metadata for configuration-driven rules.

Frequently Asked Questions

What fields does ToolPolicyContext expose?

ToolPolicyContext provides tool_name, tool_metadata, tool_arguments, agent_state, request_id, and optional user_id. These fields are defined in aisuite/agents/types.py and populated by the framework before each policy check.

Can a policy return a custom denial message?

Yes. Instead of returning a boolean, return a ToolPolicyDecision object with allowed=False and a custom reason string. AISuite includes this reason in the last_policy_events log, though the LLM receives the standard "Tool call denied by policy" message.

How do I access the conversation history inside a policy?

Inspect the agent_state field of the ToolPolicyContext. This dictionary contains the current agent context, including conversation history and any variables set by the agent, allowing you to make decisions based on the full interaction state.

Where are policy decisions logged?

Each evaluation result is stored as a policy event in last_policy_events, accessible on the runner or client instance. This audit trail captures the decision, timestamp, and reason, which is essential for debugging and compliance monitoring.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →