Model Context Protocol (MCP) 完全指南

引言

想象这样一个场景:你的 AI 助手需要访问公司内部数据库、文件系统、第三方 API、代码仓库…传统做法需要为每个数据源编写独立的集成代码,维护成本极高。

Model Context Protocol (MCP) 正是为了解决这个问题而生——一个标准化的协议,让 AI 应用能够以统一的方式访问任何外部数据源和工具。

本文将深入讲解 MCP 的架构、实现方式和最佳实践,助你构建真正"能连接万物"的 AI 应用。

什么是 MCP?

核心定义

Model Context Protocol 是一个开放的、基于 JSON-RPC 2.0 的标准化协议,旨在实现 AI 应用与外部数据源/工具之间的无缝集成。

graph LR
    A[AI 应用] -->|MCP 协议| B[MCP Server]
    B --> C[(数据库)]
    B --> D[文件系统]
    B --> E[REST API]
    B --> F[Git 仓库]
    
    style A fill:#4CAF50
    style B fill:#2196F3

传统集成 vs MCP

传统方式的痛点

flowchart LR
    AI[AI 应用] --> |自定义代码 1| DB[(数据库)]
    AI --> |自定义代码 2| API[REST API]
    AI --> |自定义代码 3| Files[文件系统]
    AI --> |自定义代码 4| Git[Git 仓库]
    
    style AI fill:#FF5252

每个数据源都需要专门的集成代码
缺乏标准,维护成本高
重复造轮子,代码难以复用
切换数据源需要重写代码

MCP 方式的优势

flowchart LR
    AI[AI 应用] -->|标准 MCP 协议| S1[MCP Server<br/>数据库]
    AI -->|标准 MCP 协议| S2[MCP Server<br/>文件系统]
    AI -->|标准 MCP 协议| S3[MCP Server<br/>Git]
    
    style AI fill:#4CAF50

统一接口:所有数据源使用相同的协议
开箱即用:社区提供大量现成的 MCP Server
即插即拔:添加/移除数据源只需修改配置
跨语言支持:Server 可用任何语言实现


MCP 架构详解

三大核心组件

graph TB
    subgraph "AI 应用端"
        A[MCP Client]
    end
    
    subgraph "传输层"
        T[Transport<br/>StdIO / HTTP / WebSocket]
    end
    
    subgraph "数据/工具端"
        S[MCP Server]
        S1[Resources<br/>资源提供]
        S2[Tools<br/>工具执行]
        S3[Prompts<br/>提示模板]
    end
    
    A <-->|JSON-RPC 2.0| T
    T <-->|JSON-RPC 2.0| S
    S --> S1
    S --> S2
    S --> S3
    
    style A fill:#4CAF50
    style S fill:#2196F3
    style T fill:#FF9800

1. MCP Client(客户端)

职责:AI 应用侧的代理,负责发起请求和处理响应。

using Microsoft.Extensions.AI.Agents.Mcp;

// 创建 MCP 客户端
var mcpClient = new McpClient(new StdioTransport(
    command: "node",
    args: new[] { "path/to/mcp-server.js" }
));

// 连接到 Server
await mcpClient.ConnectAsync();

// 查询 Server 的能力
var capabilities = await mcpClient.GetServerCapabilitiesAsync();
Console.WriteLine($"Server 支持的功能: {string.Join(", ", capabilities)}");

2. Transport(传输层)

职责:定义 Client 和 Server 之间的通信方式。

传输方式

说明

适用场景

StdIO

标准输入/输出

本地进程通信(最常用)

HTTP/SSE

HTTP + Server-Sent Events

远程服务、Web 应用

WebSocket

双向通信

实时交互场景

// StdIO Transport(本地进程)
var transport = new StdioTransport("node", new[] { "server.js" });

// HTTP Transport(远程服务)
var transport = new HttpTransport("https://api.example.com/mcp");

// 自定义 Transport
public class CustomTransport : IMcpTransport
{
    public async Task SendAsync(JsonRpcMessage message) { ... }
    public async Task<JsonRpcMessage> ReceiveAsync() { ... }
}

3. MCP Server(服务端)

职责:暴露数据和工具供 AI 应用使用。

核心能力模型

能力

说明

示例

Resources

提供只读数据

文件内容、数据库记录、API 响应

Tools

执行操作

