From 99224da241bcbf83040acd3c8efcdd68e8afa67a Mon Sep 17 00:00:00 2001 From: Shawn Jackson Date: Fri, 13 Feb 2026 18:15:42 -0800 Subject: [PATCH] RE1-T102 Fixing MCP server issue. --- Web/Resgrid.Web.Mcp/Program.cs | 117 +++++---------------------------- Web/Resgrid.Web.Mcp/Startup.cs | 94 ++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 102 deletions(-) create mode 100644 Web/Resgrid.Web.Mcp/Startup.cs diff --git a/Web/Resgrid.Web.Mcp/Program.cs b/Web/Resgrid.Web.Mcp/Program.cs index 9bd32bc9..585a897e 100644 --- a/Web/Resgrid.Web.Mcp/Program.cs +++ b/Web/Resgrid.Web.Mcp/Program.cs @@ -44,131 +44,44 @@ public static IHostBuilder CreateHostBuilder(string[] args) => { webBuilder.UseSentry(options => { - //options.MinimumBreadcrumbLevel = LogEventLevel.Debug; - //options.MinimumEventLevel = LogEventLevel.Error; options.Dsn = ExternalErrorConfig.ExternalErrorServiceUrlForMcp; options.AttachStacktrace = true; options.SendDefaultPii = true; options.AutoSessionTracking = true; - - //if (ExternalErrorConfig.SentryPerfSampleRate > 0) - // options.EnableTracing = true; - options.TracesSampleRate = ExternalErrorConfig.SentryPerfSampleRate; options.Environment = ExternalErrorConfig.Environment; - options.Release = Assembly.GetEntryAssembly().GetName().Version.ToString(); + options.Release = Assembly.GetEntryAssembly()?.GetName().Version?.ToString(); options.ProfilesSampleRate = ExternalErrorConfig.SentryProfilingSampleRate; - // Requires NuGet package: Sentry.Profiling - // Note: By default, the profiler is initialized asynchronously. This can be tuned by passing a desired initialization timeout to the constructor. - options.AddIntegration(new ProfilingIntegration( - // During startup, wait up to 500ms to profile the app startup code. This could make launching the app a bit slower so comment it out if your prefer profiling to start asynchronously - //TimeSpan.FromMilliseconds(500) - )); + // Add profiling integration + options.AddIntegration(new ProfilingIntegration()); - options.TracesSampler = samplingContext => - { - if (samplingContext != null && samplingContext.CustomSamplingContext != null) + options.TracesSampler = samplingContext => { - if (samplingContext.CustomSamplingContext.TryGetValue("__HttpPath", out var httpPath)) + if (samplingContext?.CustomSamplingContext != null) { - var pathValue = httpPath?.ToString(); - if (string.Equals(pathValue, "/health/getcurrent", StringComparison.OrdinalIgnoreCase)) + if (samplingContext.CustomSamplingContext.TryGetValue("__HttpPath", out var httpPath)) { - return 0; + var pathValue = httpPath?.ToString(); + if (string.Equals(pathValue, "/health/getcurrent", StringComparison.OrdinalIgnoreCase)) + { + return 0; + } } } - } - return ExternalErrorConfig.SentryPerfSampleRate; - }; + return ExternalErrorConfig.SentryPerfSampleRate; + }; }); } webBuilder.UseKestrel(serverOptions => { // Configure Kestrel to listen on a specific port for health checks - serverOptions.ListenAnyIP(5050); // Health check port - }); - webBuilder.Configure(app => - { - app.UseRouting(); - app.UseSentryTracing(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); + serverOptions.ListenAnyIP(5050); }); - }) - .ConfigureAppConfiguration((_, config) => - { - config.SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddEnvironmentVariables() - .AddCommandLine(args); - }) - .ConfigureServices((hostContext, services) => - { - var configuration = hostContext.Configuration; - - // Configuration is already loaded in ConfigureWebHostDefaults - // Initialize Resgrid logging framework with Sentry if available - if (!string.IsNullOrWhiteSpace(ExternalErrorConfig.ExternalErrorServiceUrlForMcp)) - { - Framework.Logging.Initialize(ExternalErrorConfig.ExternalErrorServiceUrlForMcp); - } - - // Register MCP server - services.AddHostedService(); - - // Add MVC controllers for health check endpoint - services.AddControllers() - .AddNewtonsoftJson(); - - // Register infrastructure services - services.AddMemoryCache(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - - // Validate required API configuration from SystemBehaviorConfig - if (string.IsNullOrWhiteSpace(SystemBehaviorConfig.ResgridApiBaseUrl)) - { - throw new InvalidOperationException( - "SystemBehaviorConfig.ResgridApiBaseUrl is required but not configured. " + - "Configure this setting via the Resgrid configuration file or environment variables (RESGRID:SystemBehaviorConfig:ResgridApiBaseUrl)."); - } - - // Register HTTP client for API calls with connection pooling - services.AddHttpClient("ResgridApi", client => - { - client.BaseAddress = new Uri(SystemBehaviorConfig.ResgridApiBaseUrl); - client.DefaultRequestHeaders.Add("Accept", "application/json"); - client.Timeout = TimeSpan.FromSeconds(30); - }) - .ConfigurePrimaryHttpMessageHandler(() => new System.Net.Http.SocketsHttpHandler - { - PooledConnectionLifetime = TimeSpan.FromMinutes(5), - PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), - MaxConnectionsPerServer = 10 - }); - - // Register API client - services.AddSingleton(); - // Register tool providers - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + webBuilder.UseStartup(); }); } } diff --git a/Web/Resgrid.Web.Mcp/Startup.cs b/Web/Resgrid.Web.Mcp/Startup.cs new file mode 100644 index 00000000..dc0e0c8f --- /dev/null +++ b/Web/Resgrid.Web.Mcp/Startup.cs @@ -0,0 +1,94 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Resgrid.Config; +using Resgrid.Web.Mcp.Infrastructure; +using Resgrid.Web.Mcp.Tools; + +namespace Resgrid.Web.Mcp +{ + public class Startup + { + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public void ConfigureServices(IServiceCollection services) + { + // Initialize Resgrid logging framework with Sentry if available + if (!string.IsNullOrWhiteSpace(ExternalErrorConfig.ExternalErrorServiceUrlForMcp)) + { + Framework.Logging.Initialize(ExternalErrorConfig.ExternalErrorServiceUrlForMcp); + } + + // Register MCP server + services.AddHostedService(); + + // Add MVC controllers for health check endpoint + services.AddControllers() + .AddNewtonsoftJson(); + + // Register infrastructure services + services.AddMemoryCache(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + // Validate required API configuration from SystemBehaviorConfig + if (string.IsNullOrWhiteSpace(SystemBehaviorConfig.ResgridApiBaseUrl)) + { + throw new InvalidOperationException( + "SystemBehaviorConfig.ResgridApiBaseUrl is required but not configured. " + + "Configure this setting via the Resgrid configuration file or environment variables (RESGRID:SystemBehaviorConfig:ResgridApiBaseUrl)."); + } + + // Register HTTP client for API calls with connection pooling + services.AddHttpClient("ResgridApi", client => + { + client.BaseAddress = new Uri(SystemBehaviorConfig.ResgridApiBaseUrl); + client.DefaultRequestHeaders.Add("Accept", "application/json"); + client.Timeout = TimeSpan.FromSeconds(30); + }) + .ConfigurePrimaryHttpMessageHandler(() => new System.Net.Http.SocketsHttpHandler + { + PooledConnectionLifetime = TimeSpan.FromMinutes(5), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), + MaxConnectionsPerServer = 10 + }); + + // Register API client + services.AddSingleton(); + + // Register tool providers + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} + +