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.

referencessampling.md

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

Sampling

Deprecated in the 2026-07-28 spec. SDK 2.x marks the sampling APIs [Obsolete] (build warning MCP9005). They stay wire-compatible with down-level clients during the transition, but don't design new servers around sampling: for "the tool needs user/LLM input mid-execution", prefer the multi-round-trip input_required pattern; for "the server needs an LLM", call a model directly server-side. Keep this page for maintaining existing 1.x-era servers; suppress MCP9005 only as a documented transition measure.

Sampling lets a tool call the LLM through the client instead of bringing its own model. The server says "summarise this for me" and the client routes the request to whatever model the user has configured (Claude, GPT, local model, anything). Costs and rate limits live with the client, not the server.

When to use sampling

  • The tool needs an LLM step (summarise, classify, draft, extract) and you don't want to ship/configure your own model in the server.
  • You want to respect the user's model choice, key, and cost preferences.
  • You're building a "meta" tool that orchestrates LLM work as part of its job (e.g. multi-step agents).

If you already have a deterministic algorithm, don't add a sampling call "for flavour" — it adds latency and cost.

Prerequisite: stateful transport

Like elicitation, sampling needs the server to call back to the client. STDIO works always; HTTP needs options.Stateless = false.

Recommended: IChatClient adapter

The cleanest API wraps the sampling channel as Microsoft.Extensions.AI.IChatClient, so you write code that looks like normal LLM-calling .NET:

using System.ComponentModel;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Server;

[McpServerToolType]
public class SummaryTools
{
    [McpServerTool(Name = "SummarizeContent"), Description("Summarises arbitrary text using the client's LLM.")]
    public static async Task<string> Summarize(
        IMcpServer server,
        [Description("The text to summarize")] string text,
        CancellationToken cancellationToken)
    {
        ChatMessage[] messages =
        [
            new(ChatRole.User, "Briefly summarize the following content:"),
            new(ChatRole.User, text),
        ];

        var options = new ChatOptions
        {
            MaxOutputTokens = 256,
            Temperature = 0.3f,
        };

        var response = await server.AsSamplingChatClient()
            .GetResponseAsync(messages, options, cancellationToken);

        return $"Summary: {response}";
    }
}

Why this is nice:

  • Same IChatClient API the rest of the .NET AI ecosystem uses.
  • Works with Microsoft.Extensions.AI middleware (rate limiting, retries, telemetry, function calling).
  • You can swap to a direct provider in tests by injecting a different IChatClient.

Lower-level: SampleAsync

When you need full control over the request shape:

using ModelContextProtocol.Protocol;

CreateMessageResult result = await server.SampleAsync(
    new CreateMessageRequestParams
    {
        Messages =
        [
            new SamplingMessage
            {
                Role = Role.User,
                Content = [new TextContentBlock { Text = "What is 2 + 2?" }]
            }
        ],
        MaxTokens = 100,
        Temperature = 0.0f,
        SystemPrompt = "You are a precise calculator.",
        // ModelPreferences, StopSequences, IncludeContext...
    },
    cancellationToken);

string answer = result.Content
    .OfType<TextContentBlock>()
    .FirstOrDefault()?.Text ?? string.Empty;

ModelPreferences lets you hint at model selection (cost vs. speed vs. intelligence priority); the client decides the actual model.

ModelPreferences = new ModelPreferences
{
    Hints = [new ModelHint { Name = "claude" }],   // soft preference
    CostPriority = 0.2,        // 0..1
    SpeedPriority = 0.4,
    IntelligencePriority = 0.9,
}

IncludeContext

Sampling requests can ask the client to include context from the current conversation:

IncludeContext = ContextInclusion.ThisServer   // include this server's prior messages
// or AllServers, or None (default)

Useful when you need the LLM to consider what's happened in the chat so far without you re-supplying it.

Capability check

Always confirm the client supports sampling — many do not:

if (server.ClientCapabilities?.Sampling is null)
    throw new McpException(
        "This client does not support sampling. " +
        "Configure a model in the host or use a different MCP client.");

Performance notes

  • Sampling calls are network round-trips (client → its provider → back). Expect 100ms–multiple seconds. Don't loop tightly.
  • Token costs are paid by the user (their API key/quota). Be conservative with MaxTokens.
  • Cancellation propagates: if the user kills the tool call, the sampling request is cancelled too.

Sampling vs. doing it server-side

Sampling (via client) Direct LLM call (server-side)
Uses the user's model + key Uses your service's key
Respects user's policy/quota Your responsibility to bill/track
Works in any host the user has Locked to the model you ship with
Higher latency (extra hop) Lower latency, direct
No secrets to manage You manage the API key

For "smart" servers shipped to many users, prefer sampling. For internal corporate servers where you want consistent behaviour and you're already paying for the model, direct is fine.

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 19 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.