文件写入、数据库更新、发送邮件

Prompts

提供提示模板

预定义的对话模板

Sampling

请求 LLM 生成

Server 请求 Client 调用 LLM

Logging

日志记录

调试和审计


MCP 握手流程(Initialize)

Client 和 Server 建立连接时的协商过程:

sequenceDiagram
    participant C as MCP Client
    participant S as MCP Server
    
    C->>S: Initialize Request<br/>{clientInfo, capabilities}
    Note over S: 验证版本兼容性<br/>记录 Client 能力
    S-->>C: Initialize Response<br/>{serverInfo, capabilities}
    Note over C: 记录 Server 能力<br/>确认可用功能
    C->>S: Initialized Notification
    Note over S: 连接已就绪
    
    Note over C,S: 可以开始正常通信

示例代码

// Client 发起握手
var initRequest = new InitializeRequest
{
    ProtocolVersion = "2024-11-05",
    ClientInfo = new ClientInfo
    {
        Name = "MyAIApp",
        Version = "1.0.0"
    },
    Capabilities = new ClientCapabilities
    {
        Roots = new RootsCapability { ListChanged = true },
        Sampling = new SamplingCapability()  // 支持 Sampling
    }
};

var initResponse = await mcpClient.SendRequestAsync<InitializeResponse>(
    "initialize", 
    initRequest
);

// 检查 Server 能力
if (initResponse.Capabilities.Tools != null)
{
    Console.WriteLine("Server 支持 Tools 能力");
}

if (initResponse.Capabilities.Resources != null)
{
    Console.WriteLine("Server 支持 Resources 能力");
}

// 发送 initialized 通知
await mcpClient.SendNotificationAsync("notifications/initialized");


核心能力深入解析

能力 1:Resources(资源)

用途:提供只读数据,如文件内容、数据库查询结果等。

Server 端实现

using Microsoft.Extensions.AI.Agents.Mcp;

public class FileSystemMcpServer : McpServer
{
    protected override Task<ListResourcesResponse> HandleListResourcesAsync(
        ListResourcesRequest request)
    {
        var resources = new[]
        {
            new Resource
            {
                Uri = "file:///docs/readme.md",
                Name = "README 文档",
                Description = "项目说明文档",
                MimeType = "text/markdown"
            },
            new Resource
            {
                Uri = "file:///data/users.json",
                Name = "用户数据",
                MimeType = "application/json"
            }
        };
        
        return Task.FromResult(new ListResourcesResponse { Resources = resources });
    }
    
    protected override async Task<ReadResourceResponse> HandleReadResourceAsync(
        ReadResourceRequest request)
    {
        // 根据 URI 读取资源内容
        var uri = new Uri(request.Uri);
        var filePath = uri.LocalPath;
        
        if (!File.Exists(filePath))
        {
            throw new McpException($"文件不存在: {filePath}");
        }
        
        var content = await File.ReadAllTextAsync(filePath);
        
        return new ReadResourceResponse
        {
            Contents = new[]
            {
                new ResourceContent
                {
                    Uri = request.Uri,
                    MimeType = "text/plain",
                    Text = content
                }
            }
        };
    }
}

Client 端使用

// 列出所有资源
var resourcesResponse = await mcpClient.ListResourcesAsync();

foreach (var resource in resourcesResponse.Resources)
{
    Console.WriteLine($"资源: {resource.Name} ({resource.Uri})");
}

// 读取特定资源
var readResponse = await mcpClient.ReadResourceAsync("file:///docs/readme.md");
var content = readResponse.Contents[0].Text;

Console.WriteLine($"文件内容: {content}");


能力 2:Tools(工具)

用途:执行操作,如文件写入、数据库更新、API 调用等。

Server 端实现

public class DatabaseMcpServer : McpServer
{
    private readonly IDbConnection _db;
    
    protected override Task<ListToolsResponse> HandleListToolsAsync(
        ListToolsRequest request)
    {
        var tools = new[]
        {
            new Tool
            {
                Name = "query_users",
                Description = "查询用户列表",
                InputSchema = new
                {
                    type = "object",
                    properties = new
                    {
                        limit = new { type = "integer", description = "返回数量" },
                        offset = new { type = "integer", description = "偏移量" }
                    }
                }
            },
            new Tool
            {
                Name = "create_user",
                Description = "创建新用户",
                InputSchema = new
                {
                    type = "object",
                    properties = new
                    {
                        name = new { type = "string", description = "用户名" },
                        email = new { type = "string", description = "邮箱" }
                    },
                    required = new[] { "name", "email" }
                }
            }
        };
        
        return Task.FromResult(new ListToolsResponse { Tools = tools });
    }
    
