All skills
github avatar

/dotnet-mcp-builder

@aa01464 official
by githubgithub/awesome-copilot40k stars
5,040

Build Model Context Protocol (MCP) servers in C#/.NET against the current ModelContextProtocol 2.x NuGet packages. Helps with cases the model gets wrong without guidance — stale versions (0.x preview or 1.x-era defaults), the v2 stateless-by-default HTTP flip, the 2026-07-28 spec deprecations (roots/sampling/logging), MCP Apps and Tasks extension packages, elicitation URL mode, per-session HTTP wiring, OAuth and reverse-proxy deploy specifics, and debugging MapMcp / STDIO / Streamable-HTTP errors. Also covers STDIO and Streamable HTTP transports (SSE is deprecated), tools, prompts, resources, completions, and a basic .NET MCP client. Trigger when the user says or implies any .NET MCP server work: ModelContextProtocol, McpServerTool, MapMcp, WithStdioServerTransport, "MCP server in C#", "MCP tool in dotnet", "expose this as MCP", or names a primitive (prompt/resource/elicitation/MCP App) in a .NET context. Skip for MCP work in other languages.

Use this Skill: https://skilld.dev/gh/github/awesome-copilot/dotnet-mcp-builder

This session only. Nothing lands on disk.

referencesserver-features.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Other server features (completions, logging, progress, filters)

A quick reference for the smaller MCP server features beyond the core primitives. Each section is short — load this file when one of these comes up.

Argument completions

Completions let the host autocomplete prompt arguments and resource template parameters. The user starts typing; the client asks the server "what are valid values?".

Implement via the low-level handler (no high-level attribute exists yet):

builder.Services.Configure<McpServerOptions>(options =>
{
    options.Capabilities ??= new();
    options.Capabilities.Completions ??= new();

    options.Capabilities.Completions.CompleteHandler = async (ctx, ct) =>
    {
        // ctx.Params.Ref tells us what they're completing (a prompt or resource).
        // ctx.Params.Argument has the partial value typed so far.
        var partial = ctx.Params.Argument.Value ?? "";
        var matches = MyDataSource
            .Where(x => x.StartsWith(partial, StringComparison.OrdinalIgnoreCase))
            .Take(100)
            .ToArray();

        return new CompleteResult
        {
            Completion = new()
            {
                Values = matches,
                HasMore = false,
                Total = matches.Length
            }
        };
    };
});

Useful for: project IDs, file names, enum values that depend on dynamic data.

Logging

Servers can emit log messages that hosts surface in their UI (and the LLM can sometimes see). Use the standard ILogger<T> injected via DI — the SDK plumbs it through.

public class WeatherTools
{
    private readonly ILogger<WeatherTools> _log;
    public WeatherTools(ILogger<WeatherTools> log) => _log = log;

    [McpServerTool, Description("…")]
    public string GetWeather(string city)
    {
        _log.LogInformation("Looking up weather for {City}", city);
        return "...";
    }
}

For STDIO servers, remember: console logging must go to stderr (LogToStandardErrorThreshold = LogLevel.Trace) — otherwise it corrupts the JSON-RPC stream. See transport-stdio.md.

MCP-channel logging is deprecated in the 2026-07-28 spec — SDK 2.x marks it [Obsolete] (MCP9005), along with the logging capability and the client's setLevel method (replaced by a _meta log level on requests). ILogger-based logging above is unaffected and remains the right default. Only use the MCP-channel notification below when supporting down-level clients that expect it:

await server.SendNotificationAsync(
    NotificationMethods.LoggingMessageNotification,
    new LoggingMessageNotificationParams
    {
        Level = LoggingLevel.Info,
        Logger = "weather",
        Data = JsonSerializer.SerializeToElement(new { city, latency_ms = 123 })
    },
    ct);

The client may have set a setLevel filter — don't spam levels below it.

Progress notifications

For long-running tools, send progress updates so the host can display a spinner with text:

[McpServerTool, Description("Processes a large dataset.")]
public static async Task<string> Process(
    IMcpServer server,
    RequestContext<CallToolRequestParams> ctx,
    string datasetId,
    CancellationToken ct)
{
    var progressToken = ctx.Params.Meta?.ProgressToken;

    for (int i = 0; i < 100; i++)
    {
        await Task.Delay(50, ct);

        if (progressToken is not null)
        {
            await server.SendNotificationAsync(
                NotificationMethods.ProgressNotification,
                new ProgressNotificationParams
                {
                    ProgressToken = progressToken,
                    Progress = i + 1,
                    Total = 100,
                    Message = $"Processing item {i + 1} of 100"
                },
                ct);
        }
    }

    return "Done.";
}

Only send progress if the client passed a progressToken in the request meta — otherwise the host isn't listening.

