Building MCP Servers in .NET 10: A Practical Guide (STDIO + HTTP)

May 18, 2026

Why MCP servers? 

LLMs are powerful—but they’re limited to what they can ‘see’. The Model Context Protocol (MCP) is an open protocol that standardizes how apps expose tools, resources, and prompts to AI clients so models can interact with real systems in a structured, discoverable way. 

For .NET developers, this is especially useful because you can build MCP servers in C# using the official MCP C# SDK and run servers locally over stdio or remotely over HTTP. 

MCP mental model (fast) 

Host: The application that contains the AI experience (IDE/agent tool). 

Client: The MCP-capable component inside the host that connects to servers. 

Server: Your service that exposes tools/resources/prompts. 

Choosing a transport: STDIO vs Streamable HTTP 

STDIO (local): The client launches your server as a subprocess and communicates via stdin/stdout. Messages are newline-delimited JSON-RPC, and stdout must contain only protocol messages (logs must go to stderr). 

Streamable HTTP (remote/scalable): Runs as an independent server reachable over HTTP. Validate the Origin header to reduce DNS rebinding risk and bind to localhost for local runs. 

Part 1 — The fastest way: .NET 10 MCP Server Project Template 

Microsoft provides a quickstart showing how to create a minimal MCP server using the .NET 10 SDK and the Microsoft.McpServer.ProjectTemplates template package. 

This path is great for getting a working server quickly with correct wiring and sane defaults. 

dotnet new install Microsoft.McpServer.ProjectTemplates

Part 2 — Build a Minimal STDIO MCP Server (from scratch) 

STDIO is ideal when your MCP server needs access to local machine resources and you want the simplest deployment path. 

Below is a minimal server that uses Microsoft.Extensions.Hosting and exposes one tool (Echo). 

 

Step A — Create project & add packages

dotnet new console -n MyMcpServer
cd MyMcpServer
dotnet add package ModelContextProtocol
dotnet add package Microsoft.Extensions.Hosting

Note: Microsoft’s MCP server walkthrough shows the SDK approach using Microsoft.Extensions.Hosting and MCP server registration in the builder

Step B — Program.cs (STDIO server + tool discovery)

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using System.ComponentModel;

var builder = Host.CreateApplicationBuilder(args);

// IMPORTANT: STDIO servers must keep stdout clean.
// Route logs to stderr so they don't corrupt JSON-RPC output.
builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace);

builder.Services
    .AddMcpServer()
    .WithStdioServerTransport()
    .WithToolsFromAssembly();

await builder.Build().RunAsync();

[McpServerToolType]
public static class EchoTools
{
    [McpServerTool, Description("Echoes the message back to the client.")]
    public static string Echo([Description("Message to echo")] string message)
        => $"Hello from MCP (.NET 10): {message}";
}

Why the stderr logging rule matters
The MCP transport spec explicitly requires that in stdio, servers must not write non-protocol output to stdout; logs should go to stderr

 

Part 3 — Build a Remote MCP Server over Streamable HTTP (ASP.NET Core) 

If you want a centrally hosted MCP server (team-wide tooling, enterprise integrations), use HTTP transport. MCP’s spec notes Streamable HTTP is the standard remote transport and includes security requirements like Origin validation. [modelconte...rotocol.io], [dometrain.com]Below is a minimal HTTP server exposing a demo Weather tool. 


Step A — Create ASP.NET Core app & add MCP server support
 

dotnet new web -n MyHttpMcpServer
cd MyHttpMcpServer
dotnet add package ModelContextProtocol.AspNetCore

Step B — Program.cs (HTTP MCP endpoint)

using ModelContextProtocol.Server;
using System.ComponentModel;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddMcpServer()
    .WithHttpTransport(options =>
    {
        // Stateless mode is commonly recommended for simple remote servers
        // that don't need advanced server->client features.
        options.Stateless = true;
    })
    .WithToolsFromAssembly();

var app = builder.Build();

// Exposes /mcp endpoint (or the configured MCP endpoint)
app.MapMcp();

app.Run("http://localhost:3001");

[McpServerToolType]
public static class WeatherTools
{
    [McpServerTool, Description("Returns a sample weather status for a city.")]
    public static string GetWeather(string city)
        => $"Weather for {city}: Sunny (demo)";
}

Security note (important): For Streamable HTTP, MCP recommends validating the Origin header to prevent DNS rebinding and binding locally to localhost when running locally

 

