Up to this point in the series, we've extended the agent's capabilities in two ways: custom C# tools that execute code, and MCP servers that connect to external systems. Both are about what the agent can do. Skills are about what the agent knows and more specifically, how to apply that knowledge in a repeatable, composable way.
A skill is a folder containing a SKILL.md file with specialised instructions, and optionally supporting files like scripts, templates, or examples. You point the SDK at a directory, the agent scans for skills automatically, and when a task matches a skill's description, that skill's instructions are injected into the session context. The agent gains focused expertise — without you writing any C#, and without redeploying your application.
Why skills exist
Think about a development task you repeat across projects: scaffolding a solution, writing a particular kind of test, generating API documentation in a house style, following a specific migration pattern. The first time you do it with an AI assistant, you spend real effort crafting the right prompt, correcting the model, and iterating until it gets things right.
The second time, you do it all again.
Skills solve this by letting you encode that effort once, in a file, and make it available to any agent that loads the skill directory. They're reusable, versionable, shareable across teams, and can be used across agents. An open standard that works across multiple AI agents. A skill you write for the Copilot SDK works in the CLI, works in VS Code, and works in any future agent that adopts the standard.
This is the key distinction from custom instructions, which are applied globally to a session. Skills are selective: the agent decides when a skill is relevant based on the task at hand, and injects it only when needed. You can have dozens of skills without bloating every session's context.
Anatomy of a skill
Every skill lives in its own subdirectory. The only required file is SKILL.md. Supporting files — scripts, templates, example outputs — can live alongside it and be referenced from the instructions.
skills/
├── dotnet-clean-architecture/
│ ├── SKILL.md
│ ├── solution-template.md
│ └── scripts/
│ └── scaffold.sh
├── api-documentation/
│ ├── SKILL.md
│ └── examples/
│ └── endpoint-doc-example.md
└── security-review/
└── SKILL.md
The SKILL.md file has two parts: a YAML frontmatter block that identifies the skill, and a body that contains the instructions the agent follows when the skill is active.
---
name: dotnet-clean-architecture
description: >
Scaffolds .NET solutions using Clean Architecture. Use when creating new
.NET projects, solutions, or service templates. Triggers on: clean architecture,
solution scaffold, new .NET project, DDD, domain-driven design.
---
# .NET Clean Architecture Scaffolder
You are an expert .NET architect. When scaffolding a solution, always follow these conventions:
## Project Structure
Every solution must have these layers:
- `src/{SolutionName}.Domain` — entities, value objects, domain events, no dependencies
- `src/{SolutionName}.Application` — use cases, commands, queries, CQRS, depends only on Domain
- `src/{SolutionName}.Infrastructure` — EF Core, external services, implements Application interfaces
- `src/{SolutionName}.API` — ASP.NET Core, controllers or minimal API, depends on Application
## Conventions
- Use record types for value objects and DTOs
- Commands and queries must implement MediatR's `IRequest<T>`
- Repository interfaces live in Application, implementations in Infrastructure
- Never reference Infrastructure from Application or Domain
## Script
Run `./scripts/scaffold.sh {SolutionName}` to generate the folder structure before writing any code.
The frontmatter name is how you reference the skill explicitly (more on that below). The description is what the agent uses to decide whether a skill is relevant — think of it as a semantic index. The more precise your description, the more accurately the agent will match it to appropriate prompts. Put all "when to use" guidance in the description, never in the body — the body is for instructions the agent follows, not for routing decisions.
Loading skills
Skills are registered on SessionConfig via SkillDirectories. The SDK scans every subdirectory of each path for SKILL.md files automatically — you don't enumerate them individually:
var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
SkillDirectories = ["./skills"] // loads all SKILL.md files found under ./skills
});
That's the entire integration. Every skill in the directory tree becomes available to the session.
You can load from multiple directories, which is useful for separating project-specific skills from shared organisational ones:
var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
SkillDirectories =
[
"./skills", // project-specific skills
Path.Combine(homeDir, ".copilot/skills") // personal skills, shared across projects
]
});
The SDK also supports system-level skill directories following the same conventions as the CLI: ~/.copilot/skills for personal skills shared across projects, or .github/skills within a repository for project-specific ones.
How the agent selects skills
When a user sends a prompt, the agent evaluates the prompt against the description of every loaded skill. If a skill's description is a semantic match, the skill's SKILL.md content is injected into the session context and the agent follows its instructions.
This happens automatically — the user doesn't need to know skills exist. A developer asking "scaffold a new service for order processing using clean architecture" will trigger the dotnet-clean-architecture skill without typing anything special.
If you want to invoke a skill explicitly, prefix the skill name with a forward slash in the prompt:
/dotnet-clean-architecture Create a new service for handling payment processing
This bypasses the automatic matching and forces the skill into context regardless of whether the description would have matched organically. It's useful for testing and for cases where users have learned which skill they want.
Disabling skills per session
A skill in the directory is available to every session. Sometimes you want to suppress a skill for a specific session — a different workflow, a different team, or a controlled experiment comparing behaviour with and without a skill active.
You can disable skills by name without deleting the file:
var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
SkillDirectories = ["./skills"],
DisabledSkills = ["security-review"] // suppressed for this session only
});
DisabledSkills takes the name from the skill's frontmatter. The file stays on disk; the skill simply isn't offered to the agent in this session.
Creating skills at runtime
Skills don't have to exist on disk when your application starts. Because skills are just files, you can generate them dynamically — from a database, a configuration API, or user input — and write them to a temporary directory before creating the session:
public async Task<CopilotSession> CreateSessionWithDynamicSkillAsync(
string skillName,
string skillDescription,
string skillInstructions)
{
// Write the skill to a temp directory
var skillDir = Path.Combine(Path.GetTempPath(), "copilot-skills", skillName);
Directory.CreateDirectory(skillDir);
var skillContent = $"""
---
name: {skillName}
description: {skillDescription}
---
{skillInstructions}
""";
await File.WriteAllTextAsync(Path.Combine(skillDir, "SKILL.md"), skillContent);
return await _client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
SkillDirectories = [Path.GetDirectoryName(skillDir)!]
});
}
This opens up patterns like tenant-specific skills in a multi-tenant application, user-customisable agent behaviour stored in a database, or skills assembled from configuration without a redeployment. Because skills are files, they're composable, testable, and can be version-controlled independently of your application code.
What's next
Skills give your agent reusable, portable expertise that can be authored independently of application code, shared across teams, and updated without redeployment. Combined with custom C# tools and MCP servers, you now have three complementary ways to shape what your agent can do and how it behaves.
In the next post we’ll cover multi-agent patterns — how to use the Microsoft Agent Framework to compose Copilot SDK agents with other agents, orchestrate specialist agents for complex workflows, and think about the boundaries between what each agent owns.