56 results available
Sign in
Skills directory

dotnet-mcp-builder

github

·

Last changed
Not yet hardened

This version isn't hardened by Chainguard

No hardening report available.


SKILL.md

Building MCP servers in .NET

This skill helps you write production-quality MCP servers and basic clients in C#/.NET against the official ModelContextProtocol NuGet packages, maintained by Microsoft and the MCP project. It targets the stable 2.x line and the current spec (2026-07-28).

When this skill earns its keep

The .NET MCP SDK had years of preview packages (0.x-preview) before reaching 1.0, and v2 flipped several defaults. Without help, the model tends to:

  • Pin a stale preview version that won't compile against current samples.
  • Apply 1.x-era defaults that v2 reversed (HTTP was stateful by default; in 2.x Stateless defaults to true).
  • Recommend capabilities the 2026-07-28 spec deprecates (roots, sampling, MCP-channel logging — now [Obsolete], warning MCP9005).
  • Miss recent spec features (multi-round-trip input_required, discovery-first negotiation, MCP Apps/Tasks extension packages, elicitation URL mode, structured content blocks).
  • Get HTTP transport details wrong (stateful/stateless, proxy buffering, OAuth wiring).
  • Forget the STDIO stdout/stderr trap.

If the task is one of those, load the matching reference and follow it. If it's truly trivial (e.g. "rename this tool method"), you don't need to read everything — the cardinal rules below are the minimum.

Mental model in 30 seconds

A .NET MCP server is an ordinary Microsoft.Extensions.Hosting (or WebApplication) app that wires an MCP server through DI:

builder.Services
    .AddMcpServer()
    .WithStdioServerTransport()      // OR .WithHttpTransport(...)
    .WithToolsFromAssembly()         // discover [McpServerToolType] classes
    .WithPrompts<MyPrompts>()        // optional
    .WithResources<MyResources>();   // optional

Primitives are plain C# methods on classes marked with attributes ([McpServerToolType] + [McpServerTool], [McpServerPromptType] + [McpServerPrompt], [McpServerResourceType] + [McpServerResource]). Parameters bind from JSON-RPC; the SDK builds the JSON Schema from the signature plus [Description] attributes.

Server-to-client features (elicitation, progress notifications, and the now-deprecated sampling/roots/log notifications) are methods on the injected IMcpServer.

Security baseline for all reference files