Part 4 — Coding standards & best practices for MCP servers 

  1. STDIO rule: stdout must be pure JSON-RPC
    Never log to stdout. Use stderr. 
    Standard: Configure logging to stderr as shown in the STDIO sample.
  2. Keep tools small + deterministic
    Tool methods should be short, validate input, and return structured outputs.
    Avoid tools that do “too much” (hard to reason about / secure).
  3. Validate inputs like public APIs
    Even though an LLM is “calling” the tool, treat it like an untrusted client:
    Validate required fields
    Constrain sizes
    Apply allowlists where possible
  4. Prefer “read-only” tools first
    Start with:
    1. search/read/query tools
    2. Then move to “write” tools with extra safety checks.
  5. Remote servers must follow transport security guidance
    Streamable HTTP transport includes security requirements like Origin validation to mitigate DNS rebinding risks

Conclusion 

.NET 10 makes it practical to build MCP servers using local STDIO transport for quick, secure local tooling, and Streamable HTTP for scalable, shared integrations. 

Start with a small set of safe tools, add observability and security early, and expand capabilities over time. 

OpenAI Assistants Bot Using RAG
Feb 19, 2026

Introduction In modern AI applications, Retrieval-Augmented Generation (RAG) is one of the most powerful patterns for building intelligent assistants that go beyond generic responses. Instead of relying only on pre-trained knowledge, RAG allows your assistant to retrieve real data (files, database, CSV, APIs) and generate accurate, context-aware answers. In this blog, we will walk through how to build an OpenAI Assistants Bot using RAG, step by step.   What is RAG? RAG (Retrieval-Augmented Generation) is a technique where: Data is stored (files, DB, vector store) User asks a question Relevant data is retrieved AI generates response based on retrieved data Instead of hallucinating, AI gives real, data-backed answers Step 1 — Register on OpenAI Platform Open: https://platform.openai.com/ Important Notes: Create an account using your email If using for organization: Ask admin to add you to organization OpenAI provides: Personal account Organization account   Step 2 — Generate API Key Go to: Settings → API Keys Important: You need to manage two types of keys properly Type Usage Personal API Key For personal development Organization API Key For production / team usage Best Practice Never hardcode API keys Store in: .env file or Azure Key Vault `` Step 3 — Open Playground & Assistant Go to: OpenAI Dashboard → Playground → Assistants First time? It will prompt: Create a new Assistant OpenAI auto-generates: A random name You can rename it (recommended) What is an Assistant? An Assistant is: A configured AI agent With instructions With connected tools With uploaded data (RAG-ready) It acts as a "brain" that connects: Instructions Files APIs Conversations Step 4 — Add Data (Core of RAG) Example You are an AI assistant for an e-commerce store. Answer user questions based only on the provided data. Do not generate fake information. This is very important for RAG behavior. Now comes the most important part — giving data to your assistant. Supported Data Types PDF CSV TXT DOCX JSON Example: E-Commerce Data (CSV) If you have an e-commerce website, create CSV like: ProductId,Name,Category,Price,Stock 1,iPhone 15,Mobile,999,20 2,Samsung S23,Mobile,899,15 3,MacBook Pro,Laptop,1999,10 `` Upload Process Go to Assistant Open Files section Upload CSV / document Attach file to assistant How RAG Works in Assistants https://via.placeholder.com/1200x500?text=Assistant+RAG+Flow Flow: User asks:  “What is price of iPhone 15?” Assistant: Searches uploaded data Retrieves relevant content AI generates answer: “The price of iPhone 15 is $999” Step 5 — Call Assistant via API Example (C# style for you ): var client = new OpenAIClient("API_KEY"); var thread = await client.CreateThreadAsync(); await client.AddMessageAsync(thread.Id, "What is the price of iPhone 15?"); var response = await client.RunAssistantAsync(thread.Id, assistantId); Console.WriteLine(response); Conclusion OpenAI Assistants + RAG is one of the most powerful ways to build: Intelligent Context-aware Production-ready AI applications Instead of building complex pipelines, you can now: Upload data Configure assistant Start querying

Exploring the Latest Features in C# 10
Jun 19, 2025

Exploring the Latest Features in C# 10 C# 10, the latest version of the C# programming language, brings exciting new features and enhancements that aim to improve developer productivity, code readability, and overall language capabilities. In this blog post, we'll take a closer look at some of the notable features introduced in C# 10. 1. Record Types Improvements Record types, introduced in C# 9, have proven to be a valuable addition for simplifying immutable data structures. In C# 10, record types receive enhancements that make them even more powerful. Example:   csharpCopy code public record Person { public string FirstName { get; init; } public string LastName { get; init; } } // C# 10 allows you to simplify the instantiation of record types var person = new Person { FirstName = "John", LastName = "Doe" }; In C# 10, you can use the with expression to create a copy of a record with modified values more concisely:   csharpCopy code var updatedPerson = person with { FirstName = "Jane" }; This syntax improves the readability of code when updating record instances. 2. Parameter Null Checking C# 10 introduces the notnull modifier for parameters, enabling developers to enforce non-null arguments at the call site. This can lead to more robust code by catching potential null reference exceptions early. Example: public void ProcessData(notnull string data) { // The 'data' parameter is guaranteed to be non-null within this method } The notnull modifier serves as a contract, making it clear that the method does not accept null arguments. 3. Global Usings C# 10 simplifies the process of importing namespaces by introducing global usings. Now, you can include common namespaces globally, reducing the need to include them in each file. Example: // C# 10 global using global using System; global using System.Collections.Generic; class Program { static void Main() { List<string> myList = new(); // ... } } By using global usings, you can make your code more concise and improve overall code readability. 4. File-scoped Namespace Declarations C# 10 introduces file-scoped namespace declarations, allowing you to define namespaces directly at the file level. This simplifies the organization of code and reduces the need for excessive indentation. Example:   csharpCopy code // File-scoped namespace declaration namespace MyNamespace; class MyClass { // ... } This feature promotes cleaner code structure, making it easier to navigate and maintain. 5. Extended Property Patterns C# 10 enhances property patterns, enabling more expressive and concise matching when working with switch statements and patterns. Example:   csharpCopy code public class Point { public int X { get; set; } public int Y { get; set; } } var point = new Point { X = 5, Y = 10 }; // C# 10 extended property patterns var result = point switch { { X: 0, Y: 0 } => "Origin", { X: var x, Y: var y } when x == y => "Diagonal", _ => "Unknown" }; These improvements make pattern matching even more powerful and expressive. Conclusion C# 10 introduces several features and enhancements that aim to make the language more expressive, concise, and developer-friendly. By leveraging these new capabilities, developers can write cleaner, more maintainable code. As always, staying updated with the latest language features empowers developers to make the most of the tools at their disposal. Explore these features in your projects and embrace the evolution of C# for a more enjoyable and efficient development experience.

Time to Migrate from .NET6.0 to .NET8.0
Oct 31, 2024

