If you’re building AI agents in .NET, two words show up fast: tools and skills.
A lot of people treat them as the same thing, they’re not, and getting the difference right makes your agents cleaner and lighter to run. Let me break it down as simply as I can.
The simple version
Think of it like this:
- A tool is one thing your agent can do. Get the weather. Send an email. Look up an order. One action, nothing more.
- A skill is a little folder of instructions your agent can read when it needs to, like handing someone a “how to file an expense report” guide instead of trying to teach them everything upfront.
That’s the whole idea. Tools are actions. Skills are instructions.
Why does the difference matter? Every tool you add sits in front of the agent the entire time, taking up space. A skill stays out of the way and only gets opened when the task calls for it. So skills keep things light.
How to add a tool
It’s just a method. Describe what it does, and you’re basically done:
using System.ComponentModel;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
[Description("Get the weather for a given location.")]
static string GetWeather(
[Description("The location to get the weather for.")] string location)
=> $"The weather in {location} is cloudy with a high of 15°C.";
AIAgent agent = new AIProjectClient(endpoint, new DefaultAzureCredential())
.AsAIAgent(
model: deploymentName,
instructions: "You are a helpful assistant...",
tools: [AIFunctionFactory.Create(GetWeather)]);
The agent decides when to call it. You don’t wire anything up.
Tools with Harness Agent
A plain agent uses the tools you pass during agent construction, and you compose any additional providers or middleware yourself. A Harness Agent uses the same function tools, but preconfigures the function-invocation pipeline, per-service-call history persistence, tool-approval support, and other harness capabilities.
Pass function tools through HarnessAgentOptions.ChatOptions.Tools when you create a HarnessAgent with AsHarnessAgent:
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
ChatOptions = new ChatOptions
{
Instructions = "You are a helpful assistant.",
Tools = [AIFunctionFactory.Create(GetWeather)],
},
});
AgentSession session = await agent.CreateSessionAsync();
AgentResponse response = await agent.RunAsync(
"What is the weather like in Amsterdam?",
session);
How to add a skill
A skill is literally a folder with a text file (SKILL.md) that says what it’s for and how to do it:
expense-report/
├── SKILL.md # Required - frontmatter + instructions
├── scripts/
│ └── validate.py # Executable code agents can run
├── references/
│ └── POLICY_FAQ.md # Reference documents loaded on demand
└── assets/
└── expense-report-template.md # Templates and static resources
You point your agent at the folder, and that’s it:
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI.Responses;
// Discover skills from the 'skills' directory
var skillsProvider = new AgentSkillsProvider(
Path.Combine(AppContext.BaseDirectory, "skills"));
// Create an agent with the skills provider
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
{
Name = "SkillsAgent",
ChatOptions = new()
{
Instructions = "You are a helpful assistant...",
},
AIContextProviders = [skillsProvider],
},
model: deploymentName);
The agent notices the skill is there and reads it only when it’s useful.
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI.Responses;
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!;
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
// Discover skills from the 'skills' directory
var skillsProvider = new AgentSkillsProvider(
Path.Combine(AppContext.BaseDirectory, "skills"));
// Create an agent with the skills provider
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
{
Name = "SkillsAgent",
ChatOptions = new()
{
Instructions = "You are a helpful assistant.",
},
AIContextProviders = [skillsProvider],
},
model: deploymentName);
Agent Skills with Harness Agent
With a plain agent, create a skills provider, add it to the agent’s context providers, and compose tool-approval middleware when needed. A Harness Agent can create or include the provider as part of its standard setup.
HarnessAgent includes AgentSkillsProvider by default and discovers file-based skills from Directory.GetCurrentDirectory(). To use a different source, set HarnessAgentOptions.AgentSkillsSource:
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
AgentSkillsSource = new AgentFileSkillsSource(
Path.Combine(AppContext.BaseDirectory, "skills")),
ToolApprovalAgentOptions = new ToolApprovalAgentOptions
{
// Auto-approve load_skill and read_skill_resource, but not run_skill_script.
AutoApprovalRules = [AgentSkillsProvider.ReadOnlyToolsAutoApprovalRule],
},
ChatOptions = new ChatOptions
{
Instructions = "Use the available skills when they match the task.",
},
});
File-based skills
Create an AgentSkillsProvider pointing to a directory containing your skills, and add it to the agent’s context providers. Pass a script runner to enable execution of file-based scripts found in skill directories:
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI.Responses;
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!;
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
// Discover skills from the 'skills' directory
var skillsProvider = new AgentSkillsProvider(
Path.Combine(AppContext.BaseDirectory, "skills"));
// Create an agent with the skills provider
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
{
Name = "SkillsAgent",
ChatOptions = new()
{
Instructions = "You are a helpful assistant.",
},
AIContextProviders = [skillsProvider],
},
model: deploymentName);
Which one should you use?
Easy:
- Need the agent to do something? → Use a tool.
- Need the agent to follow a set of instructions? → Use a skill.
Tools are for actions. Skills are for know-how. Most real agents use both, and now you know the difference.
Wrapping up
That’s really all there is to it.
Tools let your agent do things; skills hand it a set of instructions to follow when the moment comes.
Reach for a tool when you need an action, and a skill when you’ve got a repeatable playbook, no need to overthink it.
My advice: add one small tool, get it working, then move a few instructions into a skill.
Once you’ve built both with your own hands, the difference stops being a concept and just clicks.