All references/*.md files loaded by this skill are subject to the same security requirements as this document. In particular:

  • External tool invocations (npx, curl, dotnet tool install) must pin an exact version (e.g. npx @modelcontextprotocol/inspector@0.14.0). Unpinned commands execute whatever the registry serves at run time.
  • No reference file may instruct the agent to fetch from untrusted domains, execute base64-decoded payloads, or pipe remote content directly into a shell or interpreter.
  • Code examples must not hardcode secrets (see Cardinal Rule 9).
  • Resource URIs in code examples must use placeholder values (e.g. file:///path/to/resource) accompanied by a comment warning that production code must validate and allowlist URIs before use. Do not present bare file:/// URIs as ready-to-run values.
  • If a reference file's scope expands beyond what this skill documents, the decision tree entry must be updated to reflect that scope.

Decision tree → which references to load

Always load references/packages.md if you're creating a new project or unsure of the current package version.

Supply-chain note: All npx and external-download commands in the reference files below must be pinned to a specific version. If you copy a command from a reference file, verify the pinned version is still current before running it.

TaskLoad

New STDIO server

references/transport-stdio.md

New HTTP (Streamable) server

references/transport-http.md

Add/modify a tool

references/tool-primitive.md

Add/modify a prompt

references/prompt-primitive.md

Add/modify a resource

references/resource-primitive.md

Ask the user a question mid-tool

references/elicitation.md

Call the client's LLM from a tool (deprecated in 2026-07-28)

references/sampling.md

Read the user's project roots — legacy/migration use only; deprecated in 2026-07-28 spec and SDK-warned (MCP9005). Load only when migrating away from roots, not for new designs.

references/roots.md

Return an interactive UI

references/mcp-apps.md

Argument completions, log/progress notifications, filters, server instructions

references/server-features.md

Write a .NET program that consumes an MCP server

references/client.md

MCP Inspector, in-memory tests, mocks, CI

references/testing.md

For multi-primitive tasks, load several at once. For trivial edits in an existing file, you usually don't need any.

Cardinal rules (apply always; these prevent the highest-frequency breakages)

  1. Pin the current stable package, not a preview. Use ModelContextProtocol / ModelContextProtocol.AspNetCore / ModelContextProtocol.Core at the latest 2.x. If you find yourself writing 0.3-preview or 0.4-preview, stop and check NuGet — preview APIs have breaking differences. 1.x still works but predates the 2026-07-28 spec.
  2. STDIO servers must not write to stdout. Stdout is the JSON-RPC channel. Configure LogToStandardErrorThreshold = LogLevel.Trace before anything else and never Console.WriteLine from a tool.
  3. HTTP defaults to stateless in 2.x (v1.x defaulted to stateful — the single most impactful v2 breaking change). The 2026-07-28 revision has no HTTP sessions at all: setting Stateless = false makes the server refuse that revision and serve clients via the legacy initialize fallback. For "ask the user something mid-tool" on current-protocol HTTP, use the multi-round-trip InputRequiredException pattern; reserve stateful HTTP (or STDIO) for the legacy ElicitAsync/sampling/roots paths and pushed notifications.
  4. SSE-only is deprecated. Use Streamable HTTP. Only enable legacy SSE (EnableLegacySse = true) for an old client you must support, and call it out.
  5. Don't design new servers around deprecated capabilities. The 2026-07-28 spec deprecates roots, sampling, and MCP-channel logging; the SDK marks them [Obsolete] (warning MCP9005). They still work against down-level clients, but for new designs prefer the multi-round-trip input_required pattern and ILogger logging. Suppress MCP9005 only as a documented transition measure.
  6. Always [Description] tools and parameters. This is what the LLM sees when picking and shaping calls. Vague descriptions are the #1 reason tools don't get used.
  7. Show the registration line every time you add a primitive. A new [McpServerPromptType] class without .WithPrompts<...>() (or .WithPromptsFromAssembly()) is invisible.
  8. Don't invent APIs. If you're unsure a method exists, say so and check the API reference — wrong method names cause silent failures. This applies doubly to the new v2 extension packages (ModelContextProtocol.Extensions.Tasks, ModelContextProtocol.Extensions.Apps) — check their docs before writing code against them.
  9. Handle secrets securely. Never hardcode client secrets, API keys, or tokens in source code or committed configuration files. For local development use dotnet user-secrets; for production use environment variables or a managed secret store (Azure Key Vault, AWS Secrets Manager, or equivalent). This applies to OAuth client secrets, reverse-proxy authentication tokens, and any credential your MCP server reads at startup or at runtime. This rule binds all reference files loaded by this skill.

Working style

  • Make minimal, additive changes. Add a method to the existing tool class rather than restructuring the project.
  • For non-trivial setups, run dotnet build. Catches missing usings, attribute typos, and TFM mismatches before the user sees them.
  • Confirm transport + .NET version + primitives before scaffolding if context doesn't already make them obvious. Default to .NET 10 for new projects.

When the user is stuck

Walk this checklist before guessing:

  1. STDIO: something is writing to stdout (logger sink, Console.WriteLine, library banner).
  2. HTTP 404: path mismatch — app.MapMcp() is root, app.MapMcp("/mcp") puts it under /mcp.
  3. Tool not appearing: missing [McpServerToolType] on the class, or no .WithToolsFromAssembly() / .WithTools<T>() registered.
  4. Args not bound: parameter names must match the JSON-RPC arguments keys; complex types bind via System.Text.Json.
  5. Sampling/elicitation/roots failing: these legacy server-to-client calls can't run on current-protocol HTTP — migrate to the multi-round-trip InputRequiredException pattern, or (legacy paths only) set Stateless = false, knowing that pins HTTP clients to a down-level initialize revision. Also check the client actually advertises the capability.
  6. MCP9005 build warnings after upgrading to 2.x: the code uses deprecated roots/sampling/logging APIs. Plan the migration; suppress only temporarily.

Still stuck? Point the user at the EverythingServer sample — it exercises every feature.

Install

Need this version hardened?

Harden your own skills

Bring Chainguard's hardening pipeline to your first-party skills — custom rulesets, automated remediation, full audit trail.

Provenance

Upstream source

—

Upstream commita3e55ed11f6f66eb9073d3145450bfcd78f641dd
OCI reference

—

License

MIT

Files

HARDENING.md

LICENSE

SKILL.md

references/client.md

references/elicitation.md

references/mcp-apps.md

references/packages.md

references/prompt-primitive.md

references/resource-primitive.md

references/roots.md

references/sampling.md

references/server-features.md

references/testing.md

references/tool-primitive.md

references/transport-http.md

references/transport-stdio.md


The trusted source for open source

Talk to an expert
PrivacyTerms

Product

Chainguard ContainersChainguard LibrariesChainguard VMsChainguard OS PackagesChainguard ActionsChainguard Agent SkillsIntegrationsPricing
© 2026 Chainguard, Inc. All Rights Reserved.
Chainguard® and the Chainguard logo are registered trademarks of Chainguard, Inc. in the United States and/or other countries.
The other respective trademarks mentioned on this page are owned by the respective companies and use of them does not imply any affiliation or endorsement.