As .NET 6.0 approaches its end-of-support date, it’s time for developers and businesses to consider upgrading to stay on the cutting edge and ensure that applications remain secure, efficient, and compliant. With the upcoming .NET 8 offering new features and performance boosts, an upgrade promises more than just continued support—it’s an opportunity to leverage a new generation of improvements. In this guide, we’ll walk you through the upgrade process, highlighting the critical changes you’ll need to make to ensure a smooth transition from .NET 6.0. Why Does upgrade is needed from .Net6.0? End of Support and Security Risks .NET 6.0, as a Long-Term Support (LTS) version, provided stability and support, but its support period is ending. Staying on an unsupported version exposes applications to security vulnerabilities and compliance risks, making an upgrade essential for data protection and system integrity. Access to Enhanced Features and Improvements Newer .NET versions bring valuable features like better performance optimizations, productivity tools, and cloud-native support. Upgrading unlocks these advancements, helping applications run faster and utilize fewer resources. Improved Developer Productivity .NET 7 and .NET 8 include new language features, such as enhanced pattern matching and streamlined lambda expressions in C#, which simplify code and reduce boilerplate. These additions help developers write code more quickly and with fewer errors. Reduced Operational Costs Performance improvements in .NET 7 and .NET 8 mean that applications can often perform better with fewer resources. This reduction in memory and CPU usage can translate to lower costs, especially for cloud-hosted applications. Compatibility with Modern Tools and Libraries Many popular third-party libraries and tools adopt newer .NET versions quickly, ensuring compatibility and stability. Staying up-to-date with .NET versions helps maintain compatibility with these tools, avoiding potential issues with outdated dependencies. Preparing for Future Technology As Microsoft continues to evolve .NET, each version brings it closer to the needs of modern workloads, such as machine learning, cloud computing, and IoT. Upgrading ensures that applications remain ready for future tech innovations and integrate easily with emerging solutions. These improvements make upgrading to .NET 8 essential for any business looking to stay competitive and secure in today’s fast-paced digital world.   Action Plan for .NET 6 Users: Steps to Upgrade to .NET 8 Upgrading from .NET 6 to .NET 8 requires a structured approach to ensure application stability and compatibility. Follow this action plan to make your migration process smooth and efficient. 1. Prioritize Upgrading to .NET 8 Since .NET 8 is the newest LTS release, it’s vital to prioritize upgrading business-critical applications first. Applications managing sensitive data, high user traffic, or core operations will benefit most from .NET 8’s security patches and performance improvements. 2. Evaluate Your Applications for Compatibility Conduct a thorough audit of your applications to identify any potential compatibility issues. Here’s what to focus on: Dependency Audit: Ensure that third-party libraries, plugins, and tools are compatible with .NET 8. Update or replace any that are not supported. Database and ORM Compatibility: If you’re using ORMs like Entity Framework, verify their compatibility with .NET 8 and prepare for any required schema migrations. Code Review for Deprecated APIs: Review your code for deprecated APIs and replace or refactor any incompatible code. Microsoft’s .NET 8 upgrade documentation highlights specific API changes and deprecations to consider. 3. Use the .NET Upgrade Assistant Tool Microsoft’s .NET Upgrade Assistant is designed to streamline the migration process, especially for complex or large projects. It helps automate and identify critical changes needed for a successful upgrade: Flags deprecated APIs and unsupported code. Automates refactoring for .NET 8 compatibility. Provides guidance on manual changes necessary to complete the transition. This tool saves significant time, particularly for enterprise applications with complex dependencies and workflows. 4. Thoroughly Test Your Upgraded Application Testing is essential to ensure that your application performs correctly after the upgrade. Here’s a recommended testing approach: Automated Unit Testing: Run unit tests on your upgraded code to verify core functionality. Add or update tests to cover modified areas. Integration Testing: Validate that services or components work correctly together in .NET 8. Load and Performance Testing: Test under real-world conditions to validate .NET 8’s performance benefits and catch any potential bottlenecks. User Acceptance Testing (UAT): Conduct UAT to confirm that your end-users experience expected functionality, especially if significant code or API changes have been made.   Common Troubleshooting for .NET 8 Migration Here are some common issues developers encounter during migration and how to resolve them. 1. Continuous Integration Challenges with Azure DevOps Upgrading applications within CI/CD environments like Azure DevOps can present challenges. Here’s how to address some common issues: Pipeline Configuration: Update Azure DevOps pipelines to reference .NET 8 SDKs. Modify YAML files or pipeline configurations to install .NET 8 before the build, ensuring dependencies are correctly aligned. NuGet Feed Compatibility: Verify that NuGet feeds are compatible with .NET 8 packages. Update outdated or incompatible packages to avoid build errors. Validate CI Tests: Re-run automated tests in Azure DevOps pipelines to catch compatibility issues before deploying to production. 2. Problem Management on Azure App Service Deploying upgraded applications on Azure App Service may lead to configuration or compatibility issues. Here’s how to manage these effectively: Set Runtime Stack to .NET 8: In the Azure Portal, navigate to App Service Settings > Configuration > Stack Settings and select .NET 8 as the runtime stack. Diagnostic Log Analysis: Check the App Service diagnostic logs for code incompatibility, runtime errors, or dependency conflicts. Enable detailed error messages to troubleshoot effectively. Leverage Azure Application Insights: Use Application Insights to monitor performance, track errors, and get real-time insights into application health, which helps identify optimization areas post-upgrade.   Need Help with Your Upgrade? Connect with Magnusminds If you need assistance with your migration or encounter complex issues, Magnusminds offers expert support for .NET upgrades. out team provides tailored solutions for migration, implementation of new features, and troubleshooting specific to your application needs.  

Kishan Parmar

About the Author

Kishan Parmar

Team Leader at MagnusMinds IT Solution
Team Leader with a demonstrated history of working in the computer software industry. Skilled in Asp.net MVC 4.0, C#, WPF Development, Terraform, Infrastructure as a code, AWS, Azure, IONIC, Node JS, Asp. Net Core, Web API MVC, .NET Core Web API, Application Programming Interfaces, and Raspberry Pi.
Strong engineering professional with a Bachelor of Engineering (B.E.) focused on Computer Engineering.