This post shows how to build a single MCP server that supports both transports, selected at runtime with a "--http" switch, using the .NET builder pattern.
Let's think about a situation for using MCP servers. Most MCP servers run on a local machine – either directly or in a container. But with other integration scenarios like using Copilot Studio, enterprise-wide MCP servers or need more secure environments, those MCP server should run remotely through HTTP.
As long as the core logic lives in a shared layer, wrapping it in a console (STDIO) or web (HTTP) host is straightforward. However, maintaining two hosts can duplicate code. What if a single MCP server supports both STDIO and HTTP, controlled by a simple switch? It will be reducing significant amount of management overhead.
This post shows how to build a single MCP server that supports both transports, selected at runtime with a --http
switch, using the .NET builder pattern.
.NET Builder Pattern
A .NET console app starts the builder pattern using Host.CreateApplicationBuilder(args)
.
var builder = Host.CreateApplicationBuilder(args);
The builder instance is the type of HostApplicationBuilder
implementing the IHostApplicationBuilder
interface. On the other hand, an ASP.NET web app starts the builder pattern using WebApplication.CreateBuilder(args)
.
var builder = WebApplication.CreateBuilder(args);
This builder
instance is the type of WebApplicationBuilder
also implementing the IHostApplicationBuilder
interface. Now, both builder
instances have IHostApplicationBuilder
in common, and this is the key of this post today.
If we decide the hosting mode before creating the builder instance, the server can run as either STDIO or HTTP.
The --http
Switch as an Argument
As you can see, both Host.CreateApplicationBuilder(args)
and WebApplication.CreateBuilder(args)
take the list of arguments that are passed from the command-line. Therefore, before initializing the builder instance, we can identify the server type. Let's use a --http
switch as the selector. Then pass --http
when running the server.
dotnet run --project MyMcpServer -- --http
Then, before creating the builder
instance, check whether the switch is present. It looks for the environment variables first, then checks the arguments passed.
public static bool UseStreamableHttp(IDictionary env, string[] args)
{
var useHttp = env.Contains("UseHttp") &&
bool.TryParse(env["UseHttp"]?.ToString()?.ToLowerInvariant(), out var result) && result;
if (args.Length == 0)
{
return useHttp;
}
useHttp = args.Contains("--http", StringComparer.InvariantCultureIgnoreCase);
return useHttp;
}
Here's the usage:
var useStreamableHttp = UseStreamableHttp(Environment.GetEnvironmentVariables(), args);
We've identified whether to use HTTP or not. Therefore, the builder
instance is built in this way:
IHostApplicationBuilder builder = useStreamableHttp
? WebApplication.CreateBuilder(args)
: Host.CreateApplicationBuilder(args);
With this builder
instance, we can add more dependencies specific to web app or console app depending on the scenario.
The Transport Type
Let's add the MCP server to the builder
instance.
var mcpServerBuilder = builder.Services.AddMcpServer()
.WithPromptsFromAssembly()
.WithResourcesFromAssembly()
.WithToolsFromAssembly();
We haven’t told mcpServerBuilder
which transport to use yet. Use useStreamableHttp
to select the transport.
if (useStreamableHttp)
{
mcpServerBuilder.WithHttpTransport(o => o.Stateless = true);
}
else
{
mcpServerBuilder.WithStdioServerTransport();
}
Type Casting to Run Server
While configuring an ASP.NET web app, middlewares are added. The HTTP host also needs middleware, and the builder must be cast. After the builder
instance is built, the webApp
instance adds middleware including the endpoint mapping.
IHost app;
if (useStreamableHttp)
{
var webApp = (builder as WebApplicationBuilder)!.Build();
webApp.UseHttpsRedirection();
webApp.MapMcp("/mcp");
app = webApp;
}
else
{
var consoleApp = (builder as HostApplicationBuilder)!.Build();
app = consoleApp;
}
Note that WebApplication implements IHost, so you can assign it to an IHost variable. The console host built from HostApplicationBuilder is already an IHost.
Use this app
instance to run the MCP server.
await app.RunAsync();
That's it! Now you can run the MCP server with the STDIO transport or the HTTP transport by providing a single switch, --http
.
Sample apps
Sample apps are available for you to check out. Visit the MCP Samples in .NET repository, and you'll find MCP server apps. All server apps in the repo support both STDIO and HTTP via the switch.
More resources
If you'd like to learn more about MCP in .NET, here are some additional resources worth exploring: