Skip to content
Go back

从零搭建你的第一个 MCP 服务端(C# 快速上手)

想用 C# 搭一个 MCP 服务端,最快有用的路径是本地 stdio 服务端:一个控制台应用、官方 C# SDK、通用 Host、加一个 AI 宿主能发现和调用的工具方法。本文不讲远程传输、认证和部署,只讲第一个能本地跑通、能接入 VS Code 或 Claude Desktop 的 MCP 服务端。

先理解 stdio 的形状

本地 stdio MCP 服务端由宿主作为子进程启动。宿主通过 stdin 向你的进程写 JSON-RPC 消息,你的服务端通过 stdout 回 JSON-RPC 响应。

这个设计简单且可移植,但有一个首次实现时几乎必踩的坑:stdout 不是日志通道。任何 Console.WriteLine、启动横幅、调试输出或写入 stdout 的默认日志都会污染协议流。

所以第一条规则:用 ModelContextProtocol 做本地 stdio 服务端,用 Microsoft.Extensions.Hosting 让配置方式跟现代 .NET 一致,日志走 stderr,工具保持小、描述清晰、调用安全。

创建项目

从普通控制台应用开始:

dotnet new console -n FirstMcpServer
cd FirstMcpServer
dotnet add package ModelContextProtocol --version 1.4.0
dotnet add package Microsoft.Extensions.Hosting

包的选择有讲究。ModelContextProtocol.Core 是更精简的客户端和底层 API。ModelContextProtocol.AspNetCore 用于 HTTP 模式。对本地 MCP 服务端,ModelContextProtocol 是正确的包——它包含 Host 和依赖注入扩展、stdio 传输支持、以及基于属性的工具发现。

写 Program.cs

用最少的代码搭起一个可工作的服务端:

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

var builder = Host.CreateApplicationBuilder(args);

builder.Logging.AddConsole(options =>
{
    options.LogToStandardErrorThreshold = LogLevel.Trace;
});

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

await builder.Build().RunAsync();

[McpServerToolType]
public static class EchoTools
{
    [McpServerTool]
    [Description("Echoes the provided message back to the caller.")]
    public static string Echo(
        [Description("The message to echo.")] string message) =>
        $"Hello from C#: {message}";
}

逐行解释:

属性是发现契约。[McpServerToolType] 标记包含工具的类,[McpServerTool] 标记可被调用的方法,[Description] 告诉宿主和模型这个工具做什么、每个参数是什么意思。

写一个稍有用的工具

Echo 能证明服务端跑通了,但现实中的第一个工具应该做点有用的事而不碰生产系统:

[McpServerToolType]
public static class DeveloperTools
{
    [McpServerTool]
    [Description("Formats a concise engineering work item summary.")]
    public static string FormatWorkItem(
        [Description("The short title of the work item.")] string title,
        [Description("The priority from 1 to 5, where 1 is highest.")] int priority,
        [Description("Optional implementation note or risk.")] string? note = null)
    {
        var normalizedPriority = Math.Clamp(priority, 1, 5);
        var noteLine = string.IsNullOrWhiteSpace(note)
            ? "No additional implementation notes."
            : note.Trim();

        return $"Title: {title.Trim()}\n" +
               $"Priority: P{normalizedPriority}\n" +
               $"Note: {noteLine}";
    }
}

参数描述是契约的一部分,不是装饰。一个模糊的参数名比如 text 给模型的指引远不如 “The short title of the work item” 清晰。

本地运行

dotnet run --project .\FirstMcpServer.csproj

别指望看到一个 web 服务器的欢迎消息。stdio MCP 服务端在 stdin 上等待协议消息。直接跑看起来像卡住了——这可能是正常的。有意义的测试是:宿主能不能启动它、初始化 MCP 会话、列出工具、调用其中一个。

接入 VS Code

在 VS Code 的 mcp.json 里配置:

{
  "servers": {
    "first-mcp-server": {
      "type": "stdio",
      "command": "dotnet",
      "args": [
        "run",
        "--project",
        "C:\\dev\\FirstMcpServer\\FirstMcpServer.csproj"
      ]
    }
  }
}

用项目文件的绝对路径。宿主重新加载配置后,应该能启动进程并发现 EchoFormatWorkItem 等工具。

接入 Claude Desktop

Claude Desktop 使用类似 JSON 配置:

{
  "mcpServers": {
    "first-mcp-server": {
      "command": "dotnet",
      "args": [
        "run",
        "--project",
        "C:\\dev\\FirstMcpServer\\FirstMcpServer.csproj"
      ]
    }
  }
}

注意的是,VS Code 和 Claude Desktop 都只是宿主示例。服务端的契约是 MCP。同一个本地 stdio 模式可以被任何兼容客户端消费,只要它们能启动你的进程并说 MCP 协议。

常见首次运行错误

协议损坏:宿主报非法 JSON、意外 token 或初始化失败——先查 stdout 污染。搜代码里有没有 Console.WriteLine,有没有写入 stdout 的日志提供者,有没有启动横幅。

工具发现不到:宿主连上了但没显示你的工具——确认三点:类有 [McpServerToolType],方法有 [McpServerTool],服务端注册了 .WithToolsFromAssembly()

启动配置问题:宿主无法启动服务端——手动在终端跑同样的命令。验证项目路径,验证 SDK 已安装,验证 dotnet restore 成功。

包版本不对:复制代码编译不过——检查 ModelContextProtocol 的安装版本。SDK 在快速演进,本文基于稳定版 1.4.0。

这个例子够用和不够用时

够用:个人或团队的开发者工具,跑在 IDE 或桌面 AI 宿主旁边。适合仓库助手、本地诊断、格式化工具、只读查询工具和安全内部流程的包装器。

不够用:需要共享远程服务、浏览器访问、集中认证、多租户控制或云部署。这些需求会推向 ASP.NET Core、Streamable HTTP、CORS、认证和运维可观测性。

自检清单

结语

搭第一个 C# MCP 服务端的最简单方式就是保持无聊:控制台应用、ModelContextProtocolMicrosoft.Extensions.Hosting、stdio 传输、程序集扫描工具、清晰描述、日志走 stderr。这给你一个可工作的基线,不拖入远程托管、认证、部署。

基线跑通之后再做明智取舍:加更多有用的工具、决定服务端是否留在本地、需不需要打包、该不该变成远程。但先把 stdio 服务端跑通——它验证了整个契约从头到尾是通的。

参考


Tags


Previous

什么是单体架构?三种类型用 C# 讲清楚

Next

ASP.NET Core 自定义用户管理:从注册到管理员面板