MCP服务器测试工具

McpServerFactory是一个用于.NET Model Context Protocol (MCP)服务器的内存集成测试工具,支持在单一进程中通过内存管道连接真实客户端和服务器,便于依赖替换和调试。

  • by diomonogatari
  • C#
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 diomonogatari/mcp-server-factory

使用说明

McpServerFactory

内存集成 测试框架 为。NET模型上下文协议(MCP)服务器。

MCP服务器的真正契约不是与代码的契约,而是与模型的契约。 工具名称 JSON模式、代理读取的描述、它看到的错误文本、采样 它引发的往返旅行。McpServerFactory启动您的服务器 过程中的 并连接a 真实的 McpClient 通过内存管道,您可以在简单的单元测试中对该合约进行断言: 两边都有断点,依赖关系被替换为假货,没有子进程,没有端口,没有Docker, 没有现场模特。

思考 WebApplicationFactory<T> --但对于MCP来说。

// one process: a real client ⇄ your real server, over in-memory pipes
McpTestClient client = await factory.CreateTestClientAsync();

Assert.Equal("hello", await client.CallToolForTextAsync(
    "echo", new Dictionary<string, object?> { ["message"] = "hello" }));

您可以实际验证的内容

  • 一个工具 存在,它的输入模式/描述就是你所想的(GetToolAsync, McpAssert.ToolExistsAsync).
  • 工具返回右侧 文本 或 键入JSON (CallToolForTextAsync, CallToolForJsonAsync<T>).
  • 一个工具 未能如愿 — IsError 结果抛出而不是悄悄通过测试(CallToolExpectingErrorAsync).
  • 服务器启动采样 做正确的事,用你控制的假模型来回答(FakeSamplingHandler).
  • 预期 通知 火灾——记录、进度、列表已更改(NotificationRecorder).
  • 你的 真实DI图 连接起来,模拟慢速/外部比特(configureServices / ConfigureHost).

范围: 默认情况下,工厂托管 您注册的工具/资源/提示类 通过内存传输。它不会自动运行您的服务器 Program.cs.锻炼 您的真实组合根(配置、选项、托管服务),请使用 ConfigureHost 钩子。

安装

dotnet add package McpServerFactory

模板脚手架

安装模板包以引导MCP集成测试项目:

dotnet new install McpServerFactory.Templates
dotnet new mcp-itest -n MyServer.Tests

这 mcp-itest 模板接受 --McpServerFactoryVersion 以覆盖 McpServerFactory 包版本;默认跟踪的版本 您安装的模板包。

快速开始

using McpServerFactory.Testing;
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Server;
using Xunit;

[McpServerToolType]
public sealed class EchoTools
{
    [McpServerTool(Name = "echo")]
    public string Echo(string message) => message;
}

public class EchoTests
{
    [Fact]
    public async Task Echo_ReturnsInput()
    {
        await using McpServerIntegrationFactory factory = new(
            configureMcpServer: builder => builder.WithTools<EchoTools>());

        // The factory owns the client; you do not dispose it yourself.
        McpTestClient client = await factory.CreateTestClientAsync();

        string text = await client.CallToolForTextAsync(
            "echo",
            new Dictionary<string, object?> { ["message"] = "hello" });

        Assert.Equal("hello", text);
    }
}

使用xUnit?同伴 McpServerFactory.Xunit 包裹 每个测试类通过以下方式启动服务器一次 IClassFixture<T>.

为什么使用这个库

  • 快速且正在处理中 --没有子进程、端口、Docker或stdio管道。
  • 真实协议流 --一个真实的 McpClient 通过真实(内存中)传输: tools/list, tools/call、资源、提示和服务器启动的采样都是端到端运行的。
  • 易依赖性覆盖 --替代服务通过 configureServices.
  • 可调试 --在两侧设置断点;这都是一个过程。
  • 与框架无关的核心 --与xUnit、NUnit和MSTest配合使用。

如何比较

