mcp-csharp-create

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

C# MCP Server Creation

C# MCP 服务器创建

Create Model Context Protocol servers using the official C# SDK (
ModelContextProtocol
NuGet package) and the
dotnet new mcpserver
project template. Servers expose tools, prompts, and resources that LLMs can discover and invoke via the MCP protocol.
使用官方C# SDK(
ModelContextProtocol
NuGet包)和
dotnet new mcpserver
项目模板创建Model Context Protocol服务器。服务器会对外暴露工具、提示词和资源,LLM可以通过MCP协议发现并调用这些能力。

When to Use

适用场景

  • Starting a new MCP server project from scratch
  • Adding tools, prompts, or resources to an existing MCP server
  • Choosing between stdio (
    --transport local
    ) and HTTP (
    --transport remote
    ) transport
  • Setting up ASP.NET Core hosting for an HTTP MCP server
  • Wrapping an external API or service as MCP tools
  • 从零开始启动新的MCP服务器项目
  • 向现有MCP服务器添加工具、提示词或资源
  • 在stdio(
    --transport local
    )和HTTP(
    --transport remote
    )传输方式之间做选择
  • 为HTTP MCP服务器设置ASP.NET Core托管
  • 将外部API或服务封装为MCP工具

Stop Signals

禁用信号

  • Server already exists and needs debugging? → Use
    mcp-csharp-debug
  • Need tests or evaluations? → Use
    mcp-csharp-test
  • Ready to publish? → Use
    mcp-csharp-publish
  • Building an MCP client, not a server → This skill is server-side only
  • 服务器已存在需要调试? → 使用
    mcp-csharp-debug
  • 需要测试或评估? → 使用
    mcp-csharp-test
  • 准备发布? → 使用
    mcp-csharp-publish
  • 构建MCP客户端而非服务器 → 本技能仅适用于服务端开发

Inputs

输入项