Notification handlers (server-side)

Servers can react to notifications the client sends:

options.Capabilities ??= new();
options.Capabilities.NotificationHandlers ??= [];

options.Capabilities.NotificationHandlers[NotificationMethods.RootsListChangedNotification] =
    async (notification, ct) =>
    {
        // Refresh root cache, etc.
    };

options.Capabilities.NotificationHandlers[NotificationMethods.CancelledNotification] =
    async (notification, ct) =>
    {
        // The client cancelled a request; if you have side-effects in flight, abort them.
    };

Filters / middleware

The SDK supports filters that wrap tool calls (think ASP.NET Core middleware for MCP). Use them for cross-cutting concerns: auth checks, telemetry, rate limiting, audit logging.

builder.Services
    .AddMcpServer()
    .WithStdioServerTransport()
    .WithToolsFromAssembly()
    .WithCallToolFilter(async (ctx, next) =>
    {
        var sw = Stopwatch.StartNew();
        try
        {
            return await next(ctx);
        }
        finally
        {
            sw.Stop();
            ctx.Server.Services?
                .GetRequiredService<ILogger<Program>>()
                .LogInformation("Tool {Tool} took {Ms}ms",
                    ctx.Params.Name, sw.ElapsedMilliseconds);
        }
    });

Similar With*Filter helpers exist for resources, prompts, and other capabilities — check the SDK API reference for the current set.

Server instructions (system-prompt-ish)

You can supply instructions sent to the client at initialise time. Hosts may include them in the LLM's system prompt.

builder.Services.AddMcpServer(options =>
{
    options.ServerInstructions =
        "Use the booking tools to schedule meetings. " +
        "Always confirm with the user before booking via elicitation.";
});

Keep this short — every token here costs the user.

Capabilities advertising

If you want to not advertise a capability you happen to have code for, you can mute it:

builder.Services.AddMcpServer(options =>
{
    options.Capabilities = new()
    {
        Tools = new(),       // advertise tools
        Prompts = new(),     // advertise prompts
        Resources = null,    // do NOT advertise resources, even if some are registered
    };
});

By default, the SDK advertises everything you've registered — usually the right behaviour. Note that Logging is [Obsolete] in 2.x (MCP9005) — don't advertise it on new servers. Roots and sampling are client capabilities: a server never advertises them, it only checks server.ClientCapabilities?.Roots / .Sampling before using the (equally deprecated) client-side features.

Source: SKILL.md on GitHub

No alerts15d3 checks · Risk SAFE
  • Gen Agent Trust Hub15d

    The skill provides technical guidance and code references for building Model Context Protocol (MCP) servers and clients using the official C#/.NET SDK. It includes security best practices for resource access and follows standard development patterns.

  • Socket15d

    No alerts

  • Snyk15d

    Risk: LOW · No issues

Signed by skilld at aa01464. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 20 hours ago.

Activeupdated 2 months ago
  • MCP
  • dotnet
  • csharp
  • model-context-protocol
  • stdio
  • http
  • tools
  • prompts
  • resources
  • streaming

README badge

README badge for github/awesome-copilot/dotnet-mcp-builder

Builds Model Context Protocol servers in C# / .NET against the stable 1.x SDK, covering STDIO and HTTP transports, tools, prompts, resources, sampling, elicitation, MCP Apps, and debugging. Targets the specific pitfalls that cause breakage: stale preview package versions, stdout pollution in STDIO mode, stateless HTTP misconfiguration, and missing primitive registration in DI.

Generated from the current SKILL.md.

Does this skill work with preview versions of the ModelContextProtocol NuGet packages?
No. The skill targets stable 1.x packages only. Preview versions (0.3, 0.4) have breaking differences and won't compile against current samples. Always pin the latest 1.x release.
Can I use stateless HTTP transport with sampling, elicitation, or server-initiated notifications?
No. Stateless HTTP breaks those features at runtime because it cannot maintain bidirectional communication. Use stateful HTTP or STDIO if you need server-to-client capabilities.
What should I do if my STDIO server isn't working?
First check that nothing is writing to stdout — configure `LogToStandardErrorThreshold = LogLevel.Trace` and remove any `Console.WriteLine` calls, since stdout is the JSON-RPC channel. Also verify tools and prompts are registered with `.WithToolsFromAssembly()` or equivalent.
Does this skill cover building MCP servers in other languages like Python or TypeScript?
No. This skill is C#/.NET only. It skips MCP work in other languages.
Can I write a .NET program that consumes an MCP server instead of building one?
Yes. Load `references/client.md` for guidance on writing a basic .NET MCP client that calls an existing server.

Generated from the current SKILL.md. These answers refresh after source changes.