使用.NET开发并上线一个小智AI对话机器人的MCP服务转接平台

news/2025/11/26 0:02:15/文章来源:https://www.cnblogs.com/GreenShade/p/19270503

前言

最近小智AI对话机器人在ESP32社区实在是太火了,看过之前文章的小伙伴应该都知道之前有给桌面机器人开发过一个.NET客户端,所以对小智也算是比较熟悉。小智虽然支持MCP(Model Context Protocol)协议来扩展功能,但是小智的MCP端点是一个特殊的WebSocket服务,如果想要为小智开发MCP功能,就需要针对这个特殊的端点进行开发。

于是就想着能不能做一个转接平台,让开发者可以专注于标准MCP服务的开发,而不用关心小智特殊的WebSocket协议细节。这个平台可以将标准的MCP服务聚合后通过WebSocket提供给小智,同时支持多租户,每个用户都可以配置自己专属的MCP服务。

项目已经上线并开源了,大家可以直接访问 https://xiaozhi.verdure-hiro.cn 体验,也可以在GitHub上找到完整源码:verdure-mcp-for-xiaozhi

img

📺 视频演示

想快速了解项目功能和使用方法?观看我的B站视频教程:

B站视频内容 内容简介 适合人群
小智 MCP 转接服务上线与开源 平台介绍、功能演示、在线使用教程 小智商家和小智爱好者
私有化部署与米家智能家居控制 Docker部署教程、米家MCP服务接入实战 需要私有部署和智能家居控制的用户

为什么要做这个项目

技术背景

小智AI的MCP端点采用的是WebSocket协议,这是一个特殊的实现方式,与标准的MCP协议(基于HTTP/SSE)有所不同。如果想要为小智开发MCP功能,开发者需要:

  1. 了解WebSocket协议细节:需要处理连接管理、心跳检测、重连机制等
  2. 实现MCP协议转换:将标准MCP的HTTP/SSE请求转换为WebSocket消息
  3. 处理工具聚合:如果要使用多个MCP服务,需要自己实现工具列表的聚合和路由

解决方案设计

基于这些技术需求,设计了一个MCP服务转接平台来简化开发:

  • 协议转换:自动将标准MCP服务(HTTP/SSE)转换为小智的WebSocket协议
  • 多租户架构:每个用户都有独立的配置空间,互不干扰
  • 可视化管理:通过Web界面管理MCP服务配置,无需手动编辑配置文件
  • 服务聚合:平台作为中间层,将多个MCP服务的工具聚合后提供给小智
  • 分布式支持:支持多实例部署,通过Redis实现分布式协调

从这个项目能学到什么

核心技术栈

  • .NET 9 - 新的.NET框架,性能和开发体验都很棒(准备过段时间升级.NET 10)
  • Blazor 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 List<McpServiceBinding> _serviceBindings = new();public IReadOnlyCollection<McpServiceBinding> 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 List<McpTool> _tools = new();public IReadOnlyCollection<McpTool> 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 ILogger<McpSessionService> _logger;private readonly IMcpClientService _mcpClientService;private readonly McpSessionConfiguration _config;private readonly ReconnectionSettings _reconnectionSettings;// Session stateprivate ClientWebSocket? _webSocket;private readonly List<McpClient> _mcpClients = new();// 🔧 追踪每个客户端的服务配置,用于会话恢复private readonly Dictionary<int, McpServiceEndpoint> _clientIndexToServiceConfig = new();// 🔧 追踪失败的服务,用于定期重试private readonly Dictionary<string, (McpServiceEndpoint Config, DateTime LastAttempt)> _failedServices = new();// Ping timeout monitoringprivate DateTime _lastPingReceivedTime = DateTime.UtcNow;private readonly TimeSpan _pingTimeout = TimeSpan.FromSeconds(120);// Connection status eventspublic event Func<Task>? OnConnected;public event Func<string, Task>? OnConnectionFailed;public event Func<Task>? 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 List<string>();// 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 List<object>();// 遍历所有配置的服务,聚合SelectedToolsforeach (var serviceConfig in _config.McpServices){foreach (var tool in serviceConfig.SelectedTools){// 解析存储的InputSchema JSON(完整的工具Schema已在工具同步时保存)var properties = new Dictionary<string, object>();var required = Array.Empty<string>();if (!string.IsNullOrEmpty(tool.InputSchema)){var schemaDoc = JsonDocument.Parse(tool.InputSchema);if (schemaDoc.RootElement.TryGetProperty("properties", out var propsElement)){properties = JsonElementToObject(propsElement) as Dictionary<string, 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服务?

解决方案:根据工具名称查找对应的McpClient

