2026/4/19 19:35:38
网站建设
项目流程
网站排名优化怎么样,网站策划需要什么,西宁个人网站建设,wordpress 仿京东筛选近小智AI对话机器人在ESP32社区实在是太火了,看过之前文章的小伙伴应该都知道之前有给桌面机器人开发过一个.NET客户端,所以对小智也算是比较熟悉。小智虽然支持MCP(Model Context Protocol)协议来扩展功能,但是小智的MCP端点是一个特殊的WebSocket服务,如果想要为小智开发MCP功…近小智AI对话机器人在ESP32社区实在是太火了,看过之前文章的小伙伴应该都知道之前有给桌面机器人开发过一个.NET客户端,所以对小智也算是比较熟悉。小智虽然支持MCP(Model Context Protocol)协议来扩展功能,但是小智的MCP端点是一个特殊的WebSocket服务,如果想要为小智开发MCP功能,就需要针对这个特殊的端点进行开发。于是就想着能不能做一个转接平台,让开发者可以专注于标准MCP服务的开发,而不用关心小智特殊的WebSocket协议细节。这个平台可以将标准的MCP服务聚合后通过WebSocket提供给小智,同时支持多租户,每个用户都可以配置自己专属的MCP服务。项目已经上线并开源了,大家可以直接访问 https://xiaozhi.verdure-hiro.cn 体验,也可以在GitHub上找到完整源码:verdure-mcp-for-xiaozhiimg 视频演示想快速了解项目功能和使用方法观看我的B站视频教程B站视频内容 内容简介 适合人群小智 MCP 转接服务上线与开源 平台介绍、功能演示、在线使用教程 小智商家和小智爱好者私有化部署与米家智能家居控制 Docker部署教程、米家MCP服务接入实战 需要私有部署和智能家居控制的用户为什么要做这个项目技术背景小智AI的MCP端点采用的是WebSocket协议,这是一个特殊的实现方式,与标准的MCP协议(基于HTTP/SSE)有所不同。如果想要为小智开发MCP功能,开发者需要:了解WebSocket协议细节:需要处理连接管理、心跳检测、重连机制等实现MCP协议转换:将标准MCP的HTTP/SSE请求转换为WebSocket消息处理工具聚合:如果要使用多个MCP服务,需要自己实现工具列表的聚合和路由解决方案设计基于这些技术需求,设计了一个MCP服务转接平台来简化开发:协议转换:自动将标准MCP服务(HTTP/SSE)转换为小智的WebSocket协议多租户架构:每个用户都有独立的配置空间,互不干扰可视化管理:通过Web界面管理MCP服务配置,无需手动编辑配置文件服务聚合:平台作为中间层,将多个MCP服务的工具聚合后提供给小智分布式支持:支持多实例部署,通过Redis实现分布式协调从这个项目能学到什么核心技术栈.NET 9 - 新的.NET框架,性能和开发体验都很棒准备过段时间升级.NET 10Blazor WebAssembly - 纯C#开发前端,无需学习JavaScript领域驱动设计(DDD) - 规范的分层架构和领域模型仓储模式 - 数据访问层的最佳实践Keycloak认证 - OpenID Connect标准的身份认证WebSocket编程 - 实时双向通信的实现分布式协调 - 基于Redis的分布式锁和状态管理架构亮点这个项目展示了企业级.NET应用的完整架构:verdure-mcp-for-xiaozhi/├── Domain/ # 领域层:聚合根、实体、仓储接口├── Application/ # 应用服务层:业务逻辑编排├── Infrastructure/ # 基础设施层:数据访问、外部服务├── Api/ # API层:RESTful接口、WebSocket服务└── Web/ # Blazor前端:组件化UI开发平台界面展示核心设计理念领域模型设计项目采用DDD设计,核心有两个聚合根:1. XiaozhiMcpEndpoint(小智连接端点)代表用户配置的小智WebSocket连接地址,是整个系统的核心实体。public class XiaozhiMcpEndpoint : Entity, IAggregateRoot{public string Name { get; private set; }public string Address { get; private set; } // WebSocket地址public string UserId { get; private set; }public string? Description { get; private set; }public bool IsEnabled { get; private set; } // 是否启用连接public bool IsConnected { get; private set; } // 实时连接状态// 时间戳追踪public DateTime CreatedAt { get; private set; }public DateTime? UpdatedAt { get; private set; }public DateTime? LastConnectedAt { get; private set; }public DateTime? LastDisconnectedAt { get; private set; }// 服务绑定集合 - 私有字段,只读暴露private readonly ListMcpServiceBinding _serviceBindings new();public IReadOnlyCollectionMcpServiceBinding ServiceBindings _serviceBindings.AsReadOnly();public XiaozhiMcpEndpoint(string name, string address, string userId, string? description null){GenerateId(); // 使用Guid Version 7生成IDName name;Address address;UserId userId;Description description;IsEnabled false; // 默认禁用,需用户主动启用IsConnected false;CreatedAt DateTime.UtcNow;}// 核心业务方法:启用端点public void Enable(){IsEnabled true;UpdatedAt DateTime.UtcNow;}// 核心业务方法:禁用端点并断开连接public void Disable(){IsEnabled false;IsConnected false;UpdatedAt DateTime.UtcNow;}// ...其他方法: SetConnected(), SetDisconnected(), UpdateInfo()等已省略}设计要点:使用私有setter保护数据完整性通过方法(Enable/Disable)而非直接修改属性来改变状态IsEnabled和IsConnected分离:IsEnabled是用户意图,IsConnected是实际状态ServiceBindings集合封装:私有List配合只读接口暴露,防止外部直接修改2. McpServiceConfig(MCP服务配置)代表一个可用的MCP服务节点及其认证配置。public class McpServiceConfig : Entity, IAggregateRoot{public string Name { get; private set; }public string Endpoint { get; private set; }public string UserId { get; private set; }public string? Description { get; private set; }public bool IsPublic { get; private set; }// 认证配置支持4种类型: bearer, basic, apikey, oauth2public string? AuthenticationType { get; private set; }public string? AuthenticationConfig { get; private set; } // JSON格式配置public string? Protocol { get; private set; } // stdio/http/sse// 时间戳public DateTime CreatedAt { get; private set; }public DateTime? UpdatedAt { get; private set; }public DateTime? LastSyncedAt { get; private set; }// 工具集合 - 私有字段,只读暴露private readonly ListMcpTool _tools new();public IReadOnlyCollectionMcpTool Tools _tools.AsReadOnly();public McpServiceConfig(string name,string endpoint,string userId,string? description null,string? authenticationType null,string? authenticationConfig null,string? protocol null){GenerateId();Name name;Endpoint endpoint;UserId userId;Description description;IsPublic false; // 默认私有AuthenticationType authenticationType;AuthenticationConfig authenticationConfig;Protocol protocol ?? stdio; // 默认stdio协议CreatedAt DateTime.UtcNow;}// 更新服务配置信息public void UpdateInfo(string name,string endpoint,string? description null,string? authenticationType null,string? authenticationConfig null,string? protocol null){Name name;Endpoint endpoint;Description description;AuthenticationType authenticationType;AuthenticationConfig authenticationConfig;Protocol protocol;UpdatedAt DateTime.UtcNow;}public void SetPublic(){IsPublic true;UpdatedAt DateTime.UtcNow;}public void SetPrivate(){IsPublic false;UpdatedAt DateTime.UtcNow;}// ...其他方法: UpdateAuthenticationConfig()等已省略}设计要点:支持4种认证方式(Bearer/Basic/API Key/OAuth2),认证配置以JSON存储保持灵活性通过SetPublic/SetPrivate方法控制服务可见性,支持公共服务市场Tools集合封装:私有List配合只读接口暴露,防止外部直接修改使用Guid Version 7作为ID,提供更好的数据库索引性能和时序特性WebSocket会话管理这是整个平台最核心的部分,需要处理几个关键问题:问题1:如何聚合多个MCP服务的工具?解决方案:McpSessionService维护多个McpClient实例public class McpSessionService : IAsyncDisposable{private readonly ILoggerMcpSessionService _logger;private readonly IMcpClientService _mcpClientService;private readonly McpSessionConfiguration _config;private readonly ReconnectionSettings _reconnectionSettings;// Session stateprivate ClientWebSocket? _webSocket;private readonly ListMcpClient _mcpClients new();// 追踪每个客户端的服务配置,用于会话恢复private readonly Dictionaryint, McpServiceEndpoint _clientIndexToServiceConfig new();// 追踪失败的服务,用于定期重试private readonly Dictionarystring, (McpServiceEndpoint Config, DateTime LastAttempt) _failedServices new();// Ping timeout monitoringprivate DateTime _lastPingReceivedTime DateTime.UtcNow;private readonly TimeSpan _pingTimeout TimeSpan.FromSeconds(120);// Connection status eventspublic event FuncTask? OnConnected;public event Funcstring, Task? OnConnectionFailed;public event FuncTask? OnDisconnected;private async Task ConnectAsync(CancellationToken cancellationToken){// ⚠️ 关键:先连接MCP服务,再连接WebSocket// 这确保所有后端服务就绪后才告知小智我们在线_logger.LogInformation(Server {ServerId}: Connecting to {Count} MCP service(s)...,ServerId, _config.McpServices.Count);var failedServiceNames new Liststring();// 1. 先连接所有MCP服务(支持多种认证方式)foreach (var service in _config.McpServices){try{// 创建MCP客户端,传递认证配置var mcpClient await _mcpClientService.CreateMcpClientAsync($McpService_{service.ServiceName},service.NodeAddress,service.Protocol ?? stdio,service.AuthenticationType, // bearer/basic/apikey/oauth2service.AuthenticationConfig, // JSON格式认证配置cancellationToken);var clientIndex _mcpClients.Count;_mcpClients.Add(mcpClient);_clientIndexToServiceConfig[clientIndex] service; // 追踪配置用于会话恢复_logger.LogInformation(Server {ServerId}: Connected to MCP service {ServiceName},ServerId, service.ServiceName);}catch (Exception ex){// 记录失败的服务,供后续重试failedServiceNames.Add(service.ServiceName);_failedServices[service.ServiceName] (service, DateTime.UtcNow);_logger.LogWarning(Server {ServerId}: Skipping MCP service {ServiceName} - {Error},ServerId, service.ServiceName, ex.Message);}}// 检查是否至少有一个MCP客户端连接成功if (_mcpClients.Count 0){throw new InvalidOperationException($No MCP clients connected successfully (0/{_config.McpServices.Count}));}_logger.LogInformation(Server {ServerId}: {SuccessCount}/{TotalCount} MCP services connected,ServerId, _mcpClients.Count, _config.McpServices.Count);// 2. 所有MCP服务就绪后,连接小智WebSocket_webSocket new ClientWebSocket();await _webSocket.ConnectAsync(new Uri(_config.WebSocketEndpoint), cancellationToken);_lastPingReceivedTime DateTime.UtcNow; // 初始化ping监控await (OnConnected?.Invoke() ?? Task.CompletedTask); // 触发连接成功回调// 3. 启动双向通信 ping超时监控// ...消息管道和监控逻辑已省略}}设计要点:List而非Dictionary:_mcpClients使用List存储,通过索引映射到配置失败服务跟踪:_failedServices记录失败的服务供后续重试Ping超时监控:120秒未收到ping则认为连接断开连接顺序关键:先MCP服务,再WebSocket,确保后端就绪事件驱动:通过OnConnected/OnConnectionFailed/OnDisconnected通知上层问题2:小智请求工具列表怎么响应?解决方案:从配置数据直接获取工具信息,不依赖MCP客户端连接状态private async Task HandleToolsListAsync(int? id, CancellationToken cancellationToken){// ⚡ 性能优化: 直接从配置读取工具列表,不依赖MCP客户端连接状态// 即使部分MCP服务连接失败,也能返回已配置的完整工具列表if (_config.McpServices.Count 0){await SendErrorResponseAsync(id, -32603, No MCP services configured,No MCP service bindings configured for this endpoint, cancellationToken);return;}var allTools new Listobject();// 遍历所有配置的服务,聚合SelectedToolsforeach (var serviceConfig in _config.McpServices){foreach (var tool in serviceConfig.SelectedTools){// 解析存储的InputSchema JSON(完整的工具Schema已在工具同步时保存)var properties new Dictionarystring, object();var required Array.Emptystring();if (!string.IsNullOrEmpty(tool.InputSchema)){var schemaDoc JsonDocument.Parse(tool.InputSchema);if (schemaDoc.RootElement.TryGetProperty(properties, out var propsElement)){properties JsonElementToObject(propsElement) as Dictionarystring, object ?? new();}if (schemaDoc.RootElement.TryGetProperty(required, out var reqElement)){required reqElement.EnumerateArray().Select(x x.GetString() ?? ).ToArray();}}// 构建符合MCP协议的工具定义allTools.Add(new{name tool.Name,description tool.Description,inputSchema new{type object,properties properties,required required,title ${tool.Name}Arguments}});}}// 返回JSON-RPC格式响应var response new{jsonrpc 2.0,id id,result new { tools allTools.ToArray() }};await SendWebSocketResponseAsync(response, cancellationToken);}关键优化:直接从配置读取工具数据,即使MCP客户端连接失败也能返回工具列表完整解析InputSchema的properties和required字段符合MCP协议的工具schema格式要求问题3:小智调用工具怎么路由到对应的MCP服务?解决方案:根据工具名称查找对应的McpClientprivate async Task HandleToolsCallAsync(int? id, JsonDocument request, CancellationToken cancellationToken){var toolName request.RootElement.GetProperty(params).GetProperty(name).GetString();var arguments request.RootElement.GetProperty(params).GetProperty(arguments);// 遍历所有MCP客户端,查找包含该工具的服务for (int i 0; i _mcpClients.Count; i){var mcpClient _mcpClients[i];var serviceConfig _clientIndexToServiceConfig[i];// 如果配置了SelectedTools,则只在选中的工具中查找var selectedTools serviceConfig.SelectedTools;if (selectedTools.Any()){var isToolSelected selectedTools.Any(t t.Name toolName);if (!isToolSelected) continue; // 工具未被选中,跳过此服务}// 检查此MCP客户端是否提供该工具var hasTool mcpClient.Tools?.Any(t t.Name toolName) ?? false;if (!hasTool) continue;// 找到目标服务,调用工具try{var result await mcpClient.CallToolAsync(toolName,JsonSerializer.DeserializeDictionarystring, object(arguments.GetRawText())!,cancellationToken: cancellationToken);// 返回工具调用结果await SendWebSocketResponseAsync(new{jsonrpc 2.0,id id,result result}, cancellationToken);return; // 成功调用,结束}catch (Exception ex){_logger.LogError(ex, Tool call failed for {ToolName} on service {ServiceName},toolName, serviceConfig.ServiceName);// 继续尝试下一个服务}}// 未找到提供该工具的服务await SendErrorResponseAsync(id, -32601, Tool not found,$No MCP service provides tool {toolName}, cancellationToken);}MCP服务认证支持为了支持各种需要认证的MCP服务,我抽象出了统一的认证助手类:/// summary/// MCP认证配置助手类/// 被McpClientService(工具同步)和McpSessionService(WebSocket连接)共享使用/// /summarypublic static class McpAuthenticationHelper{/// summary/// 为Bearer、Basic和API Key认证构建HTTP请求头/// /summarypublic static Dictionarystring, string BuildAuthenticationHeaders(string authenticationType,string authenticationConfig,ILogger? logger null){if (string.IsNullOrEmpty(authenticationType) || string.IsNullOrEmpty(authenticationConfig)){throw new ArgumentException(Authentication type and config cannot be null or empty);}try{var authType authenticationType.ToLowerInvariant();return authType switch{bearer BuildBearerTokenHeaders(authenticationConfig, logger),basic BuildBasicAuthHeaders(authenticationConfig, logger),apikey BuildApiKeyHeaders(authenticationConfig, logger),_ throw new InvalidOperationException($Unsupported authentication type: {authenticationType}. Use BuildOAuth2Options for OAuth 2.0.)};}catch (Exception ex){logger?.LogError(ex, Failed to build authentication headers for type {AuthType}, authenticationType);throw new InvalidOperationException($Failed to configure authentication: {ex.Message}, ex);}}/// summary/// 为OAuth 2.0构建SDK的ClientOAuthOptions配置/// /summarypublic static ClientOAuthOptions BuildOAuth2Options(string authenticationConfig,ILogger? logger null){var authConfig JsonSerializer.DeserializeOAuth2AuthConfig(authenticationConfig);if (string.IsNullOrEmpty(authConfig?.ClientId) || string.IsNullOrEmpty(authConfig.RedirectUri)){throw new InvalidOperationException(OAuth 2.0 Client ID and Redirect URI are required);}logger?.LogDebug(Configuring OAuth 2.0 with Client ID: {ClientId}, authConfig.ClientId);var oauthOptions new ClientOAuthOptions{RedirectUri new Uri(authConfig.RedirectUri),ClientId authConfig.ClientId,ClientSecret authConfig.ClientSecret};if (!string.IsNullOrEmpty(authConfig.Scope)){oauthOptions.Scopes authConfig.Scope.Split( , StringSplitOptions.RemoveEmptyEntries);}return oauthOptions;}// 私有辅助方法: BuildBearerTokenHeaders, BuildBasicAuthHeaders, BuildApiKeyHeaders// ...实现细节已省略(解析JSON配置,构建对应的HTTP请求头)}设计要点:DRY原则:工具同步和WebSocket连接都复用这个助手类,消除了150行重复代码双方法设计:BuildAuthenticationHeaders:处理Bearer/Basic/API Key(通过HTTP Header传递)BuildOAuth2Options:处理OAuth 2.0(使用SDK的ClientOAuthOptions)健壮的错误处理:参数验证、异常捕获、详细错误信息可选日志:通过ILogger参数支持调试,不强制依赖私有辅助方法:每种认证类型的具体实现封装在私有方法中,保持代码清晰分布式WebSocket管理为了支持多实例部署,我使用Redis实现了分布式协调:public class McpSessionManager : IAsyncDisposable{private readonly IDistributedLockService _lockService; // RedLock实现private readonly IConnectionStateService _connectionStateService; // Redis状态管理private readonly Dictionarystring, McpSessionService _sessions new();public async Taskbool StartSessionAsync(string serverId, CancellationToken cancellationToken default){// 1. 本地检查: 避免不必要的锁竞争if (_sessions.ContainsKey(serverId)){_logger.LogInformation(Server {ServerId} session already exists locally, serverId);return false;}// 2. Redis检查: 可能其他实例已连接var connectionState await _connectionStateService.GetConnectionStateAsync(serverId);if (connectionState?.Status ConnectionStatus.Connected){_logger.LogInformation(Server {ServerId} is already connected on instance {InstanceId},serverId, connectionState.InstanceId);return false;}// 3. 获取分布式锁 (RedLock算法)var lockKey $mcp:session:lock:{serverId};var acquired await _lockService.AcquireLockAsync(lockKey,expiryTime: TimeSpan.FromMinutes(5),waitTime: TimeSpan.FromSeconds(10),retryTime: TimeSpan.FromSeconds(1));if (!acquired){_logger.LogWarning(Failed to acquire lock for server {ServerId}, serverId);return false;}try{// 4. Double-check: 再次检查Redis状态(持有锁后)connectionState await _connectionStateService.GetConnectionStateAsync(serverId);if (connectionState?.Status ConnectionStatus.Connected){_logger.LogInformation(Server {ServerId} connected by another instance during lock wait,serverId);return false;}// 5. 构建会话配置(从数据库加载服务绑定、工具等)var config await BuildSessionConfigurationAsync(serverId, cancellationToken);// 6. 创建会话并订阅事件var session new McpSessionService(config, _mcpClientService, _loggerFactory);session.OnConnected async () {await _connectionStateService.RegisterConnectionAsync(serverId, InstanceId);await UpdateEndpointStatusAsync(serverId, isConnected: true);};session.OnConnectionFailed async (error) {await _connectionStateService.UpdateStatusAsync(serverId, ConnectionStatus.Failed);await UpdateEndpointStatusAsync(serverId, isConnected: false);};session.OnDisconnected async () {await _connectionStateService.UnregisterConnectionAsync(serverId);await UpdateEndpointStatusAsync(serverId, isConnected: false);_sessions.Remove(serverId); // 清理本地会话};_sessions[serverId] session;// 7. 在后台启动会话(不阻塞)_ Task.Run(async () {try{await session.ConnectAsync(cancellationToken);}catch (Exception ex){_logger.LogError(ex, Session connection failed for {ServerId}, serverId);}}, cancellationToken);return true;}finally{await _lockService.ReleaseLockAsync(lockKey); // 释放分布式锁}}// ...其他方法: StopSessionAsync, BuildSessionConfigurationAsync等已省略}分布式设计要点:三层检查机制: 本地字典 → Redis状态 → 分布式锁,最小化锁竞争开销RedLock算法: 使用RedLock.net库实现分布式锁,支持多Redis实例容错Double-Check模式: 获取锁后再次检查Redis状态,防止竞态条件下的重复连接事件驱动状态同步: 通过OnConnected/OnDisconnected事件自动更新Redis和数据库非阻塞启动: 会话连接在后台Task中执行,StartSessionAsync立即返回故障转移支持: 实例下线时,监控服务可检测到Redis状态变化并在其他实例重启会话Blazor前端开发体验Blazor WebAssembly实现了纯C#全栈开发,前后端统一技术栈:page /connectionsinject IXiaozhiMcpEndpointClientService ServerServiceinject ISnackbar SnackbarMudDataGrid Items_servers FilterabletrueColumnsPropertyColumn Propertyx x.Name Title名称 /PropertyColumn Propertyx x.IsConnected Title状态CellTemplateif (context.Item.IsConnected){MudChip ColorColor.Success IconIcons.Material.Filled.CheckCircle已连接/MudChip}else if (context.Item.IsEnabled){MudChip ColorColor.Warning未连接/MudChip}else{MudChip已禁用/MudChip}/CellTemplate/PropertyColumnTemplateColumn Title操作CellTemplateif (context.Item.IsEnabled){MudIconButton IconIcons.Material.Filled.PowerOffColorColor.ErrorOnClick(() DisableServerAsync(context.Item.Id!)) /}else{MudIconButton IconIcons.Material.Filled.PlayArrowColorColor.SuccessOnClick(() EnableServerAsync(context.Item.Id!)) /}/CellTemplate/TemplateColumn/Columns/MudDataGridcode {private IEnumerableXiaozhiMcpEndpointDto _servers Array.EmptyXiaozhiMcpEndpointDto();protected override async Task OnInitializedAsync(){await LoadServersAsync();}private async Task EnableServerAsync(string serverId){await ServerService.EnableServerAsync(serverId);Snackbar.Add(WebSocket连接已启动, Severity.Success);await LoadServersAsync();}}使用MudBlazor组件库,界面开发效率很高,而且组件都是Material Design风格,很美观。部署和上线Docker单镜像部署项目配置了完整的Docker支持,前后端打包到一个镜像中:# 基础运行时镜像 - Alpine Linux最小化体积FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS baseWORKDIR /appEXPOSE 8080 8081RUN apk add --no-cache curl icu-libs tzdata# 构建镜像FROM mcr.microsoft.com/dotnet/sdk:9.0 AS buildARG BUILD_CONFIGURATIONReleaseWORKDIR /src# 复制项目文件并还原依赖COPY [Verdure.McpPlatform.sln, .]COPY [src/Verdure.McpPlatform.Api/Verdure.McpPlatform.Api.csproj, src/Verdure.McpPlatform.Api/]COPY [src/Verdure.McpPlatform.Web/Verdure.McpPlatform.Web.csproj, src/Verdure.McpPlatform.Web/]# ...其他项目文件已省略RUN dotnet restore src/Verdure.McpPlatform.Api/Verdure.McpPlatform.Api.csproj# 复制源代码并构建COPY . .WORKDIR /src/src/Verdure.McpPlatform.ApiRUN dotnet publish Verdure.McpPlatform.Api.csproj \-c $BUILD_CONFIGURATION \-o /app/publish \/p:UseAppHostfalse# 最终运行时镜像FROM base AS finalWORKDIR /appCOPY --frombuild /app/publish .ENV ASPNETCORE_URLShttp://:8080ENV ASPNETCORE_ENVIRONMENTProductionENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANTfalseHEALTHCHECK --interval30s --timeout10s --retries3 \CMD curl -f http://localhost:8080/api/health || exit 1ENTRYPOINT [dotnet, Verdure.McpPlatform.Api.dll]配置说明主要需要配置以下环境变量:{ConnectionStrings: {mcpdb: Hostlocalhost;Databaseverdure_mcp;Usernamepostgres;Password***,identitydb: Hostlocalhost;Databaseverdure_identity;Usernamepostgres;Password***,Redis: localhost:6379},Database: {Provider: PostgreSQL, // 或 SQLiteTablePrefix: verdure_ // 数据库表名前缀},Identity: {Url: https://auth.verdure-hiro.cn/realms/maker-community,Realm: maker-community,ClientId: verdure-mcp,Audience: verdure-mcp-api,RequireHttpsMetadata: true,ClockSkewMinutes: 5},ConnectionMonitor: {CheckIntervalSeconds: 30, // 监控检查间隔HeartbeatTimeoutSeconds: 90, // 心跳超时时间ReconnectCooldownSeconds: 60 // 重连冷却时间},Logging: {LogLevel: {Default: Information,Verdure.McpPlatform: Debug}}}关键配置说明:数据库配置:mcpdb: 业务数据库连接字符串(MCP服务配置、连接端点等)identitydb: 身份认证数据库连接字符串(用户、角色等)Provider: 支持PostgreSQL和SQLite两种数据库TablePrefix: 统一的表名前缀,用于多租户部署Redis配置:用于分布式锁和连接状态管理生产环境建议配置密码和SSL: redis:6379,password***,ssltrue,abortConnectfalse身份认证配置:Url: Keycloak服务地址,包含realm路径Realm: Keycloak realm名称ClientId: 客户端IDAudience: API受众标识,用于JWT验证ClockSkewMinutes: 时钟偏移容忍度,处理服务器时间差异连接监控配置:CheckIntervalSeconds: WebSocket连接健康检查间隔HeartbeatTimeoutSeconds: 心跳超时判定时间ReconnectCooldownSeconds: 断开后重连冷却时间开发环境可以设置更短的间隔(15秒)以快速检测问题环境变量方式配置:# 使用环境变量覆盖配置ConnectionStrings__mcpdbHostprod-db;Databaseverdure_mcp;...ConnectionStrings__Redisredis:6379,password***Identity__Urlhttps://auth.example.com/realms/prodConnectionMonitor__CheckIntervalSeconds60总结与展望通过这个项目的开发实践,可以看到.NET生态在全栈开发上的优势:统一的技术栈:从后端API到前端UI都是C#,降低了学习成本成熟的框架支持:EF Core、ASP.NET Core、Blazor等都很完善企业级特性:DDD、仓储模式、分布式协调等都有现成的最佳实践可以参考目前平台已经上线并开源,大家可以访问在线服务体验,也欢迎在GitHub上贡献代码。后续计划:添加更多MCP服务的预置模板实现工具调用的监控和统计功能开发MCP服务的市场和分享机制这个项目展示了.NET在现代全栈开发中的应用,希望能给想学习.NET技术的小伙伴提供一个实战参考,同时也为小智社区的生态建设贡献一份力量。 推荐观看视频教程