Table of Contents

Discover and Browse Built-In Tools Programmatically

LM-Kit.NET ships a growing catalog of built-in tools. Rather than hard-coding tool names, your application can discover available tools at runtime, inspect their metadata (category, risk level, side effects), filter them by security criteria, and present them in a UI for users to enable or disable. This guide covers every API you need to build a tool browser, a category selector, or a risk-aware tool registration pipeline.

For background on tools and how they integrate with agents, see the AI Agent Tools glossary entry.


Why This Matters

Two production problems that programmatic tool discovery solves:

  1. Forward-compatible tool registration. Hard-coding tool names means updating your code every time the catalog grows. Discovering tools by category and risk level at runtime means your application automatically picks up new tools without code changes.
  2. Admin-configurable tool policies. Enterprise deployments need administrators to control which tools agents can access. A tool browser UI backed by BuiltInToolCatalog and IToolMetadata lets admins see every tool's risk level and side effects, then build a ToolPermissionPolicy from their selections.

Prerequisites

Requirement Minimum
.NET SDK 8.0+

No model or VRAM is needed for tool discovery. You only need a model when you execute tools through an agent.


Step 1: Create the Project

dotnet new console -n ToolDiscovery
cd ToolDiscovery
dotnet add package LM-Kit.NET

Step 2: Understand the Discovery APIs

Three entry points serve different use cases:

API Returns Best for
BuiltInTools.GetToolInfo() ToolInfo[] Lightweight metadata for UI display
BuiltInToolCatalog.All IReadOnlyList<BuiltInToolDescriptor> Full catalog with category and risk queries
BuiltInTools.GetAll() IEnumerable<ITool> Live tool instances for registration

Step 3: List All Tools with Metadata

GetToolInfo() returns a flat array of ToolInfo objects. Each one contains the tool's name, description, category, risk level, side effects, and security flags.

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

// Get metadata for every built-in tool
ToolInfo[] allTools = BuiltInTools.GetToolInfo();

Console.WriteLine($"Built-in tools available: {allTools.Length}\n");

foreach (ToolInfo tool in allTools)
{
    Console.WriteLine($"{tool.Name,-30} [{tool.Category,-10}] " +
                      $"Risk={tool.RiskLevel,-8} Side={tool.SideEffect,-14} " +
                      $"ReadOnly={tool.IsReadOnly}");
}

ToolInfo properties:

Property Type Purpose
Name string Tool identifier (e.g., "filesystem_read")
Description string What the tool does
Category string Category key (e.g., "io", "data", "net")
SideEffect ToolSideEffect None, LocalRead, LocalWrite, NetworkRead, NetworkWrite, Irreversible
RiskLevel ToolRiskLevel Low, Medium, High, Critical
DefaultApproval ToolApprovalMode Never, Conditional, Always
IsIdempotent bool Safe to retry without side effects
IsReadOnly bool Reads state but never modifies it
HasIOCapabilities bool Interacts with file system or network

Step 4: Discover Categories at Runtime

BuiltInToolCatalog.AvailableCategories returns all category strings present in the catalog. Use this to populate dropdowns or filter panels without hard-coding category names.

IReadOnlyList<string> categories = BuiltInToolCatalog.AvailableCategories;

Console.WriteLine("Tool categories:\n");
foreach (string category in categories)
{
    IReadOnlyList<BuiltInToolDescriptor> tools = BuiltInToolCatalog.ByCategory(category);
    Console.WriteLine($"  {category,-12} ({tools.Count} tools)");
}

The eight current categories:

Category Description
data JSON, XML, CSV, YAML, HTML, databases, QR codes
document PDF, images, OCR, text extraction
text String operations, regex, templating, encoding
numeric Calculation, statistics, unit conversion
security Hashing, encryption, JWT, passwords
utility Date/time, cron, paths, colors, locales
io File system, processes, compression
net HTTP, FTP, web search, SMTP, diagnostics

Step 5: Filter Tools by Risk Level and Side Effects

Build security-aware tool lists for different deployment contexts.

// Safe tools: low risk, read-only
IEnumerable<ITool> safeTools = BuiltInTools.GetByMaxRisk(ToolRiskLevel.Low);
Console.WriteLine($"Low-risk tools: {safeTools.Count()}");

// Read-only tools (no state mutation)
IEnumerable<ITool> readOnlyTools = BuiltInTools.GetReadOnly();
Console.WriteLine($"Read-only tools: {readOnlyTools.Count()}");

// Get safe tools (pure computation, no I/O)
IEnumerable<ITool> pureTools = BuiltInTools.GetSafeTools();
Console.WriteLine($"Pure computation tools: {pureTools.Count()}");

