Microsoft has moved Agent Skills for .NET out of experimental preview in the Agent Framework. The [Experimental] attribute is gone from the API, which in practical terms means teams can now depend on it without worrying that the next update will break their agents. If you have been holding off on adopting skills because the API surface was still shifting under you, that reason no longer applies.
This matters more than it sounds. A stable API means production sign-off is easier to get, versioning follows normal semver rules, and you can build internal libraries of skills on top of it without rewriting them every few months. For anyone building enterprise agents on .NET, this is the point where Agent Skills stops being an experiment and starts being infrastructure.
What Agent Skills actually solve
An agent’s system prompt has a real cost. Every instruction you add to it consumes context window on every single call, whether that instruction is relevant to the current task or not. Agent Skills fix this by letting an agent carry a library of domain expertise on the side and pull in only the piece it needs, when it needs it.
A skill packages instructions, reference documents, and scripts around one topic. For file-based skills this is a SKILL.md file plus a folder of supporting resources. For code-based skills it is equivalent metadata expressed as C# properties. Either way, the agent does not load everything up front. It follows a four-stage progressive disclosure pattern: it sees the list of available skill names, loads the instructions for the one it decides is relevant, reads any bundled reference documents only if it needs more detail, and runs a bundled script only if the task calls for actual execution.
The net effect is that you can maintain a large catalog of specialised knowledge, HR policy, expense rules, a product’s troubleshooting runbook, without any of it sitting permanently in your token budget. Only the skill that is actually in use gets loaded, and it gets loaded on demand rather than at the start of every conversation.
Where this earns its keep in practice
The most obvious use case is enforcing enterprise policy consistently. Package your company’s expense rules or IT security guidelines as a skill, and an employee-facing agent loads that skill the moment someone asks something like whether a co-working space counts as a valid expense. Because the answer comes from the skill’s instructions rather than the model’s general training, every employee who asks the same question gets the same grounded answer, and you have an auditable path back to the source policy document.
Support playbooks are the second obvious fit. A troubleshooting guide that a human support engineer would normally follow step by step can be turned into a skill, and the agent follows the same documented steps regardless of which agent instance picks up the ticket. This is where a lot of teams get inconsistent agent behaviour today, different sessions handling the same issue in different ways, and skills are a direct fix for that.
The third pattern is cross-team composition, and this is the one I think gets underused. Different teams can author and own their own skills independently, as a shared repo of file-based skills or as packages on an internal NuGet feed, and you compose them into a single agent without writing any routing logic yourself. The agent decides which skill applies based on the skill’s own description. If you want a starting catalog rather than authoring everything from scratch, the Awesome Copilot repository for skills is a reasonable place to look.
Three authoring styles, and which one to actually reach for
The release supports three ways to author a skill, and all three plug into the same provider at runtime, so the agent treats them identically regardless of how they were built. The choice between them comes down to who owns the content and how it gets distributed, not runtime capability.
- File-based skills: a directory containing SKILL.md plus optional scripts and reference documents. This is the right choice when the content is owned by a non-developer team, policy writers, support leads, or anyone maintaining documentation in a shared repo rather than a codebase.
- Class-based skills: ordinary C# classes that package instructions, resources, and scripts, distributed through normal .NET workflows including internal NuGet packages. Pick this when a development team owns the skill and wants it to go through the same build, test, and release pipeline as the rest of the codebase.
- Code-defined skills: skills constructed directly in application code. This is the option for skills that need to be generated dynamically or that need to close over application state, something a static file or a compiled class cannot easily do.
A common mistake I would flag here is defaulting to class-based skills purely because a development team is doing the initial build. If the actual content, the wording of a policy, the steps in a runbook, is going to be edited by people outside the engineering team, file-based skills save you from becoming a bottleneck for every wording change. Reserve class-based and code-defined skills for cases where the skill’s behaviour genuinely depends on compiled logic or live application state.
The governance controls that make this production-ready
Giving an agent new capabilities only helps if you can also govern how it uses them, and this is where the release puts in real effort. The skills provider exposes three tools that the agent calls internally: load_skill to load a skill’s instructions, read_skill_resource to fetch a bundled resource, and run_skill_script to execute a bundled script. All three require approval by default. Nothing loads or executes without a human or an automated policy signing off first, though you can relax that requirement selectively for operations you trust.
Script execution is handled differently depending on the authoring style, and this distinction matters for your security posture. Class-based and code-defined skill scripts run in-process, inside your own application, so they inherit whatever sandboxing your process already has. File-based skill scripts are delegated to a runner that you supply, which means sandboxing, resource limits, and audit logging are entirely your responsibility to implement. Do not assume the framework sandboxes file-based scripts for you. It explicitly does not, by design, because it cannot know what your runner environment looks like.
Two more controls round out the production story. Filtering lets you expose only a curated subset of a shared skill library to a given agent, using a predicate that can make context-aware decisions based on which agent or tenant is asking. Caching means skills are resolved once and reused, with optional per-key isolation so a single provider can safely serve different skill sets to different agents or tenants without cross-contamination. On top of that, the underlying source classes are now public, so if the built-in composition model does not fit your architecture, you can build a custom pipeline or pull skills from your own internal registry instead.
Wiring it into an agent
The getting started code is short, and it is worth walking through what each piece is doing. You install the required Agent Framework and Azure OpenAI packages, then construct an AgentSkillsProvider pointing at a local skills folder, along with a script runner delegate for any file-based scripts. That provider then gets attached to the agent as one of its AIContextProviders when you configure the ChatClientAgentOptions.
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
var skillsProvider = new AgentSkillsProvider(
Path.Combine(AppContext.BaseDirectory, "skills"),
SubprocessScriptRunner.RunAsync);
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient()
.AsAIAgent(new ChatClientAgentOptions
{
Name = "MyAgent",
ChatOptions = new() { Instructions = "You are a helpful assistant." },
AIContextProviders = [skillsProvider],
},
model: deploymentName);
AgentResponse response = await agent.RunAsync("Help me with onboarding.");
Console.WriteLine(response.Text);
Notice that SubprocessScriptRunner.RunAsync is passed explicitly as the script runner. This is the piece that handles file-based script execution, and it is deliberately something you supply rather than something the framework provides opaquely, which ties back to the sandboxing point above. The rest of the wiring is unremarkable: authentication uses DefaultAzureCredential the same way any other Azure OpenAI agent would, and the skills provider slots in as just another context provider alongside whatever else the agent already uses. Once this is wired up, a call like agent.RunAsync(“Help me with onboarding”) is enough for the agent to discover, load, and act on the relevant skill on its own, without you writing any conditional logic to route the request to the right policy document.
One thing worth testing before you ship this: skill discovery depends on the model correctly matching the user’s request to a skill’s description. If your skill descriptions are vague or overlap heavily with each other, you will see the agent pick the wrong skill, or none at all. Write skill descriptions the same way you would write a good docstring, specific enough that there is no ambiguity about when it applies.
Where this fits, and where it does not
Agent Skills are a good fit when you have real, stable documentation you want an agent to consult, policy documents, runbooks, internal how-to guides, and you want that consultation to be consistent and auditable. They are a poor fit for knowledge that changes every few hours or that needs to be queried rather than loaded wholesale, that is a job for retrieval or a live data source, not a skill bundle.
It is also worth remembering that this release solves the packaging and governance problem, not the retrieval or reasoning problem. Skills tell the agent what to do once it has decided a skill applies. They do not make the underlying model better at deciding which skill is relevant in ambiguous cases, and they will not fix a poorly instructed agent. Treat this as infrastructure for distributing expertise cleanly, not a substitute for getting your agent’s core instructions right in the first place.
Leave a Reply