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:
- 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.
- Admin-configurable tool policies. Enterprise deployments need administrators to control which tools agents can access. A tool browser UI backed by
BuiltInToolCatalogandIToolMetadatalets admins see every tool's risk level and side effects, then build aToolPermissionPolicyfrom 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.
What to Read Next
- Equip an Agent with Built-In Tools: register tools with agents
- Secure Agent Tool Access with Permission Policies: allow/deny rules
- Intercept and Control Tool Invocations: runtime interception
- AI Agent Tools: tool concepts