Skip to content

Commit facbd6c

Browse files
committed
fix: Ignore all certificates in MCP server
1 parent f7dd718 commit facbd6c

File tree

14 files changed

+326
-119
lines changed

14 files changed

+326
-119
lines changed

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.7" />
77
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" />
88
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
9+
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.7" />
910
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
1011
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.7.0" />
1112
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />

Dockerfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
2+
3+
# This stage is used when running from VS in fast mode (Default for Debug configuration)
4+
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
5+
USER $APP_UID
6+
WORKDIR /app
7+
8+
# This stage is used to build the service project
9+
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
10+
WORKDIR /
11+
COPY . .
12+
RUN dotnet restore "./src/OpenApi.Client.Mcp/OpenApi.Client.Mcp.csproj"
13+
COPY . .
14+
RUN dotnet build "./src/OpenApi.Client.Mcp/OpenApi.Client.Mcp.csproj" -c Release -o /app/build
15+
16+
# This stage is used to publish the service project to be copied to the final stage
17+
FROM build AS publish
18+
RUN dotnet publish "./src/OpenApi.Client.Mcp/OpenApi.Client.Mcp.csproj" -c Release -o /app/publish /p:UseAppHost=false
19+
20+
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
21+
FROM base AS final
22+
WORKDIR /app
23+
COPY --from=publish /app/publish .
24+
ENTRYPOINT ["dotnet", "OpenApi.Client.Mcp.dll"]

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,42 @@ OpenAPI Client is a toolkit that helps you create HTTP clients for external APIs
77

88
The repository contains NuGet package source code, which uses C# code generators that can be used to generate native C# API clients from YAML or JSON files.
99

10+
## MCP Server
11+
12+
Build for yourself:
13+
14+
```cmd
15+
docker buildx build ./ -f ./src/OpenApi.Client.Mcp/Dockerfile -t mcp/openapi --no-cache
16+
```
17+
18+
or
19+
20+
```cmd
21+
dotnet publish .\src\OpenApi.Client.Mcp\OpenApi.Client.Mcp.csproj -c Release /t:PublishContainer
22+
```
23+
24+
Then
25+
26+
```cmd
27+
docker run -d --name mcp-openapi mcp/openapi -e MODE=Stdio
28+
docker run -d --name mcp-openapi mcp/openapi -e MODE=Http -p 64622:8080
29+
```
30+
31+
You can configure your *mcp.json* file
32+
33+
```json
34+
{
35+
"servers": {
36+
"openapi.mcp": {
37+
"type": "stdio",
38+
"command": "docker",
39+
"args": ["run", "-i", "--rm", "mcp/openapi"]
40+
}
41+
},
42+
"inputs": []
43+
}
44+
```
45+
1046
## Gettings started
1147

1248
OpenApiClient is available as NuGet package on NuGet.org:
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// This Source Code Form is subject to the terms of the MIT License.
2+
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
3+
// Copyright (C) Leszek Pomianowski and OpenAPI Client Contributors.
4+
// All Rights Reserved.
5+
6+
namespace OpenApi.Client.Mcp.Configuration;
7+
8+
internal static class ConfigurationFactory
9+
{
10+
public static ServerOptions CreateServerOptions(string[] args)
11+
{
12+
IConfigurationRoot configuration = CreateConfiguration(args);
13+
14+
string? mode =
15+
configuration["Mode"]
16+
?? configuration["MODE"]
17+
?? configuration["mode"]
18+
?? configuration["Server:Mode"];
19+
20+
return new ServerOptions { Mode = ParseMode(mode) };
21+
}
22+
23+
private static IConfigurationRoot CreateConfiguration(string[] args)
24+
{
25+
string environment =
26+
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
27+
?? Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")
28+
?? "Production";
29+
30+
IConfigurationBuilder builder = new ConfigurationBuilder()
31+
.SetBasePath(AppContext.BaseDirectory)
32+
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
33+
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
34+
.AddEnvironmentVariables()
35+
.AddCommandLine(args);
36+
37+
return builder.Build();
38+
}
39+
40+
private static McpMode ParseMode(string? mode)
41+
{
42+
if (mode is null)
43+
{
44+
return McpMode.Stdio;
45+
}
46+
47+
if (mode.Contains("both", StringComparison.InvariantCultureIgnoreCase))
48+
{
49+
return McpMode.Both;
50+
}
51+
52+
if (mode.Contains("http", StringComparison.InvariantCultureIgnoreCase))
53+
{
54+
return McpMode.Http;
55+
}
56+
57+
return McpMode.Stdio;
58+
}
59+
}

