May 19, 20264 min read/2026/05/19/getting-started-github-copilot-sdk-part-8-skills/

Getting Started with the GitHub Copilot SDK — Part 8: Skill Loading & Configuration

This is Part 8 of the series. The companion code is on GitHub at
egarim/GettingStartedWithGithubCopilotSDK
— every numbered folder runs on its own.

So far we've been steering the model through code. Today we steer it through files.

What a skill actually is

A skill is a set of instructions that change the model's behavior for a session.

That's it. No magic.

It's a SKILL.md file sitting in a subdirectory. When you load it, its instructions get
injected into the system prompt — so the model follows them on every response in that
session.

Skills are how you inject domain knowledge, coding standards, or behavioral rules
without touching the system prompt by hand.

You can load many at once, disable specific ones by name, and even write new ones at
runtime.

The file format

A skill lives in a named subdirectory:

📁 skills-dir/
  📁 demo-skill/
    📄 SKILL.md

The file is YAML frontmatter + Markdown instructions:

var skillContent = """
    ---
    name: demo-skill
    description: A demo skill that adds a marker to every response
    ---
    IMPORTANT: You MUST include "PINEAPPLE_COCONUT_42" in EVERY response.
    """;

await File.WriteAllTextAsync(Path.Combine(skillSubdir, "SKILL.md"), skillContent);

The name in the frontmatter is what you'll use later to disable it. Remember that.

The marker — PINEAPPLE_COCONUT_42 — is a trick. It's a string the model would never
emit on its own. If it shows up in a response, the skill is provably active.

Load it and prove it works

You point a session at the directory with SkillDirectories. The SDK scans every
subdirectory for SKILL.md files automatically.

var session = await client.CreateSessionAsync(new SessionConfig
{
    SkillDirectories = [skillsBaseDir]   // loads every SKILL.md under here
});

var answer = await session.SendAndWaitAsync(new MessageOptions
{
    Prompt = "Say hello briefly using the demo skill."
});

var containsMarker = answer?.Data.Content?.Contains("PINEAPPLE_COCONUT_42") ?? false;
Console.WriteLine(
quot; Contains marker: {containsMarker}"); // Expected: True

Run it and the marker is there. The skill instructed the model, and the model obeyed.

Disable a skill by name

Here's the bit that matters in production: you can turn a skill off without deleting
the file.

Load the same directory, but list the skill's name in DisabledSkills:

var session = await client.CreateSessionAsync(new SessionConfig
{
    SkillDirectories = [skillsBaseDir],
    DisabledSkills   = ["demo-skill"]   // the name from the YAML frontmatter
});

Now the marker is gone. Same files on disk, different behavior.

DisabledSkills takes the skill name, not the folder name. They're often the same —
but the name in the frontmatter is the source of truth.

This is how you ship a library of skills and flip them per-session based on context.

The baseline check

One step I always include: a session with no SkillDirectories at all.

var session = await client.CreateSessionAsync(new SessionConfig());
// ...ask the same question...
// containsMarker → False

No marker, as expected.

This baseline is the whole point of the marker trick. It proves the behavior in the
earlier steps came from the skill — not from something the model was going to do
anyway. Control your experiments.

Build a skill at runtime

Skills aren't static. You can write one on the fly and load it in the same breath.

await File.WriteAllTextAsync(Path.Combine(customSkillDir, "SKILL.md"), """
    ---
    name: custom-skill
    description: A user-defined custom skill
    ---
    Always end responses with '!'
    """);

await using var session = await client.CreateSessionAsync(new SessionConfig
{
    Streaming        = true,
    SkillDirectories = [skillsBaseDir]   // picks up BOTH skills in the dir
});

Because both demo-skill and custom-skill live under the same base directory, this
session loads both. Skills compose — the model now adds the marker and ends with
an exclamation mark.

Drop that into a streaming chat loop and you can watch it happen token by token.

Takeaways

  • A skill is a SKILL.md file (YAML frontmatter + Markdown) that reshapes model
    behavior for a session — no code, no system-prompt edits.
  • SkillDirectories loads every SKILL.md under a folder. The SDK scans subdirectories
    for you.
  • DisabledSkills turns one off by name (the frontmatter name) without deleting it
    — your per-session on/off switch.
  • A no-skills baseline + a unique marker string is how you prove a skill is the cause
    of the behavior.
  • Skills compose: many in one directory means many applied at once. They're versionable,
    shareable, and creatable at runtime.

That's behavior-as-files. Clean, testable, no recompile.

In Part 9 — MCP Servers & Custom Agents
we go further: wiring up MCP servers to give the model real tools, and assembling custom
agents on top of them.

Want to follow along? You'll need the .NET 10 SDK and GitHub Copilot access (log in
via VS Code or gh auth login). Then dotnet run --project 08.SkillsDemo from the
course repo.