using PostFundManagement.Application.Extensions; using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Health; using PostFundManagement.Domain.Mediator; using PostFundManagement.Infrastructure.Database.Services; var builder = WebApplication.CreateBuilder(args); builder.Services.AddFeatureManagement(); builder.Services.AddSharedServices(); builder.Services.AddRegisterServicesWithCaching(builder.Configuration); builder.Services.AddEndpointAndServiceCaching(builder.Configuration); builder.Services.AddTransient(); builder.Services.AddSkorubaApiService(builder.Configuration); builder.Services.AddHttpContextAccessor(); builder.Services.AddMediator(); builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TelemetryPipelineBehavior<,>)); builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingPipelineBehavior<,>)); builder.Services.AddEmailServices(builder.Configuration); builder.Services.AddHashServices(builder.Configuration); builder.Services.AddGarageS3(builder.Configuration); builder.Services.AddHttpClient(); builder.Services.AddDataProtectionDatabase(builder.Configuration); builder.Services.AddApplicationDbContext(builder.Configuration); // Register application MCP tooling builder.Services.AddApplicationMcpTools(); var isStdioMode = args.Contains("--stdio", StringComparer.OrdinalIgnoreCase); if (isStdioMode) { // Local CLI / Stdio transport mode (e.g. for Claude Desktop, local cursor, or command-line agents) builder.Services.AddMcpServer() .WithApplicationMcpTools() .WithStdioServerTransport(); } else { // Web / SSE HTTP transport mode (for network agents, external LLM platforms, and remote orchestrators) builder.Services.AddMcpServer() .WithApplicationMcpTools() .WithHttpTransport(); } builder.Services.AddHealthChecks() .AddCheck("PostgresDatabase") .AddCheck("Self", () => HealthCheckResult.Healthy()); var allowedOrigins = builder.Configuration.GetSection("CorsOrigins").Get() ?? []; builder.Services.AddCors(options => { options.AddPolicy(CorsPolicy, policy => { policy.SetIsOriginAllowed(origin => { if (string.IsNullOrWhiteSpace(origin)) return false; var clean = origin.TrimEnd('/'); if (allowedOrigins.Any(a => string.Equals(a.TrimEnd('/'), clean, StringComparison.OrdinalIgnoreCase))) return true; try { var host = new Uri(origin).Host; if (host.EndsWith("khongisa.co.za", StringComparison.OrdinalIgnoreCase) || host.Equals("localhost", StringComparison.OrdinalIgnoreCase) || host.Equals("127.0.0.1", StringComparison.OrdinalIgnoreCase)) { return true; } } catch { /* ignore */ } return false; }) .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); }); var app = builder.Build(); if (!isStdioMode) { app.UseCors(CorsPolicy); app.UseRouting(); // Map official MCP protocol endpoints app.MapMcp("/mcp"); // Root status info endpoint app.MapGet("/", () => Results.Ok(new { Service = "PostFundManagement.Mcp", Status = "Online", Version = "2.3.0", Protocol = "ModelContextProtocol", McpEndpoint = "/mcp", ToolGroupings = new[] { "Grants", "Projects", "Execution", "Governance", "Evidence", "Communications", "Users" } })); app.UseHealthChecks("/health", new HealthCheckOptions { Predicate = _ => true, AllowCachingResponses = true, ResponseWriter = HealthChecks.UI.Client.UIResponseWriter.WriteHealthCheckUIResponse }); } await app.RunAsync();