    protected override async Task<CallToolResponse> HandleCallToolAsync(
        CallToolRequest request)
    {
        switch (request.Name)
        {
            case "query_users":
                var limit = request.Arguments["limit"].GetInt32();
                var offset = request.Arguments["offset"].GetInt32();
                
                var users = await _db.QueryAsync<User>(
                    "SELECT * FROM Users LIMIT @Limit OFFSET @Offset",
                    new { Limit = limit, Offset = offset }
                );
                
                return new CallToolResponse
                {
                    Content = new[]
                    {
                        new ToolContent
                        {
                            Type = "text",
                            Text = JsonSerializer.Serialize(users)
                        }
                    }
                };
                
            case "create_user":
                var name = request.Arguments["name"].GetString();
                var email = request.Arguments["email"].GetString();
                
                await _db.ExecuteAsync(
                    "INSERT INTO Users (Name, Email) VALUES (@Name, @Email)",
                    new { Name = name, Email = email }
                );
                
                return new CallToolResponse
                {
                    Content = new[]
                    {
                        new ToolContent
                        {
                            Type = "text",
                            Text = $"用户 {name} 创建成功"
                        }
                    }
                };
                
            default:
                throw new McpException($"未知工具: {request.Name}");
        }
    }
}

Client 端使用

// 列出所有工具
var toolsResponse = await mcpClient.ListToolsAsync();

foreach (var tool in toolsResponse.Tools)
{
    Console.WriteLine($"工具: {tool.Name} - {tool.Description}");
}

// 调用工具
var callResponse = await mcpClient.CallToolAsync(new CallToolRequest
{
    Name = "query_users",
    Arguments = new Dictionary<string, object>
    {
        ["limit"] = 10,
        ["offset"] = 0
    }
});

var result = callResponse.Content[0].Text;
Console.WriteLine($"查询结果: {result}");


能力 3:Prompts(提示模板)

用途:提供预定义的提示词模板。

Server 端实现

public class PromptLibraryMcpServer : McpServer
{
    protected override Task<ListPromptsResponse> HandleListPromptsAsync(
        ListPromptsRequest request)
    {
        var prompts = new[]
        {
            new Prompt
            {
                Name = "code_review",
                Description = "代码审查模板",
                Arguments = new[]
                {
                    new PromptArgument
                    {
                        Name = "code",
                        Description = "要审查的代码",
                        Required = true
                    },
                    new PromptArgument
                    {
                        Name = "language",
                        Description = "编程语言",
                        Required = false
                    }
                }
            }
        };
        
        return Task.FromResult(new ListPromptsResponse { Prompts = prompts });
    }
    