InputRequiredDescription
Transport typeYes
stdio
(local/CLI) or
http
(remote/web). Ask user if not specified — default to stdio
Project nameYesPascalCase name for the project (e.g.,
WeatherMcpServer
)
.NET SDK versionRecommended.NET 10.0+ required. Check with
dotnet --version
Service/API to wrapRecommendedExternal API or service the tools will interact with
输入项是否必填描述
传输类型
stdio
(本地/CLI)或
http
(远程/网页端)。如果用户未指定请主动询问,默认选择stdio
项目名称采用大驼峰命名的项目名(例如
WeatherMcpServer
.NET SDK 版本推荐要求.NET 10.0及以上版本,可通过
dotnet --version
命令检查
待封装的服务/API推荐工具将要交互的外部API或服务

Workflow

工作流

Commit strategy: Commit after completing each step so scaffolding and implementation are separately reviewable.
提交策略: 每完成一步就提交代码,这样脚手架搭建和功能实现可以分开评审。

Step 1: Verify prerequisites

步骤1:确认前置依赖

  1. Confirm .NET 10+ SDK:
    dotnet --version
    (install from https://dotnet.microsoft.com if < 10.0)
  2. 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
  1. 确认已安装.NET 10+ SDK:执行
    dotnet --version
    命令,如果版本低于10.0可以从https://dotnet.microsoft.com 下载安装
  2. 检查是否已安装MCP服务器模板:
    bash
    dotnet new list mcpserver
    如果提示"No templates found" → 执行安装命令:
    dotnet new install Microsoft.McpServer.ProjectTemplates

Step 2: Choose transport

步骤2:选择传输方式

Choose stdio if…Choose HTTP if…
Local CLI tool or IDE pluginCloud/web service deployment
Single user at a timeMultiple simultaneous clients
Running as subprocess (VS Code, GitHub Copilot)Cross-network access needed
Simpler setup, no network configContainerized deployment (Docker/Azure)
Default: stdio — simpler, works for most local development. Users can add HTTP later.
符合以下场景选择 stdio符合以下场景选择 HTTP
本地CLI工具或IDE插件云端/网页服务部署
同一时间仅单用户使用支持多客户端同时访问
作为子进程运行(VS Code、GitHub Copilot)需要跨网络访问
配置更简单,无需网络设置容器化部署(Docker/Azure)
默认选择: stdio —— 配置更简单,适合大多数本地开发场景,用户后续也可以自行添加HTTP支持。

Step 3: Scaffold the project

步骤3:搭建项目脚手架

stdio server:
bash
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:
bash
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
.
stdio服务器:
bash
dotnet new mcpserver -n <ProjectName>
如果模板加载超时或不可用,可以使用
dotnet new console -n <ProjectName
创建控制台项目,再执行
dotnet add package ModelContextProtocol
安装依赖。
HTTP服务器:
bash
dotnet new web -n <ProjectName>
cd <ProjectName>
dotnet add package ModelContextProtocol.AspNetCore
这是推荐的实现方式,比模板更快更可靠。你也可以通过模板命令
dotnet new mcpserver -n <ProjectName> --transport remote
创建HTTP服务器,但
dotnet new web
方式对项目结构的可控性更强。
模板参数参考:
--transport local
(stdio,默认值)、
--transport remote
(ASP.NET Core HTTP)、
--aot
--self-contained

Step 4: Implement tools

步骤4:实现工具功能

Tools are the primary way MCP servers expose functionality. Add a class with
[McpServerToolType]
and methods with
[McpServerTool]
:
csharp
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.
  2. Constructor injection (non-static class): Use when tools need shared state or multiple services:
csharp
[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:
csharp
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.
工具是MCP服务器对外暴露能力的主要方式。新增一个带
[McpServerToolType]
特性的类,并为类方法添加
[McpServerTool]
特性:
csharp
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}";
    }
}
核心规则:
  • 每个工具方法必须添加
    [Description]
    特性 —— LLM会通过这个描述判断什么时候调用该工具
  • 每个参数必须添加
    [Description]
    特性
  • 所有异步工具都要接收
    CancellationToken
    参数
  • 仅当默认方法名含义不清晰时,才使用
    [McpServerTool(Name = "custom_name")]
    自定义工具名
DI注入模式 —— SDK支持两种注入风格:
  1. 方法参数注入(静态类): DI服务作为方法参数传入,SDK会自动解析这些服务,它们不会出现在工具的Schema中。
  2. 构造函数注入(非静态类): 当工具需要共享状态或依赖多个服务时使用:
csharp
[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);
    }
}
在Program.cs中注册服务:
csharp
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();
完整的特性参考、返回类型、DI注入、构建API模式请查看 references/api-patterns.md

Step 5: Add prompts and resources (optional)

步骤5:添加提示词和资源(可选)

