Skip to content

What are Tools?

  1. Understand what are Agents
  2. Learn what are LLMs and the Messages system
  3. Study Tools and Actions
  4. Learn the Agent workflow
  5. Code your first Agent
  6. Unit Quiz

One crucial aspect of AI Agents is their ability to take actions. As we saw, this happens through Tools.

In this section you’ll learn what a Tool is, how the model uses it and how we describe it to the model via the system message. At the end you’ll write a tool in C# and run it on your own computer, with no model needed.

A Tool is a function given to the LLM. It should have a clear objective. For a C# developer it is an ordinary method.

Here are some commonly used tools:

Tool Description
Web Search Fetches up-to-date information from the internet.
Image Generation Creates images from a text description.
Retrieval Retrieves information from an external source.
API Interface Works with an external API (GitHub, YouTube, Spotify, etc.).

Those are only examples. You can create a tool for any task.

A good tool complements the power of an LLM, meaning it does something the model cannot do by itself or does not do well:

  • Exact calculations. The model predicts text, it does not compute. A calculator tool gives the right result every time.
  • Up-to-date data. The model knows only what was in its training data. If you ask it for today’s weather and it has no tool for that, it will probably make up a forecast. This is called a hallucination.

Weather

An LLM can only read and write text. It has no way of calling a method by itself. So the work is split between two parties: the model says what it wants, and the Agent carries it out. Here the Agent is the program around the model.

Say we gave the model a tool for checking the weather, and the user asks what the weather is like in Paris:

  1. The model recognizes that it needs this tool. Instead of an answer it writes text that describes the call, such as GetWeather("Paris").
  2. The Agent reads that text, understands that it is a tool call, and runs the real GetWeather method.
  3. The Agent adds the result to the conversation as a new message and passes the conversation to the model again.
  4. The model reads the result and writes the answer for the user.

The user sees only the last step. To them it looks as if the model checked the weather itself, but all the work was done by the Agent.

The model reads only text. So “giving it a tool” means something simple: we describe the tool in text and put the description into the system prompt.

You are an AI assistant designed to help users efficiently and accurately.
Your primary goal is to provide helpful, precise, and clear responses.
You have access to the following tools:
{tools_description}

The descriptions of our tools take the place of {tools_description}.

What does the model need to know about a tool?

Section titled “What does the model need to know about a tool?”

When you hover over a method in Visual Studio, IntelliSense shows you its name, its parameters with their types, the return type and a short description. You don’t see the method body, and you don’t need it to call the method correctly.

The model needs the same four things:

  1. The name of the tool.
  2. A description of what it does.
  3. The inputs: the names and types of the arguments.
  4. The output: the type of the result.

Let’s take a simple calculator tool that multiplies two integers as an example:

int Calculator(int a, int b) => a * b;

Written as text, the four things look like this:

Tool Name: Calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int

Important: This text is everything the model knows about the tool. It never sees the C# code itself.

This exact format is not mandatory, it could just as well be JSON. What matters is that the description is precise and uses the same format for every tool.

The description can be written by hand, but that is fragile. Rename a parameter, forget to update the text, and the model starts calling the tool incorrectly, with no warning from the compiler. It is better to let the program build the description from the code. That is what we will do now.

We will write the Calculator tool, make the program build its description by itself, and insert it into the system prompt. The program will not call a model.

All you need is the .NET SDK from Unit 0. You don’t need Ollama, a model or NuGet packages.

Open a terminal and run:

Terminal window
dotnet new console -n FirstTool
cd FirstTool

The first command creates a FirstTool folder with a new console application, and the second one moves into it. Open the folder in your editor. In the next steps we will work with three small files.

Create a new file named MyTools.cs:

MyTools.cs
using System.ComponentModel;
public static class MyTools
{
[Description("Multiply two integers.")]
public static int Calculator(int a, int b) => a * b;
}

This is the method from above with one addition: the [Description] attribute. The name, the parameters and the return type are already written in the method itself. Only the description in words was missing, and the attribute adds it.

Why an attribute and not a /// comment? Comments disappear at compile time. Attributes stay in the program, and it can read them while it runs.

Create a second file, Tool.cs. It keeps everything about a tool in one place:

Tool.cs
using System.ComponentModel;
using System.Reflection;
public record Tool(
string Name,
string Description,
Delegate Function,
string Arguments,
string Outputs)
{
public static Tool FromMethod(Delegate function)
{
MethodInfo method = function.Method;
string name = method.Name;
string description =
method.GetCustomAttribute<DescriptionAttribute>()?.Description ?? "";
string arguments = string.Join(", ", method.GetParameters()
.Select(p => $"{p.Name}: {TypeName(p.ParameterType)}"));
string outputs = TypeName(method.ReturnType);
return new Tool(name, description, function, arguments, outputs);
}
public override string ToString() =>
$"Tool Name: {Name}, Description: {Description}, " +
$"Arguments: {Arguments}, Outputs: {Outputs}";
public object? Invoke(params object?[] args) =>
Function.DynamicInvoke(args);
private static string TypeName(Type type) => type.Name switch
{
"Int32" => "int",
"String" => "string",
"Boolean" => "bool",
"Double" => "double",
var other => other,
};
}

