Table of Contents

Understanding Tool Permission Policies in LM-Kit.NET


TL;DR

Tool Permission Policies are runtime access-control rules that govern which tools an AI agent is allowed to call, which tools require human approval, and which tools are denied outright. In LM-Kit.NET, the ToolPermissionPolicy class provides a fluent API for defining allow, deny, and approval-required rules by tool name, wildcard pattern, category, or risk level. Combined with IToolMetadata (which exposes each tool's category, side effects, risk level, and idempotency) and the ToolApprovalRequired event for human-in-the-loop workflows, permission policies enable enterprise-grade governance over agent tool access.


What are Tool Permission Policies?

Definition: A Tool Permission Policy is a set of rules that determine, at runtime, whether an agent is allowed to execute a given tool. Before every tool invocation, the policy evaluates the tool against its rules and returns one of three outcomes: Allowed, Denied, or ApprovalRequired. This evaluation happens before any filter or tool code executes, making it the first line of defense.

Why Permission Policies Matter

  1. Security by Default: Prevent agents from calling dangerous tools (file deletion, process execution, email sending) unless explicitly authorized.
  2. Least Privilege: Grant agents access only to the tools they need. A customer-support agent does not need file system access. A data analysis agent does not need network tools.
  3. Human Oversight: Flag sensitive operations for human approval without blocking safe tool calls. This is critical for production deployments where autonomous agents handle real-world actions.
  4. Compliance: Meet organizational and regulatory requirements by auditing and controlling every tool invocation.
  5. Defense in Depth: Permission policies work alongside guardrails and filters, each enforcing safety at a different level of the pipeline.

The Policy Evaluation Model

Three Possible Outcomes

Every tool call passes through ToolPermissionPolicy.Evaluate(tool), which returns a ToolPermissionResult:

Result Meaning What Happens
Allowed Tool call proceeds automatically Tool executes, result returns to model
Denied Tool call is blocked A ToolCallResultType.Denied message tells the model why
ApprovalRequired Human must approve or deny ToolApprovalRequired event fires; denied if no handler

Evaluation Order

The policy evaluates rules in a strict priority order:

1. Explicit deny by name         (highest priority)
2. Deny by wildcard pattern
3. Deny by category
4. Risk level gate               (tool.RiskLevel > MaxRiskLevel)
5. Approval by name
6. Approval by wildcard pattern
7. Approval by category
8. Allow by name
9. Allow by wildcard pattern
10. Allow by category
11. DefaultAction                 (lowest priority: Allow or Deny)

This means a deny always wins over an allow, and an approval requirement wins over an implicit allow. This design ensures that restrictive rules cannot be accidentally overridden by permissive ones.


Tool Metadata: The Foundation for Policies

Every LM-Kit.NET built-in tool implements IToolMetadata, which exposes security-relevant properties that the policy engine uses for evaluation:

Property Type Description
Category string Tool family: "data", "document", "io", "net", "numeric", "security", "text", "utility"
SideEffect ToolSideEffect What the tool does to the world: None, LocalRead, LocalWrite, NetworkRead, NetworkWrite, Irreversible
RiskLevel ToolRiskLevel How dangerous the tool is: Low, Medium, High, Critical
DefaultApproval ToolApprovalMode Recommended approval stance: Never, Conditional, Always
IsIdempotent bool Whether calling the tool twice produces the same result
IsReadOnly bool Whether the tool has no side effects on external state

Custom tools that implement IToolMetadata automatically benefit from category-level and risk-level policy rules.


Practical Application in LM-Kit.NET SDK

Building a Permission Policy

The ToolPermissionPolicy class uses a fluent API. All methods return this for chaining:

using LMKit.Agents.Tools;
using LMKit.Agents.Tools.BuiltIn;

// Whitelist mode: deny everything by default, then allow selectively
var strictPolicy = new ToolPermissionPolicy()
{
    DefaultAction = ToolPermissionAction.Deny
};

strictPolicy
    .AllowCategory("data", "text", "numeric", "utility")  // Safe categories
    .Allow("websearch")                                     // Allow one net tool
    .RequireApproval("filesystem_*")                        // Require approval for all filesystem tools
    .Deny("filesystem_delete");                             // Always deny file deletion

// Permissive mode: allow everything, then restrict
var permissivePolicy = new ToolPermissionPolicy()
    .DenyCategory("io")                                     // Block all IO tools
    .Deny("process_*")                                      // Block all process tools
    .SetMaxRiskLevel(ToolRiskLevel.Medium)                  // Block High and Critical tools
    .RequireApprovalForCategory("net");                     // Require approval for network tools

Attaching a Policy to an Agent

using LMKit.Model;
using LMKit.Agents;
using LMKit.Agents.Tools;
using LMKit.Agents.Tools.BuiltIn;

var model = LM.LoadFromModelID("glm4.7-flash");

var policy = new ToolPermissionPolicy()
    .AllowCategory("data", "text", "numeric", "utility", "security")
    .Allow("filesystem_read", "filesystem_list")
    .Deny("filesystem_delete", "filesystem_write")
    .RequireApproval("http_*")
    .SetMaxRiskLevel(ToolRiskLevel.High);

var agent = Agent.CreateBuilder(model)
    .WithTools(tools =>
    {
        // Register all built-in tools
        foreach (var tool in BuiltInTools.GetAll())
            tools.Register(tool);
    })
    .WithPermissionPolicy(policy)
    .Build();

Human-in-the-Loop Approval

using LMKit.Agents;
using LMKit.TextGeneration.Events;

using var executor = new AgentExecutor();

// Subscribe to the approval event
executor.ToolApprovalRequired += (sender, e) =>
{
    Console.WriteLine($"Tool: {e.ToolCall.Name}");
    Console.WriteLine($"  Risk:   {e.RiskLevel}");
    Console.WriteLine($"  Effect: {e.SideEffect}");
    Console.Write("Approve? (y/n): ");

    if (Console.ReadLine()?.Trim().ToLower() == "y")
    {
        e.Approved = true;
    }
    else
    {
        e.Approved = false;
        e.DenialReason = "User declined the operation.";
    }
};

// If no handler is subscribed, ApprovalRequired tools are denied by default

Attaching a Policy to a Conversation

using LMKit.Model;
using LMKit.TextGeneration;
using LMKit.Agents.Tools;

var model = LM.LoadFromModelID("gemma3:12b");
using var chat = new MultiTurnConversation(model);

// Set the policy directly on the tool registry
chat.Tools.PermissionPolicy = new ToolPermissionPolicy()
    .AllowCategory("data", "numeric")
    .Deny("*"); // Deny everything else

// Subscribe to approval events on the conversation
chat.ToolApprovalRequired += (sender, e) =>
{
    Console.Write($"Allow {e.ToolCall.Name}? ");
    e.Approved = Console.ReadLine()?.Trim() == "y";
};

Common Policy Patterns

Safe Chat Profile

Only pure computation tools, no external effects:

var chatPolicy = new ToolPermissionPolicy()
    .AllowCategory("data", "text", "numeric", "utility", "security")
    .DenyCategory("io", "net")
    .SetMaxRiskLevel(ToolRiskLevel.Low);

Developer Assistant Profile

Read access to files and web, approval for writes:

var devPolicy = new ToolPermissionPolicy()
    .AllowCategory("data", "text", "numeric", "utility", "security")
    .Allow("filesystem_read", "filesystem_list", "filesystem_search")
    .Deny("filesystem_delete")
    .RequireApproval("filesystem_write", "filesystem_move", "filesystem_copy")
    .Allow("http_get", "http_head", "websearch")
    .Deny("http_post", "http_put", "http_delete")
    .RequireApproval("process_*");

Document Processing Profile

Document tools plus read-only IO:

var docPolicy = new ToolPermissionPolicy()
{
    DefaultAction = ToolPermissionAction.Deny
};

docPolicy
    .AllowCategory("data", "document", "text")
    .Allow("filesystem_read")
    .SetMaxRiskLevel(ToolRiskLevel.Medium);

Policy, Filters, and Guardrails: How They Relate

User prompt
     |
     v
+---------------------------+
|  Prompt Filters           |  (content moderation, RAG injection)
+---------------------------+
     |
     v
  [LLM Inference]
     |
     v  (model requests a tool call)
+---------------------------+
|  Permission Policy        |  <-- Evaluates FIRST: Allow / Deny / ApprovalRequired
+---------------------------+
     |
     v  (if Allowed or Approved)
+---------------------------+
|  Tool Invocation Filters  |  (logging, rate limiting, caching)
+---------------------------+
     |
     v
  [Tool Execution]
     |
     v
+---------------------------+
|  Completion Filters       |  (output validation, moderation)
+---------------------------+
     |
     v
Response

The permission policy is the gatekeeper. Filters provide observability and transformation. Guardrails are the broader safety strategy that encompasses both.


Key Terms

  • ToolPermissionPolicy: The class that defines allow, deny, and approval rules for tool access.
  • ToolPermissionResult: The outcome of policy evaluation: Allowed, Denied, or ApprovalRequired.
  • ToolPermissionAction: The default policy stance: Allow (permissive) or Deny (whitelist mode).
  • ToolRiskLevel: Classification of tool danger: Low, Medium, High, Critical.
  • ToolSideEffect: What the tool does to external state: None, LocalRead, LocalWrite, NetworkRead, NetworkWrite, Irreversible.
  • ToolApprovalMode: Default approval recommendation: Never, Conditional, Always.
  • IToolMetadata: Interface exposing security-relevant metadata used by the policy engine.
  • ToolApprovalRequired: Event that fires when a tool call needs human approval; denied if no handler is subscribed.
  • Wildcard Pattern: Name matching with * and ? for domain-level control (e.g., filesystem_* matches all filesystem tools).



External Resources


Summary

Tool Permission Policies are the access-control layer that governs which tools an AI agent can invoke. In LM-Kit.NET, the ToolPermissionPolicy class provides a fluent API supporting Allow(), Deny(), RequireApproval(), category-level rules, wildcard patterns, and risk-level gates. Every built-in tool exposes IToolMetadata with its category, side effect type, risk level, and approval mode, enabling fine-grained, metadata-driven governance. When a policy returns ApprovalRequired, the ToolApprovalRequired event enables human-in-the-loop workflows. Combined with filters and guardrails, permission policies form the security backbone of production agent deployments, enforcing the principle of least privilege across every tool invocation.

Share