private 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.Deserialize<Dictionary<string, 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连接)共享使用
/// </summary>
public static class McpAuthenticationHelper
{/// <summary>/// 为Bearer、Basic和API Key认证构建HTTP请求头/// </summary>public static Dictionary<string, 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配置/// </summary>public static ClientOAuthOptions BuildOAuth2Options(string authenticationConfig,ILogger? logger = null){var authConfig = JsonSerializer.Deserialize<OAuth2AuthConfig>(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 Dictionary<string, McpSessionService> _sessions = new();public async Task<bool> 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 "/connections"
@inject IXiaozhiMcpEndpointClientService ServerService
@inject ISnackbar Snackbar<MudDataGrid Items="@_servers" Filterable="true"><Columns><PropertyColumn Property="x => x.Name" Title="名称" /><PropertyColumn Property="x => x.IsConnected" Title="状态"><CellTemplate>@if (context.Item.IsConnected){<MudChip Color="Color.Success" Icon="@Icons.Material.Filled.CheckCircle">已连接</MudChip>}else if (context.Item.IsEnabled){<MudChip Color="Color.Warning">未连接</MudChip>}else{<MudChip>已禁用</MudChip>}</CellTemplate></PropertyColumn><TemplateColumn Title="操作"><CellTemplate>@if (context.Item.IsEnabled){<MudIconButton Icon="@Icons.Material.Filled.PowerOff" Color="Color.Error" OnClick="@(() => DisableServerAsync(context.Item.Id!))" />}else{<MudIconButton Icon="@Icons.Material.Filled.PlayArrow" Color="Color.Success" OnClick="@(() => EnableServerAsync(context.Item.Id!))" />}</CellTemplate></TemplateColumn></Columns>
</MudDataGrid>@code {private IEnumerable<XiaozhiMcpEndpointDto> _servers = Array.Empty<XiaozhiMcpEndpointDto>();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 base
WORKDIR /app
EXPOSE 8080 8081
RUN apk add --no-cache curl icu-libs tzdata# 构建镜像
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /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.Api"
RUN dotnet publish "Verdure.McpPlatform.Api.csproj" \-c $BUILD_CONFIGURATION \-o /app/publish \/p:UseAppHost=false# 最终运行时镜像
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .ENV ASPNETCORE_URLS=http://+:8080
ENV ASPNETCORE_ENVIRONMENT=Production
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=falseHEALTHCHECK --interval=30s --timeout=10s --retries=3 \CMD curl -f http://localhost:8080/api/health || exit 1ENTRYPOINT ["dotnet", "Verdure.McpPlatform.Api.dll"]

配置说明

主要需要配置以下环境变量:

{"ConnectionStrings": {"mcpdb": "Host=localhost;Database=verdure_mcp;Username=postgres;Password=***","identitydb": "Host=localhost;Database=verdure_identity;Username=postgres;Password=***","Redis": "localhost:6379"},"Database": {"Provider": "PostgreSQL",  // 或 "SQLite""TablePrefix": "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"}}
}

关键配置说明:

  1. 数据库配置:

    • mcpdb: 业务数据库连接字符串(MCP服务配置、连接端点等)
    • identitydb: 身份认证数据库连接字符串(用户、角色等)
    • Provider: 支持PostgreSQL和SQLite两种数据库
    • TablePrefix: 统一的表名前缀,用于多租户部署
  2. Redis配置:

    • 用于分布式锁和连接状态管理
    • 生产环境建议配置密码和SSL: "redis:6379,password=***,ssl=true,abortConnect=false"
  3. 身份认证配置:

    • Url: Keycloak服务地址,包含realm路径
    • Realm: Keycloak realm名称
    • ClientId: 客户端ID
    • Audience: API受众标识,用于JWT验证
    • ClockSkewMinutes: 时钟偏移容忍度,处理服务器时间差异
  4. 连接监控配置:

    • CheckIntervalSeconds: WebSocket连接健康检查间隔
    • HeartbeatTimeoutSeconds: 心跳超时判定时间
    • ReconnectCooldownSeconds: 断开后重连冷却时间
    • 开发环境可以设置更短的间隔(15秒)以快速检测问题

环境变量方式配置:

# 使用环境变量覆盖配置
ConnectionStrings__mcpdb="Host=prod-db;Database=verdure_mcp;..."
ConnectionStrings__Redis="redis:6379,password=***"
Identity__Url="https://auth.example.com/realms/prod"
ConnectionMonitor__CheckIntervalSeconds=60

总结与展望

通过这个项目的开发实践,可以看到.NET生态在全栈开发上的优势:

  • 统一的技术栈:从后端API到前端UI都是C#,降低了学习成本
  • 成熟的框架支持:EF Core、ASP.NET Core、Blazor等都很完善
  • 企业级特性:DDD、仓储模式、分布式协调等都有现成的最佳实践可以参考

目前平台已经上线并开源,大家可以访问在线服务体验,也欢迎在GitHub上贡献代码。后续计划:

  • 添加更多MCP服务的预置模板
  • 实现工具调用的监控和统计功能
  • 开发MCP服务的市场和分享机制

这个项目展示了.NET在现代全栈开发中的应用,希望能给想学习.NET技术的小伙伴提供一个实战参考,同时也为小智社区的生态建设贡献一份力量。

📺 推荐观看视频教程

如果你想更直观地了解项目的使用方法和部署流程,强烈推荐观看我的B站视频内容:

  • 小智 MCP 转接服务上线与开源 - 快速上手指南
  • 私有化部署与米家智能家居控制 - 深度实战教程
  • B站主页 @绿荫阿广 - 获取更多AI和创客教程

参考推荐

  • 创客社区地址
  • 项目开源仓库
  • 在线体验地址
  • 小智AI官网
  • MCP协议规范
  • 小智MCP例子
  • .NET官方文档
  • Blazor官方文档
  • MudBlazor组件库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/976628.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nginx 代理的请求头设置

反向代理中请求头设置 location /superone/ { proxy_pass http://1xx.xx.xx.xx:1x13/; proxy_redirect default; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forward…

全国最好的有机农场推荐——德芳有机农场

一、德芳简介 德芳的故事始于2009年,创始人何女士走遍中国众多地方,遇到这片得天得厚的种植天堂。 农场坐落于江苏盱眙,占地达10平方公里,拥有五大种植园地,富含矿物质的火山岩地质,有可供直饮的水质资源。 德芳…

从网页复制变化内容的一个简单方法

在网页看到了想要复制的内容, 打开F12正欲复制, 但是内容是一段段输出的当想要复制网页中的变化内容时, 打开F12再一个个复制对我来说实在太麻烦了有这样的解决方案:打开F12的控制台, 定义函数:function observeElemen…

2025年11月PMMA板片生产线,EVA板片生产线,PET板片生产线厂家权威推荐,透明板材设备品质红榜发布!

2025年11月PMMA板片生产线,EVA板片生产线,PET板片生产线厂家权威推荐在塑料加工行业蓬勃发展的当下,优质的板片生产线对于众多企业而言至关重要。2025年11月,我们为大家权威推荐一家在板片生产线领域表现出色的企业…

2025年11月管道除锈设备,管道3pe设备,管道内壁喷粉设备厂家推荐,防腐工艺与适配管径测评!

引言在当今的基础设施建设和工业生产领域,管道设备的质量与性能对于整个系统的稳定运行起着至关重要的作用。为了能给广大用户提供具有权威性的管道设备厂家推荐,国际知名的工业设备测评协会开展了一项全面且严谨的测…

实验三 类和对象

实验1 1.源代码#pragma once#include <iostream> #include <string>class Button { public:Button(const std::string &label_);const std::string& get_label() const;void click();private:std:…

2025年11月管道除锈设备,管道涂塑设备,管道设备厂家品牌榜,严苛工况适配性深度解析!

引言在全球管道设备市场中,对于管道设备厂家的评估至关重要。国际管道设备协会曾针对全球范围内的众多管道设备厂家进行了一场全面且权威的测评。此次测评涵盖了技术研发能力、产品性能稳定性、服务质量以及节能环保等…

2025年11月钢管涂塑设备,钢管3PE设备,钢管防腐设备厂商推荐:聚焦机械制造实力与核心技术竞争力

2025年11月钢管涂塑设备,钢管3PE设备,钢管防腐设备厂商推荐:聚焦机械制造实力与核心技术竞争力在2025年11月,当众多企业在寻找优质的钢管涂塑设备、钢管3PE设备以及钢管防腐设备时,青岛涂疆管道科技有限公司无疑是…

2025年11月管道3pe设备,管道设备,管道涂塑设备厂家权威推荐,96小时连续运行稳定性实测!

引言在全球管道设备市场中,产品的质量与性能直接影响着众多行业的发展。为了能为广大企业和用户筛选出优质的管道设备厂家,国际知名的管道设备测评协会开展了一项全面且严谨的测评活动。该协会采用了多维度的测评方法…

2025年11月钢管粉末喷涂设备,钢管设备,钢管3PE设备公司推荐,专业防腐机械制造与品质保障之选

《2025年11月钢管粉末喷涂设备、钢管设备、钢管3PE设备优质公司推荐》在管道防腐设备领域,青岛涂疆管道科技有限公司是众多企业值得信赖的选择。该公司成立于2017年,扎根于山东省青岛市这片产业沃土,是一家专注于管…

2025年11月光伏热镀锌螺栓,热镀锌螺栓,外六角热镀锌螺栓厂家最新推荐,光伏工程紧固品质排名与采购攻略!

2025年11月光伏热镀锌螺栓厂家推荐与采购攻略在光伏工程建设中,热镀锌螺栓作为重要的紧固部件,其质量直接影响着工程的稳定性和耐久性。尤其是外六角热镀锌螺栓,在光伏项目中应用广泛。今天,为大家推荐一家值得信赖…

2025年11月地脚螺栓预埋件,热镀锌地脚螺栓,光伏地脚螺栓厂家推荐,抗腐蚀与适配场景实用指南!

2025年11月地脚螺栓预埋件,热镀锌地脚螺栓,光伏地脚螺栓厂家推荐,抗腐蚀与适配场景实用指南!在建筑、能源等众多行业中,地脚螺栓预埋件、热镀锌地脚螺栓以及光伏地脚螺栓都发挥着至关重要的作用。2025年11月,如果…

二手电商技术架构准备

一、前台系统:用户触达与交互层​ 多端接入(APP、小程序、H5)、高并发访问、个性化推荐、实时交互等功能技术选型与架构设计​ 跨端框架:采用Flutter(如闲鱼)实现iOS、Android、小程序等多端统一前端框架:基于V…

2025年11月电力热镀锌螺栓,10.9级热镀锌螺栓,热镀锌螺栓厂家权威测评,高强度防腐紧固品牌优选榜单!

2025年11月电力热镀锌螺栓,10.9级热镀锌螺栓权威测评:高强度防腐紧固品牌优选在众多的热镀锌螺栓厂家中,河北中洲金属制品有限公司以其卓越的产品质量和服务脱颖而出,成为高强度防腐紧固领域的优选品牌。河北中洲金…

2025年11月软包隔音门,录音棚隔音门,静音隔音门厂家最新推荐,定制化隔音方案解析

2025年11月软包隔音门、录音棚隔音门、静音隔音门厂家最新推荐:定制化隔音方案解析在当下的生活与工作中,隔音需求无处不在。无论是录音棚、直播间,还是家庭卧室、办公室等场所,都迫切需要良好的隔音效果来保障安静…

2025年11月电力热镀锌螺栓,防腐热镀锌螺栓,10.9级热镀锌螺栓厂家品牌榜,工业紧固技术实力与口碑红榜!

2025年11月电力热镀锌螺栓等优质之选——河北中洲金属制品有限公司在工业领域,螺栓作为重要的紧固部件,其质量和性能直接关系到工程的安全与稳定。2025年11月,河北中洲金属制品有限公司凭借其在电力热镀锌螺栓、防腐…

2025年11月钢结构地脚螺栓,9字型地脚螺栓,地脚螺栓厂家优选,工业级品质与工程案例实景呈现!

2025年11月:河北中洲金属制品,地脚螺栓优选之选在2025年11月,当您在寻找高品质的钢结构地脚螺栓和9字型地脚螺栓时,河北中洲金属制品有限公司无疑是值得优选的地脚螺栓厂家。该公司以工业级品质和丰富的工程案例实…

2025年11月静音隔音门,钢质隔音门,实验室隔音门厂家品牌推荐,隔音等级权威测评

2025年11月静音隔音门、钢质隔音门、实验室隔音门厂家品牌推荐及隔音等级权威测评在如今追求安静舒适环境的时代,静音隔音门、钢质隔音门、实验室隔音门等各类隔音门的需求日益增长。对于有隔音需求的场所来说,选择一…

2025年11月诊断故障测振仪,振动分析测振仪,高精度测振仪公司推荐,专业测振设备制造与品牌保障之选

2025 年 11 月诊断故障测振仪之选:北京森德格科技有限公司在工业生产中,设备的稳定运行至关重要,而测振仪等振动测量设备则是保障设备健康的关键工具。在众多测振仪公司中,北京森德格科技有限公司凭借其深厚的技术…

2025年11月录音棚隔音门,静音隔音门,钢质隔音门厂家权威推荐,专业场景适配测评

2025年11月录音棚隔音门,静音隔音门,钢质隔音门厂家权威推荐,专业场景适配测评在当今社会,无论是专业的录音棚、直播间,还是家庭的影音室、KTV等场所,对隔音的需求都日益增长。选择一款合适的隔音门,成为了保障…