Platform AI v0.1.0

C# MCP Server Creation

Create MCP servers using the C# SDK and .NET project templates. Covers scaffolding, tool/prompt/resource implementation, and transport configuration for stdio and HTTP. USE FOR: creating new MCP server projects, scaffolding with dotnet new mcpserver, adding MCP tools/prompts/resources, choosing stdio vs HTTP transport, configuring MCP hosting in Program.cs, setting up ASP.NET Core MCP endpoints with MapMcp. DO NOT USE FOR: debugging or running existing servers (use mcp-csharp-debug), writing tests (use mcp-csharp-test), publishing or deploying (use mcp-csharp-publish), building MCP clients, non-.NET MCP servers.

Workflow

> Commit strategy: Commit after completing each step so scaffolding and implementation are separately reviewable.

Step 1: Verify prerequisites

  1. Confirm .NET 10+ SDK: dotnet --version (install from https://dotnet.microsoft.com if < 10.0)
  1. Check if the MCP server template is already installed:

``bash dotnet new list mcpserver ` If "No templates found" → install: dotnet new install Microsoft.McpServer.ProjectTemplates`

Step 2: Choose transport

| Choose stdio if… | Choose HTTP if… | |----------------------|---------------------| | Local CLI tool or IDE plugin | Cloud/web service deployment | | Single user at a time | Multiple simultaneous clients | | Running as subprocess (VS Code, GitHub Copilot) | Cross-network access needed | | Simpler setup, no network config | Containerized deployment (Docker/Azure) |

Default: stdio — simpler, works for most local development. Users can add HTTP later.

Step 3: Scaffold the project

stdio server:

dotnet new mcpserver -n <ProjectName>

If the template times out or is unavailable, use dotnet new console -n <ProjectName> and add dotnet add package ModelContextProtocol.

HTTP server:

dotnet new web -n <ProjectName>
cd <ProjectName>
dotnet add package ModelContextProtocol.AspNetCore

This is the recommended approach — faster and more reliable than the template. The template also supports HTTP via dotnet new mcpserver -n <ProjectName> --transport remote, but dotnet new web gives you more control over the project structure.

Template flags reference: --transport local (stdio, default), --transport remote (ASP.NET Core HTTP), --aot, --self-contained.

Step 4: Implement tools

Tools are the primary way MCP servers expose functionality. Add a class with [McpServerToolType] and methods with [McpServerTool]:

using ModelContextProtocol.Server;
using System.ComponentModel;

[McpServerToolType]
public static class MyTools
{
    [McpServerTool, Description("Brief description of what the tool does.")]
    public static async Task<string> DoSomething(
        [Description("What this parameter controls")] string input,
        CancellationToken cancellationToken = default)
    {
        // Implementation
        return $"Result: {input}";
    }
}

Critical rules:

  • Every tool method must have a [Description] attribute — LLMs use this to decide when to call the tool
  • Every parameter must have a [Description] attribute
  • Accept CancellationToken in all async tools
  • Use [McpServerTool(Name = "custom_name")] only if the default method name is unclear

DI injection patterns — the SDK supports two styles:

  1. Method parameter injection (static class): DI services appear as method parameters. The SDK resolves them automatically — they do not appear in the tool schema.
  1. Constructor injection (non-static class): Use when tools need shared state or multiple services:
[McpServerToolType]
public class ApiTools(HttpClient httpClient, ILogger<ApiTools> logger)
{
    [McpServerTool, Description("Fetch a resource by ID.")]
    public async Task<string> FetchResource(
        [Description("Resource identifier")] string id,
        CancellationToken cancellationToken = default)
    {
        logger.LogInformation("Fetching {Id}", id);
        return await httpClient.GetStringAsync($"/api/{id}", cancellationToken);
    }
}

Register services in Program.cs:

var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(options =>
    options.LogToStandardErrorThreshold = LogLevel.Trace);

builder.Services.AddHttpClient();        // registers IHttpClientFactory + HttpClient
// ILogger<T> is registered by default — no extra setup needed.

builder.Services.AddMcpServer()
    .WithStdioServerTransport()
    .WithToolsFromAssembly();            // discovers non-static [McpServerToolType] classes

await builder.Build().RunAsync();

For the full attribute reference, return types, DI injection, and builder API patterns, see references/api-patterns.md.

Step 5: Add prompts and resources (optional)

Prompts — reusable LLM interaction templates:

[McpServerPromptType]
public static class MyPrompts
{
    [McpServerPrompt, Description("Summarize content into one sentence.")]
    public static ChatMessage Summarize(
        [Description("Content to summarize")] string content) =>
        new(ChatRole.User, $"Summarize this into one sentence: {content}");
}

Resources — data the LLM can read:

[McpServerResourceType]
public static class MyResources
{
    [McpServerResource(UriTemplate = "config://app", Name = "App Config",
        MimeType = "application/json"), Description("Application configuration")]
    public static string GetConfig() => JsonSerializer.Serialize(AppConfig.Current);
}

Step 6: Configure Program.cs

stdio transport:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;

var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(options =>
    options.LogToStandardErrorThreshold = LogLevel.Trace); // CRITICAL: stderr only

builder.Services.AddMcpServer()
    .WithStdioServerTransport()
    .WithToolsFromAssembly();

await builder.Build().RunAsync();

HTTP transport:

using ModelContextProtocol.Server;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
    .WithHttpTransport()
    .WithToolsFromAssembly();

// Register services your tools need via DI
// builder.Services.AddHttpClient();
// builder.Services.AddSingleton<IMyService, MyService>();

var app = builder.Build();
app.MapMcp();                     // exposes MCP endpoint at /mcp (Streamable HTTP)
app.MapGet("/health", () => "ok"); // health check for container orchestrators
app.Run();

Key HTTP details: MapMcp() defaults to /mcp path. For containers, set ASPNETCORE_URLS=http://+:8080 and EXPOSE 8080. The MCP HTTP protocol uses Streamable HTTP — no special client config needed beyond the URL.

For transport configuration details (stateless mode, auth, path prefix, HttpContextAccessor), see references/transport-config.md.

Step 7: Verify the server starts

cd <ProjectName>
dotnet build
dotnet run

For stdio: the process starts and waits for JSON-RPC input on stdin. For HTTP: the server listens on the configured port.

Related skills

Framework AI

Use ML.NET to train, evaluate, or integrate machine-learning models into .NET applications with realistic data preparation, inference, and deployment expectations.

Microsoft.ML.*

Build .NET AI agents and multi-agent workflows with Microsoft Agent Framework using the right agent type, threads, tools, workflows, hosting protocols, and enterprise guardrails.

Microsoft.Agents.*

Build AI-enabled .NET applications with Semantic Kernel using services, plugins, prompts, and function-calling patterns that remain testable and maintainable.

Microsoft.SemanticKernel.*