方法 速度 无进程/端口 两侧断点 DI替换 练习真实 Program.cs
McpServerFactory 快 ✅ ✅ ✅ 通过 ConfigureHost
stdio子流程+ McpClient 缓慢 ❌ 仅限服务器 ❌ ✅
未加工管道+ StreamClientTransport 快 ✅ ✅ ✅ (手动) ❌
MCP检查员(手动) n/a ❌ ❌ ❌ ✅

使用它时 您希望对您的工具/资源/提示进行快速、自动化、过程中的测试,以及 具有依赖替换的处理程序。 改用stdio子流程 当你必须 验证实际发布的二进制文件、其实际传输配置或进程级启动。

在野外使用

stash-mcp --用于Bitbucket的40工具MCP服务器 服务器——使用McpServerFactory测试其工具。它对工厂进行子类化,注册其真正的工具 组装,并将实时Bitbucket客户端、缓存和弹性服务替换为假货,因此 整个工具表面在过程中进行锻炼,而无需接触Bitbucket实例:

public sealed class StashMcpTestFactory(Action<StashMcpTestFactory>? configureMocks = null)
    : McpServerFactory.Testing.McpServerFactory
{
    public IBitbucketClient BitbucketClient { get; } = Substitute.For<IBitbucketClient>();

    protected override void ConfigureMcpServer(IMcpServerBuilder builder) =>
        builder.WithToolsFromAssembly(typeof(ProjectTools).Assembly);

    protected override void ConfigureServices(IServiceCollection services) =>
        services.AddSingleton(BitbucketClient); // ...plus cache, settings, resilience fakes
}

测试工具、资源、提示和结构化输出

McpTestClient 包裹真实 McpClient 使用测试形状的助手(所有分页都是安全的):

McpTestClient client = await factory.CreateTestClientAsync();

string[] tools   = await client.GetToolNamesAsync();
string greeting  = await client.CallToolForTextAsync("greet");
MyDto result     = await client.CallToolForJsonAsync<MyDto>("compute", args); // structured or JSON-text
string contents  = await client.ReadResourceTextAsync("resource://config");
string[] prompts = await client.GetPromptNamesAsync();

工具故障(IsError)不再默默地流逝: CallToolForTextAsync 投掷 McpToolCallException,以及 CallToolExpectingErrorAsync 断言负路径。这 框架无关 McpAssert 助手(ToolExistsAsync, Succeeded, IsError, TextEquals) 在任何测试框架下工作。

测试服务器启动采样

如果您的服务器调用模型(服务器发起的采样),则连接一个确定性的假模型进行测试 永远不需要真正的法学硕士:

FakeSamplingHandler sampling = FakeSamplingHandler.Returning("42");

await using McpServerIntegrationFactory factory = new(
    configureMcpServer: builder => builder.WithTools<AskTools>(),
    options: new McpServerFactoryOptions
    {
        ConfigureClient = client => client.UseSamplingHandler(sampling),
    });

McpTestClient client = await factory.CreateTestClientAsync();
string answer = await client.CallToolForTextAsync(
    "ask", new Dictionary<string, object?> { ["question"] = "..." });

Assert.Single(sampling.ReceivedRequests); // assert what the server asked the model

ConfigureClient 暴露全部 McpClientOptions,所以你也可以声明启发、根、, 或通知处理程序。捕获服务器发送的通知(日志记录、进度、列表更改) NotificationRecorder.Attach(client.Inner) 和 await recorder.WaitForMethodAsync(...).

测试你的真实作文根

默认情况下,工厂托管您注册的类。要执行相同的注册,请 服务器的 Program.cs 使用--实际配置绑定、选项和托管服务--供应 ConfigureHost工厂始终拥有MCP服务器注册和内存传输, 所以配置一切 除了 运输;通过注册工具/资源/提示 configureMcpServer:

