
Use themcp-server-hybrid template to be able to easily switch betweenstdio andsse transports.
Source code:https://github.com/NikiforovAll/mcp-template-dotnet
| Package | Version |
|---|---|
Nall.ModelContextProtocol.Template | |
Nall.ModelContextProtocol.Inspector.Aspire.Hosting |
Previously, I’ve shared with you the blog post -Simplifying Model Context Protocol (MCP) Server Development with Aspire. In this post we explored two ways to run the MCP server using Aspire.
🎯 In reality, depending on the context, you may want to run the MCP server in different ways. For example, you may want to run the MCP server insse mode for debugging/development purposes, but instdio mode for production.
In this post, I will show you how to use a simple template to create an MCP server that can be run in both modes.
📦 As in my previous post, let’s installNall.ModelContextProtocol.Aspire.Templatepackage:
dotnet newinstallNall.ModelContextProtocol.Template# These templates matched your input: 'mcp'# Template Name Short Name Language Tags# ----------------- ----------------- -------- -------------# MCP Server mcp-server [C#] dotnet/ai/mcp# MCP Server SSE mcp-server-sse [C#] dotnet/ai/mcp# MCP Server Hybrid mcp-server-hybrid [C#] dotnet/ai/mcp➕Create anmcp-server-hybrid project:
dotnet new mcp-server-hybrid-o MyAwesomeMCPServer-n MyAwesomeMCPServerNow we can run it in two different modes:
InSSE mode:
dotnet run# info: Microsoft.Hosting.Lifetime[14]# Now listening on: http://localhost:3001# info: Microsoft.Hosting.Lifetime[0]# Application started. Press Ctrl+C to shut down.# info: Microsoft.Hosting.Lifetime[0]# Hosting environment: Development# info: Microsoft.Hosting.Lifetime[0]# Content root path: ${HOME}/MyAwesomeMCPServer# info: Microsoft.Hosting.Lifetime[0]Start theMCP Inpsector and configure it to listen on the default address: “http://localhost:3001/sse”.
npx @modelcontextprotocol/inspectorInStdio mode:
npx @modelcontextprotocol/inspector dotnet run-v q----stdio# ⚙️ Proxy server listening on port 6277# 🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀# New SSE connection# Query parameters: {# transportType: 'stdio',# command: 'dotnet',# args: 'run -v q --stdio'# }# Stdio transport: command=C:\Program Files\dotnet\dotnet.exe, args=run,-v,q,--stdioThe benefit of theSSE mode is that you can run the MCP server with a debugger attached and/or see the logs directly. TheStdio mode is slightly more complex, as it relies on theMCP Client (e.g.,MCP Inspector) to start the server, and it disables logging on the MCP server to maintain compatibility with theMCP Client.
On the other hand, theStdio Server’s lifetime is managed by theMCP Client. This makes it much easier to consume MCP servers in this mode because you typically don’t have to worry about the server’s lifetime. It is started by theMCP Client and stopped when theMCP Client is stopped.
Before you start developing your own MCPs using this template, let’s take a look at the code generated by the template. Here is a content ofProgram.cs:
varbuilder=WebApplication.CreateBuilder(args);builder.WithMcpServer(args).WithToolsFromAssembly();varapp=builder.Build();app.MapMcpServer(args);app.Run();[McpServerToolType]publicstaticclassEchoTool{[McpServerTool,Description("Echoes the message back to the client.")]publicstaticstringEcho(stringmessage)=>$"hello{message}";}💡 All “magic” happens in theMcpServerExtensions.cs class. In the code below, we check if the--stdio argument is present. If it is, we configure the server to use theStdio transport. Otherwise, we use theSSE transport. You don’t need to worry about how to switch between the two modes. The template does it for you.
publicstaticIMcpServerBuilderWithMcpServer(thisWebApplicationBuilderbuilder,string[]args){varisStdio=args.Contains("--stdio");if(isStdio){builder.WebHost.UseUrls("http://*:0");// random port// logs from stderr are shown in the inspectorbuilder.Services.AddLogging(builder=>builder.AddConsole(consoleBuilder=>{consoleBuilder.LogToStandardErrorThreshold=LogLevel.Trace;consoleBuilder.FormatterName="json";}).AddFilter(null,LogLevel.Warning));}varmcpBuilder=isStdio?builder.Services.AddMcpServer().WithStdioServerTransport():builder.Services.AddMcpServer();returnmcpBuilder;}publicstaticWebApplicationMapMcpServer(thisWebApplicationapp,string[]args){varisSse=!args.Contains("--stdio");if(isSse){app.MapMcp();}returnapp;}Down below I demonstrate how to run the MCP server using theAspire hosting integration in two different modes simultaneously.
➕ Create AppHost:
dotnet new aspire-apphost-n AppHost-o AppHost📦 InstallNall.ModelContextProtocol.Inspector.Aspire.Hostingpackage:
dotnet add ./Apphost package Nall.ModelContextProtocol.Inspector.Aspire.Hosting🔗 Add project reference to AppHost:
dotnet add ./AppHost/AppHost.csproj reference ./MyAwesomeMCPServer/MyAwesomeMCPServer.csprojAdd the following code toProgram.cs of the AppHost:
varbuilder=DistributedApplication.CreateBuilder(args);varsse=builder.AddProject<Projects.MyAwesomeMCPServer>("server");builder.AddMCPInspector("mcp-sse",serverPort:9000,clientPort:8080).WithSSE(sse);builder.AddMCPInspector("mcp-stdio").WithStdio<Projects.MyAwesomeMCPServer>();builder.Build().Run();Here is how the Aspire Dashboard looks like:

And it works like a charm!
🙌 I hope you found it helpful. If you have any questions, please feel free to reach out. If you’d like to support my work, a star on GitHub would be greatly appreciated! 🙏
Jibber-jabbering about programming and IT.