    protected override Task<GetPromptResponse> HandleGetPromptAsync(
        GetPromptRequest request)
    {
        if (request.Name == "code_review")
        {
            var code = request.Arguments["code"];
            var language = request.Arguments.GetValueOrDefault("language", "unknown");
            
            var promptText = $@"请审查以下 {language} 代码:

```{language}
{code}

请分析:

  1. 潜在的 bug

  2. 性能问题

  3. 最佳实践建议";

         return Task.FromResult(new GetPromptResponse
         {
             Description = "代码审查",
             Messages = new[]
             {
                 new PromptMessage
                 {
                     Role = "user",
                     Content = new TextContent { Text = promptText }
                 }
             }
         });
     }
     
     throw new McpException($"未知提示模板: {request.Name}");
    
    

    } }


#### Client 端使用

```csharp
// 获取提示模板
var promptResponse = await mcpClient.GetPromptAsync(new GetPromptRequest
{
    Name = "code_review",
    Arguments = new Dictionary<string, string>
    {
        ["code"] = "public void ProcessData() { ... }",
        ["language"] = "csharp"
    }
});

// 将模板应用到 AI 对话
var messages = promptResponse.Messages.Select(m => 
    new ChatMessage(
        m.Role == "user" ? ChatRole.User : ChatRole.System,
        m.Content.Text
    )
).ToList();

var response = await chatClient.GetResponseAsync(messages);


实战:构建一个完整的 MCP 系统

场景:企业知识库 MCP Server

让我们构建一个能够访问公司文档、数据库和 API 的 MCP Server。

using Microsoft.Extensions.AI.Agents.Mcp;
using Microsoft.Extensions.Logging;

// 1. 定义 MCP Server
public class EnterpriseKnowledgeServer : McpServer
{
    private readonly IDocumentRepository _docs;
    private readonly IDbConnection _db;
    private readonly IApiClient _api;
    private readonly ILogger<EnterpriseKnowledgeServer> _logger;
    
    public EnterpriseKnowledgeServer(
        IDocumentRepository docs,
        IDbConnection db,
        IApiClient api,
        ILogger<EnterpriseKnowledgeServer> logger)
        : base(new McpServerOptions
        {
            ServerInfo = new ServerInfo
            {
                Name = "EnterpriseKnowledge",
                Version = "1.0.0"
            },
            Capabilities = new ServerCapabilities
            {
                Resources = new ResourcesCapability { Subscribe = true },
                Tools = new ToolsCapability(),
                Prompts = new PromptsCapability(),
                Logging = new LoggingCapability()
            }
        })
    {
        _docs = docs;
        _db = db;
        _api = api;
        _logger = logger;
    }
    
    // 2. 实现 Resources 能力
    protected override async Task<ListResourcesResponse> HandleListResourcesAsync(
        ListResourcesRequest request)
    {
        _logger.LogInformation("列出所有资源");
        
        var documents = await _docs.GetAllAsync();
        
        var resources = documents.Select(doc => new Resource
        {
            Uri = $"doc://{doc.Id}",
            Name = doc.Title,
            Description = doc.Description,
            MimeType = "text/markdown"
        }).ToArray();
        
        return new ListResourcesResponse { Resources = resources };
    }
    
    protected override async Task<ReadResourceResponse> HandleReadResourceAsync(
        ReadResourceRequest request)
    {
        _logger.LogInformation("读取资源: {Uri}", request.Uri);
        
        // 解析 URI: doc://123
        var docId = request.Uri.Replace("doc://", "");
        var document = await _docs.GetByIdAsync(docId);
        
        if (document == null)
        {
            throw new McpException($"文档不存在: {docId}");
        }
        
        return new ReadResourceResponse
        {
            Contents = new[]
            {
                new ResourceContent
                {
                    Uri = request.Uri,
                    MimeType = "text/markdown",
                    Text = document.Content
                }
            }
        };
    }
    
    // 3. 实现 Tools 能力
    protected override Task<ListToolsResponse> HandleListToolsAsync(
        ListToolsRequest request)
    {
        var tools = new[]
        {
            new Tool
            {
                Name = "search_documents",
                Description = "搜索企业文档",
                InputSchema = new
                {
                    type = "object",
                    properties = new
                    {
                        query = new { type = "string", description = "搜索关键词" },
                        limit = new { type = "integer", description = "返回数量", default_value = 10 }
                    },
                    required = new[] { "query" }
                }
            },
            new Tool
            {
                Name = "query_database",
                Description = "查询企业数据库",
                InputSchema = new
                {
                    type = "object",
                    properties = new
                    {
                        table = new { type = "string", description = "表名" },
                        filters = new { type = "object", description = "过滤条件" }
                    },
                    required = new[] { "table" }
                }
            },
            new Tool
            {
                Name = "call_api",
                Description = "调用企业内部 API",
                InputSchema = new
                {
                    type = "object",
                    properties = new
                    {
                        endpoint = new { type = "string", description = "API 端点" },
                        method = new { type = "string", description = "HTTP 方法", enum_values = new[] { "GET", "POST" } },
                        body = new { type = "object", description = "请求体" }
                    },
                    required = new[] { "endpoint", "method" }
                }
            }
        };
        
        return Task.FromResult(new ListToolsResponse { Tools = tools });
    }
    
    protected override async Task<CallToolResponse> HandleCallToolAsync(
        CallToolRequest request)
    {
        _logger.LogInformation("调用工具: {ToolName}", request.Name);
        
        try
        {
            switch (request.Name)
            {
                case "search_documents":
                    var query = request.Arguments["query"].GetString();
                    var limit = request.Arguments.GetValueOrDefault("limit", 10).GetInt32();
                    
                    var results = await _docs.SearchAsync(query, limit);
                    
                    return new CallToolResponse
                    {
                        Content = new[]
                        {
                            new ToolContent
                            {
                                Type = "text",
                                Text = JsonSerializer.Serialize(results, new JsonSerializerOptions
                                {
                                    WriteIndented = true
                                })
                            }
                        }
                    };
                    
                case "query_database":
                    var table = request.Arguments["table"].GetString();
                    var filters = request.Arguments.GetValueOrDefault("filters", new {});
                    
                    var data = await _db.QueryAsync(BuildSqlQuery(table, filters));
                    
                    return new CallToolResponse
                    {
                        Content = new[]
                        {
                            new ToolContent
                            {
                                Type = "text",
                                Text = JsonSerializer.Serialize(data, new JsonSerializerOptions
                                {
                                    WriteIndented = true
                                })
                            }
                        }
                    };
                    
                case "call_api":
                    var endpoint = request.Arguments["endpoint"].GetString();
                    var method = request.Arguments["method"].GetString();
                    var body = request.Arguments.GetValueOrDefault("body", null);
                    
                    var apiResponse = await _api.CallAsync(endpoint, method, body);
                    
                    return new CallToolResponse
                    {
                        Content = new[]
                        {
                            new ToolContent
                            {
                                Type = "text",
                                Text = apiResponse
                            }
                        }
                    };
                    
                default:
                    throw new McpException($"未知工具: {request.Name}");
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "工具执行失败");
            
            return new CallToolResponse
            {
                IsError = true,
                Content = new[]
                {
                    new ToolContent
                    {
                        Type = "text",
                        Text = $"执行失败: {ex.Message}"
                    }
                }
            };
        }
    }
    
    // 4. 实现 Prompts 能力
    protected override Task<ListPromptsResponse> HandleListPromptsAsync(
        ListPromptsRequest request)
    {
        var prompts = new[]
        {
            new Prompt
            {
                Name = "analyze_document",
                Description = "分析企业文档",
                Arguments = new[]
                {
                    new PromptArgument { Name = "doc_id", Required = true }
                }
            },
            new Prompt
            {
                Name = "generate_report",
                Description = "生成业务报告",
                Arguments = new[]
                {
                    new PromptArgument { Name = "report_type", Required = true },
                    new PromptArgument { Name = "date_range", Required = false }
                }
            }
        };
        
        return Task.FromResult(new ListPromptsResponse { Prompts = prompts });
    }
    
    protected override async Task<GetPromptResponse> HandleGetPromptAsync(
        GetPromptRequest request)
    {
        if (request.Name == "analyze_document")
        {
            var docId = request.Arguments["doc_id"];
            var document = await _docs.GetByIdAsync(docId);
            
            var promptText = $@"请分析以下企业文档:

标题: {document.Title}
内容:
{document.Content}

请提供:
1. 文档摘要
2. 关键要点
3. 相关建议";

            return new GetPromptResponse
            {
                Messages = new[]
                {
                    new PromptMessage
                    {
                        Role = "user",
                        Content = new TextContent { Text = promptText }
                    }
                }
            };
        }
        
        throw new McpException($"未知提示模板: {request.Name}");
    }
    
    private string BuildSqlQuery(string table, object filters)
    {
        // 简化实现,实际应使用参数化查询
        return $"SELECT * FROM {table} WHERE ...";
    }
}

// 5. 启动 Server
public class Program
{
    public static async Task Main(string[] args)
    {
        var host = Host.CreateDefaultBuilder(args)
            .ConfigureServices((context, services) =>
            {
                // 注册依赖
                services.AddSingleton<IDocumentRepository, DocumentRepository>();
                services.AddSingleton<IDbConnection, SqlConnection>();
                services.AddSingleton<IApiClient, HttpApiClient>();
                
                // 注册 MCP Server
                services.AddSingleton<EnterpriseKnowledgeServer>();
            })
            .Build();
        
        var server = host.Services.GetRequiredService<EnterpriseKnowledgeServer>();
        
        // 使用 StdIO Transport
        var transport = new StdioTransport();
        await server.RunAsync(transport);
    }
}

Client 端使用

using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents.Mcp;

// 1. 连接到 MCP Server
var mcpClient = new McpClient(new StdioTransport(
    command: "dotnet",
    args: new[] { "run", "--project", "EnterpriseKnowledgeServer" }
));

await mcpClient.ConnectAsync();

// 2. 将 MCP Tools 转换为 AI Functions
var tools = await mcpClient.GetToolsAsAIFunctionsAsync();

// 3. 创建 AI 客户端
IChatClient chatClient = new OpenAIClient(...)
    .AsChatClient("gpt-4")
    .UseFunctionInvocation();  // 自动执行函数调用

// 4. 配置选项
var options = new ChatOptions
{
    Tools = tools,
    ToolMode = ChatToolMode.Auto
};

// 5. 执行对话
var messages = new List<ChatMessage>
{
    new(ChatRole.System, "你是企业知识助手,可以访问公司文档、数据库和 API"),
    new(ChatRole.User, "搜索关于'产品路线图'的文档")
};

var response = await chatClient.GetResponseAsync(messages, options);
Console.WriteLine(response.Text);

// AI 会自动调用 search_documents 工具,并整合结果返回


高级特性

1. Resources 订阅(Subscribe)

当资源更新时主动通知 Client:

// Server 端
protected override Task HandleResourceSubscribeAsync(
    ResourceSubscribeRequest request)
{
    _logger.LogInformation("订阅资源: {Uri}", request.Uri);
    
    // 监听资源变化
    _docs.OnChanged += async (sender, doc) =>
    {
        if ($"doc://{doc.Id}" == request.Uri)
        {
            await SendNotificationAsync("notifications/resources/updated", new
            {
                uri = request.Uri
            });
        }
    };
    
    return Task.CompletedTask;
}

// Client 端
mcpClient.OnResourceUpdated += async (uri) =>
{
    Console.WriteLine($"资源已更新: {uri}");
    
    // 重新读取资源
    var content = await mcpClient.ReadResourceAsync(uri);
    Console.WriteLine($"新内容: {content}");
};

await mcpClient.SubscribeToResourceAsync("doc://123");

2. Sampling(请求 LLM)

Server 可以请求 Client 调用 LLM:

// Server 端
protected override async Task<SamplingResult> HandleSamplingRequestAsync(
    SamplingRequest request)
{
    // Server 请求 Client 的 LLM 生成内容
    var response = await RequestSamplingAsync(new CreateMessageRequest
    {
        Messages = new[]
        {
            new SamplingMessage
            {
                Role = "user",
                Content = new TextContent { Text = "总结这段文本..." }
            }
        },
        MaxTokens = 500
    });
    
    return response;
}

// Client 端(自动处理)
mcpClient.OnSamplingRequest += async (request) =>
{
    // 使用 AI 客户端处理
    var messages = request.Messages.Select(m => 
        new ChatMessage(ChatRole.User, m.Content.Text)
    ).ToList();
    
    var response = await chatClient.GetResponseAsync(messages, new ChatOptions
    {
        MaxOutputTokens = request.MaxTokens
    });
    
    return new SamplingResult
    {
        Content = new TextContent { Text = response.Text }
    };
};

3. 自定义 Transport

实现 HTTP Transport 支持远程 MCP Server:

using System.Net.Http;
using System.Net.Http.Json;

public class HttpMcpTransport : IMcpTransport
{
    private readonly HttpClient _httpClient;
    private readonly string _endpoint;
    
    public HttpMcpTransport(string endpoint)
    {
        _httpClient = new HttpClient();
        _endpoint = endpoint;
    }
    
    public async Task SendAsync(JsonRpcMessage message)
    {
        var response = await _httpClient.PostAsJsonAsync(
            _endpoint, 
            message
        );
        
        response.EnsureSuccessStatusCode();
    }
    
    public async Task<JsonRpcMessage> ReceiveAsync()
    {
        // 使用 Server-Sent Events (SSE) 接收消息
        using var request = new HttpRequestMessage(HttpMethod.Get, $"{_endpoint}/events");
        request.Headers.Accept.Add(new("text/event-stream"));
        
        using var response = await _httpClient.SendAsync(
            request, 
            HttpCompletionOption.ResponseHeadersRead
        );
        
        var stream = await response.Content.ReadAsStreamAsync();
        // 解析 SSE 消息...
        
        return await JsonSerializer.DeserializeAsync<JsonRpcMessage>(stream);
    }
}

// 使用
var transport = new HttpMcpTransport("https://mcp.example.com");
var mcpClient = new McpClient(transport);


最佳实践

1. 错误处理

// Server 端
protected override async Task<CallToolResponse> HandleCallToolAsync(
    CallToolRequest request)
{
    try
    {
        // 执行工具逻辑
        var result = await ExecuteToolAsync(request);
        
        return new CallToolResponse
        {
            Content = new[] { new ToolContent { Text = result } }
        };
    }
    catch (ValidationException ex)
    {
        // 参数验证错误
        _logger.LogWarning(ex, "参数验证失败");
        
        return new CallToolResponse
        {
            IsError = true,
            Content = new[]
            {
                new ToolContent
                {
                    Type = "text",
                    Text = $"参数错误: {ex.Message}"
                }
            }
        };
    }
    catch (Exception ex)
    {
        // 系统错误
        _logger.LogError(ex, "工具执行异常");
        
        return new CallToolResponse
        {
            IsError = true,
            Content = new[]
            {
                new ToolContent
                {
                    Type = "text",
                    Text = "系统错误,请稍后重试"
                }
            }
        };
    }
}

// Client 端
var response = await mcpClient.CallToolAsync(request);

if (response.IsError)
{
    Console.WriteLine($"工具调用失败: {response.Content[0].Text}");
}

2. 性能优化:缓存

public class CachedMcpServer : McpServer
{
    private readonly IMemoryCache _cache;
    
    protected override async Task<ReadResourceResponse> HandleReadResourceAsync(
        ReadResourceRequest request)
    {
        var cacheKey = $"resource:{request.Uri}";
        
        // 检查缓存
        if (_cache.TryGetValue(cacheKey, out ReadResourceResponse? cached))
        {
            _logger.LogInformation("缓存命中: {Uri}", request.Uri);
            return cached!;
        }
        
        // 读取资源
        var response = await base.HandleReadResourceAsync(request);
        
        // 缓存 5 分钟
        _cache.Set(cacheKey, response, TimeSpan.FromMinutes(5));
        
        return response;
    }
}

3. 安全性:权限控制

public class SecureMcpServer : McpServer
{
    private readonly IAuthorizationService _authService;
    
    protected override async Task<CallToolResponse> HandleCallToolAsync(
        CallToolRequest request)
    {
        // 从请求中提取用户信息(可通过自定义 Header 传递)
        var userId = GetUserIdFromContext();
        
        // 检查权限
        if (!await _authService.HasPermissionAsync(userId, $"tool:{request.Name}"))
        {
            return new CallToolResponse
            {
                IsError = true,
                Content = new[]
                {
                    new ToolContent
                    {
                        Type = "text",
                        Text = "权限不足"
                    }
                }
            };
        }
        
        // 执行工具
        return await base.HandleCallToolAsync(request);
    }
}

4. 日志和审计

public class AuditedMcpServer : McpServer
{
    private readonly IAuditLogger _auditLogger;
    
    protected override async Task<CallToolResponse> HandleCallToolAsync(
        CallToolRequest request)
    {
        var userId = GetUserIdFromContext();
        var startTime = DateTime.UtcNow;
        
        try
        {
            var response = await base.HandleCallToolAsync(request);
            
            // 记录审计日志
            await _auditLogger.LogAsync(new AuditEntry
            {
                UserId = userId,
                Action = "ToolCall",
                ToolName = request.Name,
                Arguments = request.Arguments,
                Success = !response.IsError,
                Duration = DateTime.UtcNow - startTime
            });
            
            return response;
        }
        catch (Exception ex)
        {
            await _auditLogger.LogAsync(new AuditEntry
            {
                UserId = userId,
                Action = "ToolCall",
                ToolName = request.Name,
                Success = false,
                Error = ex.Message
            });
            
            throw;
        }
    }
}


总结

MCP 协议的核心价值:

统一接口:标准化的 JSON-RPC 2.0 协议
灵活扩展:Resources、Tools、Prompts 等多种能力模型
即插即用:社区生态丰富,开箱即用
跨语言支持:Server 可用任何语言实现
生产级特性:权限控制、错误处理、日志审计

记住:MCP = 统一的协议 + 标准的能力模型 + 灵活的传输层 + 丰富的生态

资源链接