Added MCp support

This commit is contained in:
2026-10-30 10:16:27 +02:00
parent d7806ff784
commit 58f8006ab2
57 changed files with 1832 additions and 22 deletions
+41
View File
@@ -0,0 +1,41 @@
# Stage 1: Runtime base
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
# Stage 2: Build & Restore
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
# Copy props and csproj files for layer caching
COPY ["Directory.Build.props", "./"]
COPY ["Directory.Packages.props", "./"]
COPY ["PostFundManagement.snk", "./"]
COPY ["LICENSE", "./"]
COPY ["PostFundManagement.Mcp/PostFundManagement.Mcp.csproj", "PostFundManagement.Mcp/"]
COPY ["PostFundManagement.Application/PostFundManagement.Application.csproj", "PostFundManagement.Application/"]
COPY ["PostFundManagement.Domain/PostFundManagement.Domain.csproj", "PostFundManagement.Domain/"]
COPY ["PostFundManagement.Infrastructure/PostFundManagement.Infrastructure.csproj", "PostFundManagement.Infrastructure/"]
# Restore packages
RUN dotnet restore "PostFundManagement.Mcp/PostFundManagement.Mcp.csproj"
# Copy full source code
COPY . .
# Build from the project folder using relative filename
WORKDIR "/src/PostFundManagement.Mcp"
RUN dotnet build "PostFundManagement.Mcp.csproj" -c Release -o /app/build
# Stage 3: Publish
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "PostFundManagement.Mcp.csproj" -c Release -o /app/publish /p:UseAppHost=false
# Stage 4: Final Runtime
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "PostFundManagement.Mcp.dll"]
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<UserSecretsId>dotnet-PostFundManagement.Mcp-38a42b10-67c2-4889-b1d5-89f5cda11234</UserSecretsId>
</PropertyGroup>
<!-- Health Checks -->
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" />
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" />
</ItemGroup>
<!-- Model Context Protocol -->
<ItemGroup>
<PackageReference Include="ModelContextProtocol.AspNetCore" />
</ItemGroup>
<!-- File Nesting -->
<ItemGroup>
<ProjectCapability Include="ConfigurableFileNesting" />
<ProjectCapability Include="ConfigurableFileNestingFeatureEnabled" />
</ItemGroup>
<!-- CQRS Source Generator -->
<ItemGroup>
<PackageReference Include="Mediator.SourceGenerator">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PostFundManagement.Application\PostFundManagement.Application.csproj" />
</ItemGroup>
</Project>
+125
View File
@@ -0,0 +1,125 @@
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<ITenantContextService, HeadlessTenantContextService>();
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<PostgresHealthCheck>("PostgresDatabase")
.AddCheck("Self", () => HealthCheckResult.Healthy());
var allowedOrigins = builder.Configuration.GetSection("CorsOrigins").Get<string[]>() ?? [];
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();
@@ -0,0 +1,12 @@
{
"profiles": {
"PostFundManagement.Mcp": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:50668;http://localhost:50669"
}
}
}
+42
View File
@@ -0,0 +1,42 @@
{
"EnableServiceCaching": true,
"CorsOrigins": [
"https://partner.ifri.khongisa.co.za",
"https://console.ifri.khongisa.co.za",
"https://localhost:7208",
"http://localhost:5173",
"http://localhost:5174"
],
"GeneralS3Settings": {
"ServiceUrl": "https://s3.garage.khongisa.co.za",
"Region": "garage",
"BucketName": "pfm",
"CdnBaseUrl": "https://pfm.cdn.khongisa.co.za"
},
"Monitoring": {
"Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889",
"ServiceName": "Pfm.MCP"
},
"SecuritySettings": {
"Authority": "https://sts.security.khongisa.co.za",
"Audience": "pfm-mcp-dev"
},
"MachineIdentitySettings": {
"Authority": "https://sts.security.khongisa.co.za",
"Scope": "skoruba_identity_admin_api"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"ModelContextProtocol": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database": "Error",
"Microsoft.EntityFrameworkCore.Update": "Error"
}
},
"HasherSettings": {
"MinHashLength": 11
},
"AllowedHosts": "*"
}