skillsdotnet

mcp
Guvenlik Denetimi
Uyari
Health Uyari
  • No license — Repository has no license file
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 9 GitHub stars
Code Uyari
  • Code scan incomplete — No supported source files were scanned during light audit
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

C# implementation of agent skills (agentskills.io) with MCP integration and convenience.

README.md

SkillsDotNet

NuGet
NuGet

Agent skills (agentskills.io) for the C# MCP SDK.

Experimental: This package is under active development and its API is subject to change.

Skills provide "context-as-progressive-disclosure" via SKILL.md files. This library uses a skill:// URI convention to distribute skills as MCP resources -- clients can load frontmatter for discovery (~50-100 tokens), full SKILL.md on demand, and supporting files as needed.

Installation

dotnet add package SkillsDotNet.Mcp

Requires the official C# MCP SDK (ModelContextProtocol package).

If you only need the core skill types (parsing, validation, scanning) without the MCP transport layer:

dotnet add package SkillsDotNet

Server-Side Usage

Register a single skill directory

builder.Services
    .AddMcpServer()
    .WithSkill("/path/to/my-skill");

Register all skills from a directory

Each subdirectory containing a SKILL.md is registered as a skill:

builder.Services
    .AddMcpServer()
    .WithSkillsDirectory("/path/to/skills");

Vendor shortcuts

Register skills from well-known agent directories:

builder.Services
    .AddMcpServer()
    .WithClaudeSkills()    // ~/.claude/skills/
    .WithCursorSkills()    // ~/.cursor/skills/
    .WithCopilotSkills()   // ~/.copilot/skills/
    .WithCodexSkills()     // /etc/codex/skills/ + ~/.codex/skills/
    .WithGeminiSkills()    // ~/.gemini/skills/
    .WithGooseSkills()     // ~/.config/agents/skills/
    .WithOpenCodeSkills(); // ~/.config/opencode/skills/

Options

builder.Services
    .AddMcpServer()
    .WithSkillsDirectory("/path/to/skills", new SkillOptions
    {
        // List every file as a separate resource (default: Template)
        SupportingFiles = SkillFileMode.Resources,

        // Custom main file name (default: "SKILL.md")
        MainFileName = "SKILL.md"
    });

Client-Side Usage

Discover skills on a server

var skills = await client.ListSkillsAsync();
foreach (var skill in skills)
{
    Console.WriteLine($"{skill.Name}: {skill.Description}");
}

Read a skill's manifest

var manifest = await client.GetSkillManifestAsync("code-review");
foreach (var file in manifest.Files)
{
    Console.WriteLine($"  {file.Path} ({file.Size} bytes, {file.Hash})");
}

Download a skill locally

var path = await client.DownloadSkillAsync("code-review", targetDirectory: "./skills");

Sync all skills from a server

var paths = await client.SyncSkillsAsync(targetDirectory: "./skills");

Using skills with IChatClient

SkillCatalog discovers skills from one or more MCP servers and provides two things for use with Microsoft.Extensions.AI: context blocks (frontmatter summaries for the system prompt) and a load_skill tool the model can call to load a skill's full content on demand.

// Build a catalog from one or more MCP servers
var catalog = new SkillCatalog();
await catalog.AddClientAsync(mcpClient1);
await catalog.AddClientAsync(mcpClient2);

// Get frontmatter context blocks — add to the system message so the
// model knows what skills are available (~50-100 tokens each)
var contexts = catalog.GetSkillContexts(); // IReadOnlyList<TextContent>

var messages = new List<ChatMessage>
{
    new(ChatRole.System, [
        new TextContent("You are a helpful assistant. The following skills are available:"),
        .. contexts
    ]),
    new(ChatRole.User, "Help me review this pull request"),
};

// Add catalog.Tools to ChatOptions.Tools — this is load_skill plus, once any skill
// has been loaded, unload_skill (so the model can free context when finished).
var options = new ChatOptions
{
    Tools = [.. catalog.Tools],
};

var response = await chatClient.GetResponseAsync(messages, options);

If a server disconnects, remove its skills from the catalog:

catalog.RemoveClient(mcpClient1);

Handling skill dependencies

Skills can declare MCP server dependencies in their frontmatter:

---
name: explore-everything
description: Explores an MCP server
dependencies: [everything-server]
---

When a skill with dependencies is loaded via the load_skill tool, the OnDependenciesRequired callback fires so the client host can connect to the required servers on demand:

