Custom tools
Implement ITool or annotate any method with [LMFunction]. Schemas are inferred from your types.
A model that can only talk is a chat box. An agent earns its name when
it can call functions, query databases, parse PDFs, search the web, and
write to disk. LM-Kit.NET ships 70+ built-in tools across 8 categories,
a typed ITool interface, and [LMFunction]
attribute binding so any C# method becomes callable in seconds. All on-device.
Implement ITool or annotate any method with [LMFunction]. Schemas are inferred from your types.
HTTP, file system, web search, PDF, OCR, calculator, regex, JSON, SQL, and more, ready to register.
Every tool ships with metadata: category, side-effect, risk level, idempotence. Drives ToolPermissionPolicy.
A naive function-calling agent looks easy until you ship it. The hard parts hit later: schema generation, argument validation, permission gates, error recovery, observability, and grammar-constrained decoding so the model cannot emit malformed JSON. LM-Kit handles each one.
Method parameters become a JSON Schema automatically. No hand-written tool definitions, no drift between your code and the prompt.
When the model is choosing a tool, it cannot emit invalid JSON. The grammar is built from the registered tools at decode time. No retry loops on malformed calls.
One tool, one feature. filesystem_read, filesystem_write, http_get, http_post. Wildcard policies (filesystem_*) get natural domain-level control.
IToolMetadata exposes SideEffect, RiskLevel, IsReadOnly, IsIdempotent. Decisions become policy, not vibes.
BeforeToolInvocation and AfterToolInvocation events let you log, override, or cancel a call before it runs.
Tools accept CancellationToken. The agent execution loop respects them. No more agent runs hanging on a slow HTTP call.
Pick the friction level that matches your tool. Reach for
[LMFunction] for one-line bindings of existing methods,
or implement ITool when you need full control over schema,
streaming, side-effect declaration and metadata.
Annotate any C# method with [LMFunction] and register the whole class in one call. Schema is inferred from parameter types.
using LMKit.Agents.Tools; // Annotate any C# method. The agent sees its name, description, and inferred schema. public class WeatherTools { [LMFunction(Name = "get_current_weather", Description = "Returns current weather for a city. Units default to celsius.")] public WeatherReport GetCurrentWeather(string city, string units = "c") { // Existing business logic. Untouched by LM-Kit. return _weatherService.Lookup(city, units); } } // Register the whole class with one call. var agent = Agent.CreateBuilder(model) .WithTools(t => t.AddFromInstance(new WeatherTools())) .Build();
Implement ITool + IToolMetadata when you need explicit schema, streaming, side-effect declarations, and approval semantics.
using LMKit.Agents.Tools; // Full control: explicit schema, streaming, custom metadata, permissions. public sealed class DatabaseQueryTool : ITool, IToolMetadata { public string Name => "db_query"; public string Description => "Run a parameterised SQL query against the prod read replica."; public string Category => "data"; public ToolSideEffect SideEffect => ToolSideEffect.NetworkRead; public ToolRiskLevel RiskLevel => ToolRiskLevel.Medium; public bool IsReadOnly => true; public bool IsIdempotent => true; public ToolApprovalMode DefaultApproval => ToolApprovalMode.Conditional; public string InputSchema => """ { "type": "object", "properties": { "query": { "type": "string", "description": "Parameterised SQL" }, "args": { "type": "array", "items": { "type": "string" } } }, "required": ["query"] } """; public async Task<ToolCallResult> InvokeAsync(ToolCallArgs args, CancellationToken ct) { var sql = args.GetString("query"); var rows = await _db.QueryAsync(sql, args.GetArray("args"), ct); return ToolCallResult.Success(rows.ToJson()); } }
Atomic by design: one tool equals one feature. New tools are added in
every release, so do not hard-code the count. Each category exposes
a group registration helper (tools.AddDataTools(),
tools.AddNetTools(), etc.).
data
JSON, XML, CSV, YAML, HTML, Markdown parsing and transformation. Spreadsheets, SQLite databases, QR codes, base64, hex.
document
PDF read, split, merge, page extraction. Image preprocessing, native OCR. Markdown / EML / HTML / DOCX conversion.
text
Diff, regex, templating, encoding, slugification, fuzzy matching, phonetics, Unicode normalisation, case operations.
numeric
Calculator, expression evaluator, unit conversion, statistics, financial math, random numbers.
security
Hashing (MD5, SHA, BLAKE), symmetric encryption, JWT issue/verify, password generation, checksums, validation primitives.
utility
Date/time arithmetic, cron expressions, URL parsing, color manipulation, locale, MIME types, paths, scheduling, time zones.
io
File system read/write/list/copy/move/delete, process execution, compression (zip, tar, gzip), clipboard, environment variables, file watching.
net
HTTP (GET/POST/PUT/PATCH/DELETE/HEAD), FTP, web search (DuckDuckGo, Brave, Tavily, Serper, SearXNG), network diagnostics, SMTP, RSS feeds.
Switch search backends without changing your agent. Free providers for hobby projects, premium providers for production scale.
DuckDuckGo
Default provider. HTML scraping, no auth, good for prototyping.
Brave
Curated index, generous free tier, simple API key auth.
Tavily
Designed for AI workloads. Returns clean, citation-friendly content.
Serper
Google search via API. High quality, predictable pricing.
SearXNG
Run your own meta-search instance. Zero third-party dependency.
Custom
Implement the provider interface to plug in any internal search backend.
For one-shot natural-language-to-method routing without the overhead of
a planning loop, SingleFunctionCall is the lightweight path.
Hand it a prompt and a method registry. It picks one, extracts arguments,
and optionally invokes.
using LMKit.FunctionCalling; var caller = new SingleFunctionCall(model); caller.Plugins.Add(new BookPlugin()); // One prompt, one method selected, arguments extracted, return value bound. FunctionCallResult r = await caller.InvokeAsync( "Find me a thriller written by an author whose surname starts with K."); Console.WriteLine(r.Method.Name); // SearchBooks Console.WriteLine(r.Confidence); // 0.92
Python-first. No first-class .NET story. No standardised security metadata. Tools live in scattered community packages.
Plugins exist but are opinionated toward Microsoft Graph and Azure OpenAI. Local model integration is secondary; tool catalog is sparse.
Native .NET, 70+ atomic tools, typed metadata for permission policies, grammar-constrained decoding so the model cannot emit invalid JSON.
Allow, deny, or require approval per tool, category, or risk level. Centralised security for tool calls.
Connect to any MCP server (GitHub, Slack, internal services) and register its tools alongside your local ones.
Intercept tool invocations with ASP.NET-style middleware. Log, redact, rate-limit, validate.
Grammar-constrained generation underpins reliable tool calling. Same primitive, different audience.
Working console demos on GitHub, step-by-step how-to guides on the docs site, and the API reference for the classes used on this page.
Agent demo: custom and built-in tools registered to an agent.
Open on GitHub → DemoConsole demo: function-calling at the conversation level.
Open on GitHub → How-to guideBrowse the catalog and register tools by category.
Read the guide → How-to guideCatalog of 8 tool families: data, document, IO, net, numeric, security, text, utility.
Read the guide →