await using McpServerIntegrationFactory factory = new(
    configureMcpServer: builder => builder.WithTools<MyTools>(),
    options: new McpServerFactoryOptions
    {
        ConfigureHost = builder =>
        {
            builder.Configuration.AddInMemoryCollection(/* test config */);
            builder.Services.AddMyDomainServices();   // your real registration, minus the transport
        },
    });

使用xUnit每类启动一次

安装配套包并衍生夹具:

dotnet add package McpServerFactory.Xunit
using McpServerFactory.Testing.Xunit;

public sealed class EchoFixture : McpServerFixture
{
    protected override void ConfigureMcpServer(IMcpServerBuilder builder) => builder.WithTools<EchoTools>();
}

public sealed class EchoTests(EchoFixture fixture) : IClassFixture<EchoFixture>
{
    [Fact]
    public async Task Echoes() =>
        Assert.Equal("hi", await fixture.TestClient.CallToolForTextAsync(
            "echo", new Dictionary<string, object?> { ["message"] = "hi" }));
}

行为保证

  • CreateClientAsync / CreateTestClientAsync 每个工厂实例都是线程安全和幂等的。
  • 并发呼叫返回相同的已连接客户端; 工厂拥有并处置它 --你知道 不需要自己处理退回的客户。
  • 启动失败不会泄漏临时主机实例或其管道。
  • DisposeAsync 多次调用是安全的,并且受以下限制 ShutdownTimeout (它不会挂起来 拆卸)。
  • CreateClientAsync 投掷 ObjectDisposedException 处置后。
  • 需要独立的服务器实例(隔离)?创建多个工厂实例——每个实例都有自己的 自己的主人。单个工厂公开一个内存会话。

兼容性和支持

  • 目标框架: net8.0, net9.0, net10.0.
  • MCP SDK依赖关系: ModelContextProtocol 0.4.0-preview.3.
  • 兼容性承诺:每个软件包版本都会根据所有固定的MCP SDK版本进行验证 目标框架。
  • 升级策略:MCP SDK凸起是明确的,并在 更改日志.md.虽然 MCP SDK正在预览中, McpServerFactory 停留 0.x 步调一致地颠簸着。
McpServerFactory MCP SDK 目标框架
0.2.x 0.4.0-评论.3 净8.0,净9.0,净10.0
0.1.x 0.4.0-评论.3 净值10.0

API概述

  • McpServerFactory / McpServerIntegrationFactory
    • 启动进程内服务器主机;通过以下方式创建连接的客户端 CreateClientAsync() 或一个 工厂自有包装 CreateTestClientAsync().
    • 暴露 Services 用于启动后的DI验证。
  • McpServerFactoryOptions
    • 配置服务器标识、超时、指令、日志记录、客户端(ConfigureClient),以及 宿主组成根(ConfigureHost).
  • McpTestClient
    • 带有工具、资源、提示、结构化输出和服务器元数据帮助程序的包装器。
  • FakeSamplingHandler / NotificationRecorder / McpAssert
    • 确定性采样、通知捕获和框架无关断言。
  • McpServerFactory.Xunit (单独包装)
    • McpServerFixture 为了 IClassFixture<T> 每节课开机一次。

登录测试输出

默认情况下,主机日志记录提供程序被抑制,以保持测试输出的干净。 要在测试期间启用自定义日志记录,请执行以下操作:

using Microsoft.Extensions.Logging;

var options = new McpServerFactoryOptions
{
    SuppressHostLogging = false,
    ConfigureLogging = logging => logging.SetMinimumLevel(LogLevel.Debug),
};

发行说明

看 更改日志.md 查看发布历史和即将发生的更改。

样品

存储库布局

  • src/McpServerFactory --可重用工厂库(与框架无关的核心)。
  • src/McpServerFactory.Xunit — xUnit 设备 (McpServerFixture).
  • tests/McpServerFactory.Tests --以单元/集成为重点的库测试。
  • templates/McpServerFactory.Templates — dotnet new 模板包。
  • samples/MinimalSmoke --可运行的示例控制台应用程序。
  • docs --架构说明和使用指南。