catalog.OnDependenciesRequired = async (request, cancellationToken) =>
{
    Console.WriteLine($"Skill '{request.SkillName}' requires: {string.Join(", ", request.ServerNames)}");

    foreach (var serverName in request.ServerNames)
    {
        // Look up and connect to the server using your own configuration
        var client = await ConnectToServerAsync(serverName, cancellationToken);
        if (client is null)
            return false; // Signal failure — LoadSkillAsync will throw
    }

    return true; // All dependencies satisfied
};

The callback receives a SkillDependencyRequest with the skill name and the list of required server names. Return true when all servers are connected, or false to abort (which causes LoadSkillAsync to throw InvalidOperationException). If the callback is not set, skills with dependencies load silently without notification.

See the DynamicMcpServers sample for a complete example.

Unloading skills

Progressive disclosure shouldn't become progressive accumulation. Once at least one skill is loaded, SkillCatalog.UnloadSkillTool becomes available alongside load_skill — the model is told about it via a short postscript appended to each load_skill response, and can call it to free a skill's context when finished. The unload_skill AIFunction and its accompanying guidance only appear while at least one skill is currently loaded.

The OnSkillUnloaded callback fires every time a skill is unloaded. The host wires it to do two things:

  1. Disconnect any released MCP servers. The catalog passes only the servers no longer needed by any other still-loaded skill, so there's no ref-counting to do.
  2. Scrub the prior load_skill call/result pair from chat history — that's where the SKILL.md content actually lives. The library doesn't touch your message list; the host is in charge.

Mutating the chat history while FunctionInvokingChatClient is iterating is unsafe, so queue scrub work during the callback and process it after GetStreamingResponseAsync returns:

var pendingScrubs = new List<string>();

catalog.OnSkillUnloaded = async (result, ct) =>
{
    foreach (var serverName in result.ReleasedServers)
        await DisconnectAsync(serverName, ct);

    pendingScrubs.Add(result.SkillName);
};

// ... after each streaming turn:
foreach (var skillName in pendingScrubs)
    ScrubLoadCallsForSkill(messages, skillName);
pendingScrubs.Clear();

The scrub itself is a simple predicate over the messages list — match FunctionCallContent whose Name == "load_skill" and Arguments["skillName"] equals the unloaded skill, then drop those calls and the matching FunctionResultContent. See DynamicMcpServers/Program.cs for the complete implementation.

URI Convention

Each skill exposes three resources following the FastMCP convention:

URI Type Content
skill://{name}/SKILL.md Resource (listed) Full SKILL.md content
skill://{name}/_manifest Resource (listed) JSON manifest with file listing
skill://{name}/{+path} ResourceTemplate Supporting files on demand

Manifest format

{
  "skill": "code-review",
  "files": [
    { "path": "SKILL.md", "size": 512, "hash": "sha256:abc123..." },
    { "path": "references/checklist.md", "size": 256, "hash": "sha256:def456..." }
  ]
}

Writing a Skill

A skill is a directory containing a SKILL.md file with YAML frontmatter:

my-skill/
  SKILL.md
  references/
    example.md
---
name: my-skill
description: What this skill does
license: MIT
compatibility: claude, cursor
metadata:
  author: your-name
  version: "1.0"
---

# My Skill

Instructions for the agent go here.

Frontmatter fields

Field Required Description
name Yes 1-64 chars, lowercase alphanumeric + hyphens, must match directory name
description Yes 1-1024 chars
license No License identifier
compatibility No Comma-separated list of compatible agents (max 500 chars)
dependencies No List of required MCP server names (connected on demand via OnDependenciesRequired)
allowed-tools No Experimental tool restrictions
metadata No Arbitrary key-value pairs for client-specific data

Name rules

Per the agentskills.io spec:

  • Lowercase letters, digits, and hyphens only
  • No leading, trailing, or consecutive hyphens
  • NFKC normalized, max 64 characters
  • Must match the directory name

Packages

Package Description
SkillsDotNet Core library: FrontmatterParser, SkillValidator, SkillDirectoryScanner, SkillInfo, SkillFileInfo, SkillContextExtensions. Depends on Microsoft.Extensions.AI.Abstractions.
SkillsDotNet.Mcp MCP transport layer: SkillResourceFactory, SkillCatalog, builder extensions, client extensions. Depends on SkillsDotNet and ModelContextProtocol.

Target Frameworks

  • .NET 10
  • .NET 9
  • .NET 8

Acknowledgements

Many conventions and especially the server-side resource implementations in this library are inspired by FastMCP 3.0. We've tried to align with FastMCP whenever possible.

License

MIT

Yorumlar (0)

Sonuc bulunamadi