Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
3c528be
增加采样功能
walterlv Apr 3, 2026
57c1d0b
简化代码,添加采样功能的单元测试
walterlv Apr 3, 2026
c1f4f2f
添加人工测试的 MCP 采样工具
walterlv Apr 3, 2026
27efe1b
解决采样协议不被客户端认识的问题
walterlv Apr 3, 2026
c432af1
整理采样异常
walterlv Apr 3, 2026
e56fb0a
调整一些代码
walterlv Apr 7, 2026
b069d80
处理审查
walterlv Apr 7, 2026
e2c67fe
处理审查
walterlv Apr 7, 2026
da9028c
整理传输层读取请求或响应的分支
walterlv Apr 7, 2026
3db477f
避免不应该的双语注释
walterlv Apr 7, 2026
694a602
添加更多的调试日志
walterlv Apr 8, 2026
9acc4e6
兼容 MCP Inspector 的采样拒绝
walterlv Apr 8, 2026
3010535
记录客户端错误
walterlv Apr 8, 2026
e2eba44
修复指令中的协议版本
walterlv Apr 8, 2026
8dceafe
AI 增加的屎山代码:修复 SSE 消息流向
walterlv Apr 8, 2026
733ca8c
简化传输层的实现
walterlv Apr 8, 2026
c6292f6
处理遗留的双语问题
walterlv Apr 8, 2026
10002e6
重构以简化传输层的代码
walterlv Apr 8, 2026
128aa76
更新文档记录
walterlv Apr 8, 2026
5963987
处理审查意见
walterlv Apr 8, 2026
52670d8
处理审查意见
walterlv Apr 8, 2026
4b18f12
处理审查意见
walterlv Apr 8, 2026
3933fbe
处理审查意见
walterlv Apr 8, 2026
19a5117
处理审查意见:修复并发竞态、重复请求ID、SSE心跳、以及初始化请求分类等问题
Copilot Apr 8, 2026
c96610f
调整心跳时间
walterlv Apr 8, 2026
9888474
处理审查意见:协议版本运算符比较、SSE并发写互斥、重复请求ID、双语注释、ToArray优化
Copilot Apr 8, 2026
11e92fb
处理审查意见
walterlv Apr 8, 2026
a855395
修复 ServerTransportManager:PipeReader 资源释放和大小写不敏感属性查找
Copilot Apr 9, 2026
00bed63
使用更简单的 JsonElement,不需要 PipeReader
walterlv Apr 9, 2026
71549d1
我觉得没必要为了这个不规范的写法浪费性能
walterlv Apr 9, 2026
52f6e76
处理审查意见
walterlv Apr 9, 2026
0710443
添加辅助传输层日志记录的类(避免日志打太多)
walterlv Apr 9, 2026
9b5cd10
统一使用受管理的传输层原始日志记录,避免日志大幅影响控制台输出
walterlv Apr 9, 2026
bbb20b0
尝试消除竞态条件
walterlv Apr 9, 2026
3ba6c05
为 GET 添加日志
walterlv Apr 9, 2026
6032afa
记录更详细的原始日志
walterlv Apr 10, 2026
b2f9f65
ToString 可以输出文本内容
walterlv Apr 10, 2026
2f754ca
允许不传入 params
walterlv Apr 17, 2026
13ce538
对齐 MCP 传输层协议
walterlv Apr 27, 2026
750bebc
添加协议兼容与版本协商设计方案
walterlv Apr 27, 2026
cc5ee4e
协议兼容计划
walterlv Apr 27, 2026
bc99044
兼容 2024-11-05 版旧协议
walterlv Apr 27, 2026
c5112f5
默认兼容旧协议
walterlv Apr 27, 2026
156c96a
后续计划
walterlv Apr 27, 2026
7d3198c
版本兼容与版本协商
walterlv Apr 27, 2026
d200b58
代码审查和修复
walterlv Apr 27, 2026
ab43daf
核对官方 MCP 文档并修复实现问题
walterlv Apr 27, 2026
dc0e53c
删除已实现的两个计划
walterlv Apr 27, 2026
ec9bf9e
注释测试
walterlv Apr 27, 2026
a5e98fe
记录上游问题
walterlv Apr 30, 2026
ac1e063
加入 In-Process MCP
walterlv Apr 30, 2026
11dd8a6
补文档
walterlv Apr 30, 2026
bb03919
优化 CallToolResult.ToString 体验
walterlv Apr 30, 2026
e234f78
Json 序列化时,避免输出 `\uxxxx`
walterlv Apr 30, 2026
9e58f92
Add json serialize doc
walterlv May 8, 2026
cdd647f
IPC 计划
walterlv May 8, 2026
9822613
支持 IPC 传输层,支持 InProcess 一对多通信
walterlv May 9, 2026
5c78e66
确保可先创建后连接
walterlv May 9, 2026
ce426f9
简化一对多进程内 MCP 通信的代码结构
walterlv May 9, 2026
78bfe59
实现 MCP 客户端的 meta 注入
walterlv May 9, 2026
2172330
初版文档
walterlv May 9, 2026
8429728
第二版文档
walterlv May 9, 2026
35236ec
DeepSeek V4 Pro 润色
walterlv May 9, 2026
ab4a663
翻译文档
walterlv May 9, 2026
b200a39
提供一个让业务使用的扩展方法
walterlv May 11, 2026
43526ae
因为涉及到生成源,所以项目不应自带命名空间,应该由文件自己携带
walterlv May 11, 2026
b4588d4
恢复版本
walterlv May 11, 2026
bda0e2e
文档说明集合不允许作为返回值
walterlv May 11, 2026
7eaaa02
增加 MCP 工具的依赖注入说明
walterlv May 12, 2026
fcb67d7
MCP over IPC 单元测试
walterlv May 12, 2026
539153f
补上先连的 IpcProvider 后开 MCP 服务的测试
walterlv May 12, 2026
20d9d8f
修复预先启动的 IpcProvider,MCP 无法响应的问题
walterlv May 12, 2026
b4c3dc7
修正预先启动的单元测试的错误写法
walterlv May 12, 2026
c68d7c4
补充传输层的更多文档提示
walterlv May 12, 2026
3ea0576
修复对象中的集合属性被错误映射成 object 的 OutputSchema 导致无法通过类型校验的问题
walterlv May 12, 2026
953d0fa
修复示例错误
walterlv May 12, 2026
c022417
修复 OpenAI API 兼容性
walterlv May 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ src/DotNetCampus.ModelContextProtocol/
- 代码主要以最新版本协议进行编写
- 遇到需要兼容旧协议的部分,用 `Legacy` 命名相关代码并尽量减少代码量
- **协议消息类型规范**:详见 [/docs/knowledge/protocol-messages-guide.md](../docs/knowledge/protocol-messages-guide.md)
- 所有 Protocol 消息类型必须添加中英双语注释
- **仅** `Protocol/` 文件夹下的消息类型必须添加中英双语注释;其他所有代码(接口、实现类、传输层等)一律使用**纯中文注释**(注:当前存在一些遗留非协议代码仍使用双语注释,如果改到了相关代码,请顺手改为纯中文注释)
- 英文注释必须使用 MCP 官方 Schema 原文
- 当前使用协议版本:**2025-11-25**
- Schema 文件:[schema.ts](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.ts)
Expand Down Expand Up @@ -75,8 +75,9 @@ src/DotNetCampus.ModelContextProtocol/