Prompts — reusable LLM interaction templates:
csharp
[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:
csharp
[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);
}
提示词 —— 可复用的LLM交互模板:
csharp
[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}");
}
资源 —— LLM可以读取的数据:
csharp
[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

步骤6:配置Program.cs

stdio transport:
csharp
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:
csharp
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.
stdio传输:
csharp
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传输:
csharp
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();
HTTP核心说明:
MapMcp()
默认暴露的路径是
/mcp
。容器部署时请设置
ASPNETCORE_URLS=http://+:8080
并开放
8080
端口。MCP HTTP协议使用可流HTTP传输,除了URL之外无需额外的客户端配置。
传输配置详情(无状态模式、鉴权、路径前缀、
HttpContextAccessor
)请查看
references/transport-config.md

Step 7: Verify the server starts

步骤7:验证服务器启动

bash
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.
bash
cd <ProjectName>
dotnet build
dotnet run
stdio模式:进程启动后会等待stdin输入的JSON-RPC请求。 HTTP模式:服务器会在配置的端口上监听请求。

Validation

校验清单

  • Project builds with no errors (
    dotnet build
    )
  • All tool classes have
    [McpServerToolType]
    attribute
  • All tool methods have
    [McpServerTool]
    and
    [Description]
    attributes
  • All parameters have
    [Description]
    attributes
  • stdio: logging directed to stderr, not stdout
  • HTTP:
    app.MapMcp()
    is called in Program.cs
  • Server starts successfully with
    dotnet run
  • 项目编译无错误(执行
    dotnet build
  • 所有工具类都添加了
    [McpServerToolType]
    特性
  • 所有工具方法都添加了
    [McpServerTool]
    [Description]
    特性
  • 所有参数都添加了
    [Description]
    特性
  • stdio模式:日志输出到stderr而非stdout
  • HTTP模式:Program.cs中调用了
    app.MapMcp()
  • 执行
    dotnet run
    服务器可以正常启动

Common Pitfalls

常见问题

PitfallSolution
stdio server outputs garbage or hangsLogging to stdout corrupts JSON-RPC protocol. Set
LogToStandardErrorThreshold = LogLevel.Trace
Tool not discovered by LLM clientsMissing
[McpServerToolType]
on the class or
[McpServerTool]
on the method. Verify
.WithToolsFromAssembly()
in Program.cs
LLM doesn't understand when to use a toolAdd clear
[Description]
attributes on both the method and all parameters
WithToolsFromAssembly()
fails in AOT
Reflection-based discovery is incompatible with Native AOT. Use
.WithTools<MyTools>()
instead
Parameters not appearing in tool schema
CancellationToken
,
IMcpServer
, and DI services are injected automatically — they do not appear in the schema. Only parameters with
[Description]
are exposed
HTTP server returns 404
app.MapMcp()
must be called. Check the request path matches the configured route
问题解决方案
stdio服务器输出乱码或卡住日志输出到stdout会破坏JSON-RPC协议,请设置
LogToStandardErrorThreshold = LogLevel.Trace
LLM客户端无法发现工具类缺少
[McpServerToolType]
特性或方法缺少
[McpServerTool]
特性,请确认Program.cs中调用了
.WithToolsFromAssembly()
LLM不知道什么时候调用工具请为方法和所有参数添加清晰的
[Description]
特性描述
AOT模式下
WithToolsFromAssembly()
调用失败
基于反射的发现机制和Native AOT不兼容,请改用
.WithTools<MyTools>()
注册
参数没有出现在工具Schema中
CancellationToken
IMcpServer
和DI服务会自动注入,不会出现在Schema中,只有带
[Description]
的参数才会对外暴露
HTTP服务器返回404必须调用
app.MapMcp()
,请检查请求路径和配置的路由是否匹配

Related Skills

相关技能

  • mcp-csharp-debug
    — Run, debug, and test with MCP Inspector
  • mcp-csharp-test
    — Unit tests, integration tests, evaluations
  • mcp-csharp-publish
    — NuGet, Docker, Azure deployment
  • mcp-csharp-debug
    —— 使用MCP Inspector运行、调试和测试
  • mcp-csharp-test
    —— 单元测试、集成测试、评估
  • mcp-csharp-publish
    —— NuGet、Docker、Azure部署

Reference Files

参考文件

  • references/api-patterns.md — Complete attribute reference, return types, DI injection, builder API, dynamic tools, experimental APIs. Load when: implementing tools, prompts, or resources beyond the basic patterns shown above.
  • references/transport-config.md — Detailed transport configuration: stateless HTTP mode, OAuth/auth, custom path prefix,
    HttpContextAccessor
    , OpenTelemetry observability. Load when: configuring advanced transport options or authentication.
  • references/api-patterns.md —— 完整的特性参考、返回类型、DI注入、构建API、动态工具、实验性API。加载时机: 实现超出上述基础模式的工具、提示词或资源时
  • references/transport-config.md —— 详细的传输配置:无状态HTTP模式、OAuth/鉴权、自定义路径前缀、
    HttpContextAccessor
    、OpenTelemetry可观测性。加载时机: 配置高级传输选项或身份验证时

More Info

更多信息