src/OpenApi.Client.Mcp/Dockerfile

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,18 @@
44
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
55
USER $APP_UID
66
WORKDIR /app
7-
EXPOSE 8080
8-
97

108
# This stage is used to build the service project
119
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
12-
ARG BUILD_CONFIGURATION=Release
13-
WORKDIR /src
14-
COPY ["Directory.Packages.props", "."]
15-
COPY ["Directory.Build.props", "."]
16-
COPY ["Directory.Build.targets", "."]
17-
COPY ["nuget.config", "."]
18-
COPY ["src/OpenApi.Client.Mcp/OpenApi.Client.Mcp.csproj", "src/OpenApi.Client.Mcp/"]
19-
COPY ["src/OpenApi.Client.SourceGenerators/OpenApi.Client.SourceGenerators.csproj", "src/OpenApi.Client.SourceGenerators/"]
10+
WORKDIR /
11+
COPY . .
2012
RUN dotnet restore "./src/OpenApi.Client.Mcp/OpenApi.Client.Mcp.csproj"
2113
COPY . .
22-
WORKDIR "/src/src/OpenApi.Client.Mcp"
23-
RUN dotnet build "./OpenApi.Client.Mcp.csproj" -c $BUILD_CONFIGURATION -o /app/build
14+
RUN dotnet build "./src/OpenApi.Client.Mcp/OpenApi.Client.Mcp.csproj" -c Release -o /app/build
2415

2516
# This stage is used to publish the service project to be copied to the final stage
2617
FROM build AS publish
27-
ARG BUILD_CONFIGURATION=Release
28-
RUN dotnet publish "./OpenApi.Client.Mcp.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
18+
RUN dotnet publish "./src/OpenApi.Client.Mcp/OpenApi.Client.Mcp.csproj" -c Release -o /app/publish /p:UseAppHost=false
2919

3020
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
3121
FROM base AS final

src/OpenApi.Client.Mcp/GlobalUsings.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,30 @@
33
// Copyright (C) Leszek Pomianowski and OpenAPI Client Contributors.
44
// All Rights Reserved.
55

6+
global using System;
7+
global using System.Collections.Generic;
8+
global using System.ComponentModel;
9+
global using System.Linq;
10+
global using System.Net.Http;
11+
global using System.Threading;
12+
global using System.Threading.Tasks;
613
global using Microsoft.AspNetCore.Builder;
714
global using Microsoft.CodeAnalysis;
815
global using Microsoft.Extensions.Configuration;
916
global using Microsoft.Extensions.DependencyInjection;
17+
global using Microsoft.Extensions.Hosting;
1018
global using Microsoft.Extensions.Logging;
19+
global using ModelContextProtocol.Protocol;
20+
global using ModelContextProtocol.Server;
1121
global using OpenApi.Client.Mcp.Configuration;
22+
global using OpenApi.Client.Mcp.Logging;
1223
global using OpenApi.Client.Mcp.Services;
1324
global using OpenApi.Client.Mcp.Tools;
25+
#if DEBUG
1426
global using OpenTelemetry;
1527
global using OpenTelemetry.Metrics;
1628
global using OpenTelemetry.Trace;
17-
global using System;
18-
global using System.Collections.Generic;
19-
global using System.ComponentModel;
20-
global using System.IO;
21-
global using System.Linq;
22-
global using System.Net.Http;
23-
global using System.Threading;
24-
global using System.Threading.Tasks;
29+
#endif
30+
#if !DEBUG
31+
global using Microsoft.AspNetCore.Hosting;
32+
#endif
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// This Source Code Form is subject to the terms of the MIT License.
2+
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
3+
// Copyright (C) Leszek Pomianowski and OpenAPI Client Contributors.
4+
// All Rights Reserved.
5+
6+
namespace OpenApi.Client.Mcp.Logging;
7+
8+
internal static class StaticLoggerFactory
9+
{
10+
public static ILogger New(string category = "OpenAPI.Toolkit") =>
11+
LoggerFactory
12+
.Create(loggerBuilder =>
13+
{
14+
loggerBuilder.AddConsole(consoleLogOptions =>
15+
{
16+
// Configure all logs to go to stderr
17+
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
18+
});
19+
loggerBuilder.SetMinimumLevel(LogLevel.Trace);
20+
})
21+
.CreateLogger(category);
22+
}

src/OpenApi.Client.Mcp/OpenApi.Client.Mcp.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@
55
<DockerfileContext>..\..</DockerfileContext>
66
<ContainerDevelopmentMode>Fast</ContainerDevelopmentMode>
77
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
8+
<EnableSdkContainerSupport>true</EnableSdkContainerSupport>
9+
<ContainerFamily>alpine</ContainerFamily>
10+
<ContainerRepository>mcp/openapi</ContainerRepository>
11+
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
812
</PropertyGroup>
913
<ItemGroup>
1014
<PackageReference Include="Microsoft.CodeAnalysis.Common" />
15+
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
1116
<PackageReference Include="ModelContextProtocol" />
1217
<PackageReference Include="ModelContextProtocol.AspNetCore" />
1318
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" />

src/OpenApi.Client.Mcp/Program.cs

Lines changed: 74 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,66 +3,97 @@
33
// Copyright (C) Leszek Pomianowski and OpenAPI Client Contributors.
44
// All Rights Reserved.
55

6-
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
6+
ILogger logger = StaticLoggerFactory.New();
7+
ServerOptions serverOptions = ConfigurationFactory.CreateServerOptions(args);
78

8-
builder.Logging.AddConsole(consoleLogOptions =>
9-
{
10-
// Configure all logs to go to stderr
11-
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
12-
});
13-
14-
#if DEBUG
15-
builder
16-
.Services.AddOpenTelemetry()
17-
.WithTracing(b => b.AddSource("*").AddAspNetCoreInstrumentation().AddHttpClientInstrumentation())
18-
.WithMetrics(b => b.AddMeter("*").AddAspNetCoreInstrumentation().AddHttpClientInstrumentation())
19-
.WithLogging()
20-
.UseOtlpExporter();
21-
#endif
9+
logger.LogInformation("Configuring MCP Server with mode: {Mode}", serverOptions.Mode);
2210

23-
builder.Services.AddTransient<IOpenApiService, OpenApiService>();
11+
// NOTE: Do not use whole ASP.NET stack with just interactive console
12+
if (serverOptions.Mode == McpMode.Stdio)
13+
{
14+
logger.LogInformation("Adding Stdio transport to MCP Server.");
2415

25-
builder.Services.AddHttpClient();
16+
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
17+
AddMcpServices(builder, serverOptions.Mode).WithStdioServerTransport();
2618

27-
ServerOptions serverOptions = new();
28-
IConfigurationSection section = builder.Configuration.GetSection("Server");
29-
section.Bind(serverOptions);
19+
logger.LogInformation("Starting the application.");
3020

31-
string? mode = Environment.GetEnvironmentVariable("mode") ?? Environment.GetEnvironmentVariable("MODE");
21+
await builder.Build().RunAsync();
3222

33-
if (mode?.Contains("both", StringComparison.InvariantCultureIgnoreCase) ?? false)
34-
{
35-
serverOptions.Mode = McpMode.Both;
36-
}
37-
else if (mode?.Contains("http", StringComparison.InvariantCultureIgnoreCase) ?? false)
38-
{
39-
serverOptions.Mode = McpMode.Http;
23+
// NOTE: Stop here, as we are running in Stdio mode.
24+
return;
4025
}
4126

42-
IMcpServerBuilder mcpBuilder = builder.Services.AddMcpServer();
27+
logger.LogInformation("Adding HTTP transport to MCP Server.");
28+
29+
WebApplicationBuilder webBuilder = WebApplication.CreateBuilder(args);
30+
IMcpServerBuilder mcpBuilder = AddMcpServices(webBuilder, serverOptions.Mode).WithHttpTransport();
4331

4432
if (serverOptions.Mode == McpMode.Both)
4533
{
46-
_ = mcpBuilder.WithHttpTransport().WithStdioServerTransport();
47-
}
48-
else if (serverOptions.Mode == McpMode.Stdio)
49-
{
34+
logger.LogInformation("Adding Stdio transport to MCP Server.");
5035
_ = mcpBuilder.WithStdioServerTransport();
5136
}
52-
else
53-
{
54-
_ = mcpBuilder.WithHttpTransport();
55-
}
5637

57-
_ = mcpBuilder.WithTools<OpenApiTools>();
38+
#if !DEBUG
39+
webBuilder.WebHost.ConfigureKestrel(options =>
40+
{
41+
options.ListenAnyIP(80);
42+
options.ListenAnyIP(8080);
43+
options.ListenAnyIP(8000);
44+
});
45+
#endif
5846

59-
await using WebApplication app = builder.Build();
47+
await using WebApplication app = webBuilder.Build();
6048

61-
if (serverOptions.Mode is McpMode.Http or McpMode.Both)
62-
{
63-
app.MapMcp();
64-
}
49+
app.MapMcp();
50+
app.MapHealthChecks("/healthz");
6551

6652
await app.RunAsync();
6753

6854
return;
55+
56+
static IMcpServerBuilder AddMcpServices(IHostApplicationBuilder builder, McpMode mode)
57+
{
58+
builder.Logging.AddConsole(consoleLogOptions =>
59+
{
60+
// Configure all logs to go to stderr
61+
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
62+
});
63+
64+
#if DEBUG
65+
builder
66+
.Services.AddOpenTelemetry()
67+
.WithTracing(b => b.AddSource("*").AddAspNetCoreInstrumentation().AddHttpClientInstrumentation())
68+
.WithMetrics(b => b.AddMeter("*").AddAspNetCoreInstrumentation().AddHttpClientInstrumentation())
69+
.WithLogging()
70+
.UseOtlpExporter();
71+
#endif
72+
73+
builder
74+
.Services.AddHttpClient<IOpenApiService, OpenApiService>()
75+
.ConfigurePrimaryHttpMessageHandler(() =>
76+
{
77+
return new HttpClientHandler
78+
{
79+
ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
80+
AllowAutoRedirect = true,
81+
};
82+
});
83+
84+
if (mode != McpMode.Stdio)
85+
{
86+
builder.Services.AddHealthChecks();
87+
}
88+
89+
IMcpServerBuilder mcpBuilder = builder
90+
.Services.AddMcpServer(mcp =>
91+
{
92+
mcp.ServerInfo = new Implementation { Name = "OpenAPI Toolkit MCP Server", Version = "1.0.0" };
93+
mcp.InitializationTimeout = TimeSpan.FromHours(1);
94+
mcp.ScopeRequests = true;
95+
})
96+
.WithTools<OpenApiTools>();
97+
98+
return mcpBuilder;
99+
}

0 commit comments

Comments
 (0)