// Filter catalog by risk level
IReadOnlyList<BuiltInToolDescriptor> criticalTools = BuiltInToolCatalog.ByRisk(ToolRiskLevel.Critical);
Console.WriteLine($"Critical-risk tools: {criticalTools.Count}");

Step 6: Build a Tool Browser with Grouping

Combine the APIs to build a categorized, risk-annotated tool browser.

ToolInfo[] tools = BuiltInTools.GetToolInfo();

// Group by category
var grouped = tools
    .GroupBy(t => t.Category)
    .OrderBy(g => g.Key);

foreach (var group in grouped)
{
    Console.ForegroundColor = ConsoleColor.Cyan;
    Console.WriteLine($"\n=== {group.Key.ToUpper()} ===");
    Console.ResetColor();

    // Sort by risk level, then by name
    foreach (ToolInfo tool in group.OrderBy(t => t.RiskLevel).ThenBy(t => t.Name))
    {
        ConsoleColor color = tool.RiskLevel switch
        {
            ToolRiskLevel.Low => ConsoleColor.Green,
            ToolRiskLevel.Medium => ConsoleColor.Yellow,
            ToolRiskLevel.High => ConsoleColor.Red,
            ToolRiskLevel.Critical => ConsoleColor.DarkRed,
            _ => ConsoleColor.Gray
        };

        Console.ForegroundColor = color;
        Console.Write($"  [{tool.RiskLevel,-8}] ");
        Console.ResetColor();
        Console.WriteLine($"{tool.Name,-30} {tool.Description}");
    }
}

Step 7: Register Tools with Fluent Selection

ToolRegistry.AddBuiltIns() with BuiltInToolSelection lets you register tools by category, name inclusion/exclusion, and risk threshold in a single fluent call.

var registry = new ToolRegistry();

registry.AddBuiltIns(selection => selection
    .Category("data")
    .Category("text")
    .Category("numeric")
    .MaxRisk(ToolRiskLevel.Medium)
    .Exclude("process_kill"));

Console.WriteLine($"Registered {registry.Count} tools");

Or use a predefined profile:

var safeRegistry = new ToolRegistry();
safeRegistry.AddBuiltIns(BuiltInProfiles.SafeOnly);
Console.WriteLine($"Safe profile: {safeRegistry.Count} tools");

Step 8: Inspect Individual Tool Metadata via IToolMetadata

Every built-in tool implements IToolMetadata. Cast any ITool to access its metadata.

foreach (ITool tool in BuiltInTools.GetAll())
{
    if (tool is IToolMetadata metadata)
    {
        // Access metadata properties
        string category = metadata.Category;
        ToolSideEffect sideEffect = metadata.SideEffect;
        ToolRiskLevel risk = metadata.RiskLevel;
        ToolApprovalMode approval = metadata.DefaultApproval;
        bool idempotent = metadata.IsIdempotent;
        bool readOnly = metadata.IsReadOnly;

        if (sideEffect == ToolSideEffect.Irreversible)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"WARNING: {tool.Name} has irreversible side effects");
            Console.ResetColor();
        }
    }
}

The IToolMetadata interface:

Property Type Purpose
Category string Category identifier
SideEffect ToolSideEffect What external effects the tool has
RiskLevel ToolRiskLevel Security risk classification
DefaultApproval ToolApprovalMode Whether approval is needed by default
IsIdempotent bool Can be retried safely
IsReadOnly bool Never mutates state

Step 9: Build a Permission Policy from Discovery

Combine tool discovery with ToolPermissionPolicy to let administrators configure access dynamically.

// Example: admin selects categories and max risk via UI
string[] allowedCategories = { "data", "text", "numeric", "utility" };
ToolRiskLevel maxRisk = ToolRiskLevel.Medium;
string[] deniedTools = { "filesystem_delete", "process_kill" };

// Build policy from selections
var policy = new ToolPermissionPolicy
{
    DefaultAction = ToolPermissionAction.Deny
};

policy.AllowCategory(allowedCategories);
policy.SetMaxRiskLevel(maxRisk);

foreach (string denied in deniedTools)
{
    policy.Deny(denied);
}

// Preview what the policy allows
foreach (BuiltInToolDescriptor descriptor in BuiltInToolCatalog.All)
{
    ITool tool = BuiltInTools.GetAll().FirstOrDefault(t => t.Name == descriptor.Name);
    if (tool != null)
    {
        ToolPermissionResult result = policy.Evaluate(tool);
        if (result == ToolPermissionResult.Allowed)
        {
            Console.WriteLine($"  ALLOWED: {descriptor.Name} [{descriptor.Category}]");
        }
    }
}

For the full permission policy API, see Secure Agent Tool Access with Permission Policies.


Share