The first lines list what a tool consists of: the four things from the description plus the method itself (Function). Below them are three methods:

  • FromMethod creates a Tool from the method you pass in. It reads everything it needs from the method itself, through the MethodInfo object that describes it.
  • ToString lays out the description on one line. This is the text for the model.
  • Invoke runs the method itself. This is what the Agent will do when the model asks for the tool.

Here is where FromMethod gets the four things:

What we need Where it comes from Result for Calculator
Name method.Name Calculator
Description the [Description] attribute Multiply two integers.
Inputs method.GetParameters() a: int, b: int
Output method.ReturnType int

When a program reads information about its own code while it runs, that is called reflection. The TypeName helper only replaces the .NET names Int32 and String with the familiar int and string.

Open Program.cs, delete its contents and put in the following:

Program.cs
Tool calculator = Tool.FromMethod(MyTools.Calculator);
Console.WriteLine(calculator);
Console.WriteLine(calculator.Invoke(6, 7));
string systemPrompt = $"""
You are an AI assistant designed to help users efficiently and accurately.
Your primary goal is to provide helpful, precise, and clear responses.
You have access to the following tools:
{calculator}
""";
Console.WriteLine();
Console.WriteLine(systemPrompt);
// The example ends here.
// The next step would be to send this system prompt to a model.

The program does three things: it creates a Tool from the Calculator method, prints its description and the result of one call, and finally inserts the description into the system prompt.

The comment at the end marks where we stop. The next step would be to send the system prompt to a model, and we don’t have a model yet.

In the terminal, inside the FirstTool folder, run:

Terminal window
dotnet run

output:

Tool Name: Calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int
42
You are an AI assistant designed to help users efficiently and accurately.
Your primary goal is to provide helpful, precise, and clear responses.
You have access to the following tools:
Tool Name: Calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int

Here is how the program arrived at this output:

  1. Tool.FromMethod(MyTools.Calculator) received the method itself, without running it, and read its name, description, parameters and return type.
  2. Console.WriteLine(calculator) called ToString and printed the first line. It is the same description we wrote by hand above, but this time it comes from the code.
  3. calculator.Invoke(6, 7) ran the real Calculator method and returned 42. In an Agent this happens when the model asks for the tool. Here we called it ourselves so you can see that it works.
  4. {calculator} inside systemPrompt called ToString again and put the description where {tools_description} stood in the template above.

Try these two changes and run dotnet run after each one.

Rename the parameters. In MyTools.cs change a and b to x and y:

public static int Calculator(int x, int y) => x * y;

The output now says Arguments: x: int, y: int, in both places. You did not touch a single piece of text: the description comes from the code and cannot drift away from it.

Add a second tool. Add one more method to the MyTools class:

[Description("Get the current weather in a given location.")]
public static string GetWeather(string location) =>
$"the weather in {location} is sunny with low temperatures.";

In Program.cs create a Tool for it as well, below the first line:

Tool weather = Tool.FromMethod(MyTools.GetWeather);

and add it to the system prompt on a new line below {calculator}:

You have access to the following tools:
{calculator}
{weather}
""";

A second tool appears at the end of the output, described in the same format:

Tool Name: GetWeather, Description: Get the current weather in a given location., Arguments: location: string, Outputs: string

You will meet this description again in the Dummy Agent Library, where we will send it to a real model.

The Tool class is a teaching example, but real libraries work the same way. In .NET this is done by AIFunctionFactory from the Microsoft.Extensions.AI library. With AIFunctionFactory.Create(MyTools.Calculator) it also reads the method name, the [Description] attribute and the parameters. The difference is that it describes the inputs as JSON Schema, a format modern models understand.

We’ll use it when building our first Agent. How an Agent calls the tool is covered in the Actions section.

Our Tool class only works inside our own program. Every Agent library has a similar class of its own, so the same tool has to be described again for each of them.

Model Context Protocol (MCP) is an open protocol that standardizes how applications provide tools to LLMs. A tool is implemented once, in a separate program called an MCP server, and any framework that supports MCP can use it. There are already many ready-made MCP servers that your Agent can plug into directly.

In .NET there is an official MCP C# SDK (the ModelContextProtocol NuGet package) that works directly with Microsoft.Extensions.AI. If you want to dive deeper into MCP, check out the free MCP Course by Hugging Face.

  • A Tool is a function that gives an LLM capabilities beyond generating text: exact calculations, up-to-date data, actions in external systems.
  • The model does not run tools. It only writes which tool it wants and with what arguments, and the Agent runs it and returns the result.
  • To use a tool, the model has to know its name, description, inputs and output. That description lives in the system prompt.
  • In C# the description is taken from the method itself through reflection and the [Description] attribute, so it cannot drift away from the code.
  • MCP lets a tool be implemented once and used by different frameworks.

Now we can move on to the Agent Workflow, where you’ll see how an Agent observes, thinks, and acts. It brings together everything we’ve covered so far and sets the stage for creating your own AI Agent.

But first, it’s time for another short quiz!