## 参考资源

- [MCP 官方规范 (2025-06-18)](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports) - **当前使用版本**
- [MCP Schema (2025-06-18)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-06-18/schema.ts) - **官方消息类型定义**
- [MCP 官方规范 (2025-11-25)](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports) - **当前使用版本**
- [MCP Schema (2025-11-25)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.ts) - **官方消息类型定义**
- [MCP 官方规范 (2025-06-18)](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports) - 旧版本(兼容支持)
- [MCP 官方规范 (2024-11-05)](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports) - 旧版本(兼容支持)
- [JSON-RPC 2.0 规范](https://www.jsonrpc.org/specification)
- [SSE 标准](https://html.spec.whatwg.org/multipage/server-sent-events.html)
Expand Down
96 changes: 33 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# DotNetCampus.ModelContextProtocol

[![.NET Build and Test](https://github.com/dotnet-campus/DotNetCampus.ModelContextProtocol/actions/workflows/dotnet-build.yml/badge.svg)](https://github.com/dotnet-campus/DotNetCampus.ModelContextProtocol/actions/workflows/dotnet-build.yml) [![NuGet](https://img.shields.io/nuget/v/DotNetCampus.ModelContextProtocol.svg?label=DotNetCampus.ModelContextProtocol)](https://www.nuget.org/packages/DotNetCampus.ModelContextProtocol)
[![.NET Build and Test](https://github.com/dotnet-campus/DotNetCampus.ModelContextProtocol/actions/workflows/dotnet-build.yml/badge.svg)](https://github.com/dotnet-campus/DotNetCampus.ModelContextProtocol/actions/workflows/dotnet-build.yml) [![NuGet](https://img.shields.io/nuget/v/DotNetCampus.ModelContextProtocol.svg?label=DotNetCampus.ModelContextProtocol)](https://www.nuget.org/packages/DotNetCampus.ModelContextProtocol) [![NuGet](https://img.shields.io/nuget/v/DotNetCampus.ModelContextProtocol.Ipc.svg?label=DotNetCampus.ModelContextProtocol.Ipc)](https://www.nuget.org/packages/DotNetCampus.ModelContextProtocol.Ipc) [![NuGet](https://img.shields.io/nuget/v/DotNetCampus.ModelContextProtocol.TouchSocket.Http.svg?label=DotNetCampus.ModelContextProtocol.TouchSocket.Http)](https://www.nuget.org/packages/DotNetCampus.ModelContextProtocol.TouchSocket.Http)

| [English][en] | [简体中文][zh-hans] |
| ------------- | ------------------- |
| [English][en] | [简体中文][zh-hans] | [繁體中文][zh-hant] |
| ------------- | ------------------- | ------------------- |

[en]: /docs/en/QuickStart.md
[zh-hans]: /docs/zh-hans/QuickStart.md
[en]: /docs/en/README.md
[zh-hans]: /docs/zh-hans/README.md
[zh-hant]: /docs/zh-hant/README.md

A lightweight, zero-dependency yet full-featured MCP protocol implementation built with .NET. It can be easily integrated into your application, regardless of its architecture.

Expand All @@ -25,87 +26,56 @@ A lightweight, zero-dependency yet full-featured MCP protocol implementation bui
dotnet add package DotNetCampus.ModelContextProtocol
```

### Quick Start
## Quick Start

A typical MCP server program looks like this:
### Server

```csharp
internal class Program
{
private static async Task Main(string[] args)
{
// The server name and version will be sent to clients via the MCP protocol
var mcpServer = new McpServerBuilder("Sample Server", "1.0.0")
// If your MCP tool parameters and return values use custom types, you need to provide a JSON serialization context
.WithJsonSerializer(McpToolJsonContext.Default)
.WithTools(t => t
// Register various MCP tools
.WithTool(() => new SampleTools())
.WithTool(() => new SampleTools2())
)
// Use Streamable HTTP transport, listening on http://localhost:5943/mcp
// Also compatible with SSE, listening on http://localhost:5943/mcp/sse
.WithLocalHostHttp(5943, "mcp")
// You can also use stdio (standard input/output) transport, which is recommended by the MCP protocol for all MCP servers
// However, it's generally not recommended to enable both http and stdio simultaneously,
// as the former typically requires singleton execution while the latter must support multiple instances
// .WithStdio()
.Build();
#if DEBUG
// Enable debug mode so that when the MCP server encounters exceptions, it returns exception information to clients for easier debugging
// It's generally not recommended to enable this mode in production, as it would expose internal implementation details of the server
mcpServer.EnableDebugMode();
#endif
// Run the MCP server
await mcpServer.RunAsync();
}
}
var mcpServer = new McpServerBuilder("Sample Mcp Server", "1.0.0")
.WithTools(tools => tools.WithTool(() => new SampleTools()))
.WithLocalHostHttp(5943, "mcp")
.Build();

[JsonSerializable(typeof(Foo))]
[JsonSerializable(typeof(Bar))]
[JsonSourceGenerationOptions(
// Recommended: Most MCP protocol implementations use camelCase naming
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
// Recommended: Most MCP protocol implementations use string enums
UseStringEnumConverter = true,
// Recommended: Cannot guarantee AI will always put metadata properties first
AllowOutOfOrderMetadataProperties = true
// If you plan to use less capable models, you can also enable the following options
// PropertyNameCaseInsensitive = true,
// NumberHandling = JsonNumberHandling.AllowReadingFromString
)]
internal partial class McpToolJsonContext : JsonSerializerContext;
```

### Declaring MCP Tool Methods
await mcpServer.RunAsync();

```csharp
public class SampleTools
{
/// <summary>
/// A tool for AI debugging that echoes back information as-is
/// 原样返回输入文本。
/// </summary>
/// <param name="text">The string to echo back</param>
/// <returns>The echoed string</returns>
/// <param name="text">要原样返回的字符串</param>
[McpServerTool(ReadOnly = true)]
public string Echo(string text)
public string EchoTool(string text)
{
return text;
}
}
```

### Advanced Usage
### Client

```csharp
var client = new McpClientBuilder("Sample Mcp Client", "1.0.0")
.WithHttp("http://localhost:5943/mcp")
.Build();

var arguments = JsonSerializer.SerializeToElement(new { text = "Hello, MCP!" });
var result = await client.CallToolAsync("echo_tool", arguments);

Console.WriteLine(result.Content);
```

## Documentation

For advanced usage including supported types for parameters and return values, type polymorphism, and more, please refer to the [Quick Start Guide](docs/quickstart/README.md)
See [docs/en/README.md](docs/en/README.md) for the full documentation index.

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
Contributions are welcome. Please feel free to submit a pull request.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.

## About dotnet-campus

Expand Down
2 changes: 1 addition & 1 deletion build/Version.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<Version>0.1.0</Version>
<Version>0.0.0</Version>
</PropertyGroup>
</Project>
59 changes: 0 additions & 59 deletions docs/McpClient设计方案.md

This file was deleted.

5 changes: 5 additions & 0 deletions docs/en/Authorization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Authorization

MCP Authorization enables the client and server to complete authentication and authorization before or during a connection.

> The current version does not yet provide built-in Authorization support (planned). In the meantime, you can implement authentication logic yourself using custom HTTP headers (via `HttpClientTransportOptions`'s `HttpClient`) or `WithRequestHandlers` interceptors.
128 changes: 128 additions & 0 deletions docs/en/DependencyInjection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Dependency Injection

The MCP library's dependency injection is entirely implemented by **compile-time source generators**, with **zero runtime reflection**. This document explains how it works, how to use it, and how it differs from conventional DI containers.

## Core Principle

When you write:

```csharp
var mcpServer = new McpServerBuilder("Example Server", "1.0.0")
.WithServices(appServiceProvider)
.WithTools(tools => tools
.WithTool<MyTool>()
.WithTool<MyOtherTool>())
.Build();
```

What actually happens at compile time:

1. **`WithServices(IServiceProvider)`** merely stores your `IServiceProvider` reference — **no service registration or container scanning** occurs.
2. **`WithTool<MyTool>()`** has a method body that is literally `throw new InvalidOperationException()` (never executed). The C# 12 **Interceptors** feature intercepts this call at compile time.
3. **The compile-time generated interceptor code** uses Roslyn to analyze `MyTool`'s constructor signature and generates explicit `serviceProvider.GetService(typeof(TParam))` calls for each constructor parameter.

> **Key takeaway: Tool classes do NOT need to be registered in the DI container.** The `IServiceProvider` is only used to resolve the **parameter types** of the tool's constructor (such as `ILogger`, `HttpClient`, etc.).

## Two Injection Approaches

### Approach 1: Constructor Injection (`WithTool<T>()`, Recommended)

Best for tool classes with multiple shared dependencies. The source generator (`WithToolInterceptorGenerator`) finds the constructor at compile time and generates `GetService` calls for each parameter:

```csharp
// User code
public class MyTool
{
private readonly ILogger _logger;
private readonly IDataService _dataService;

public MyTool(ILogger logger, IDataService dataService)
{
_logger = logger;
_dataService = dataService;
}

/// <summary>
/// Process input and return result.
/// </summary>
[McpServerTool]
public string DoSomething(string input)
{
_logger.Info($"processing: {input}");
return _dataService.Process(input);
}
}

// Registration — no factory needed
builder.WithServices(appServiceProvider);
builder.WithTool<MyTool>(); // Interceptor auto-generates DI code
```

Equivalent code generated at compile time (simplified):

```csharp
// Generated by interceptor at compile time, zero runtime reflection
var factory = () => new MyTool(
(ILogger?)serviceProvider.GetService(typeof(ILogger))
?? throw new InvalidOperationException("Unable to resolve ILogger."),
(IDataService?)serviceProvider.GetService(typeof(IDataService))
?? throw new InvalidOperationException("Unable to resolve IDataService."));
```

### Approach 2: Parameter Injection (`[ToolParameter(Type = ToolParameterType.Injected)]`)

Best when only a few parameters need DI. The source generator (`McpServerToolSourceBuilder`) generates independent `GetService` calls for each injected parameter:

```csharp
public class SampleTools
{
[McpServerTool]
public string FormatMessage(
string text,
[ToolParameter(Type = ToolParameterType.Injected)] ILogger logger)
{
logger.Info($"formatting: {text}");
return text.ToUpper();
}
}
```

Equivalent code generated at compile time:

```csharp
// Nullable types → TryGetService, returns null on resolution failure
var logger = context.TryGetService<ILogger>();

// Non-nullable types → EnsureGetService, throws on resolution failure
var requiredService = context.EnsureGetService<IRequiredService>("IRequiredService");
```

## What You Need to Configure

| Action | Required? | Notes |
|--------|-----------|-------|
| Register tool types in DI container | **No** | Source generator has already analyzed constructors; `new` is used directly at runtime |
| Register constructor parameter types in DI container | **Yes** | Types like `ILogger`, `IDataService` must be resolvable from `IServiceProvider` |
| Call `WithServices()` | **Yes** | Passes your `IServiceProvider` to the MCP server |
| Call `WithTool<T>()` (without factory) | **Yes** | Triggers the source generator to emit DI code for this type |
| Call `WithTool(() => new MyTool(dep1))` | Optional | Manual instantiation; no `IServiceProvider` needed |

## Comparison with Conventional DI Containers

| | Conventional DI (e.g. `Microsoft.Extensions.DI`) | MCP Library |
|---|---|---|
| Service discovery | Runtime assembly scanning | Compile-time Roslyn source analysis |
| Instance creation | Runtime `Activator.CreateInstance` | Compile-time `new T(...)` expressions |
| Tool registration | `services.AddTransient<MyTool>()` | **Not needed** |
| Parameter injection | Container recursive type-tree resolution | Compile-time generated `serviceProvider.GetService(typeof(T))` |
| Resolution failure | Runtime exception | Nullable params return `null`; non-nullable throw |

## Safety

If the `WithTool<T>()` interceptor is missing (e.g., forgot to reference the Analyzer NuGet package), the actual method body is `throw new InvalidOperationException` — the application fails immediately at startup with a clear error, rather than silently using the wrong resolution mechanism.

## Why Not Reflection

1. **AOT-compatible**: No `Activator.CreateInstance` or assembly scanning; fully compatible with NativeAOT compilation.
2. **Compile-time error detection**: Unresolvable constructor parameters produce `#error` at compile time, no need to wait until runtime.
3. **Zero overhead**: Generated code performs identically to hand-written `new MyTool(dep1, dep2)`.
5 changes: 5 additions & 0 deletions docs/en/Elicitation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Elicitation

MCP Elicitation allows the server to request additional information from the client, which the client then collects from the user.

> The current version has not yet implemented Elicitation (planned).
Loading
Loading