In the rapidly evolving world of AI and machine learning, effective communication between models and applications is critical. The Model Context Protocol (MCP) is a standardized protocol designed to facilitate this communication by providing a structured way to exchange context and data between AI models and their clients. Whether you’re building AI-powered applications or integrating multiple models into a cohesive system, MCP ensures interoperability and scalability. For developers using tools like Visual Studio Code, you can now integrate and leverage MCP servers in your development flow and it makes it easy to build and test MCP servers on your local machine.
With the release of the MCP C# SDK, developers can now easily build both servers and clients that leverage this protocol. This SDK simplifies the implementation process, allowing you to focus on your application’s unique features rather than the complexities of protocol handling. Additionally, the SDK includes support for consuming MCP servers, enabling developers to create robust client applications that interact seamlessly with MCP servers.
In this blog post, we’ll explore how you can use the C# SDK to create your own MCP server and client applications.
Note
The MCP C# SDK is in preview and APIs may change. We will continuously update this blog as the SDK evolves.Getting Started Building an MCP server
The MCP C# SDK is distributed as NuGet packages that you can integrate into a simple console application. Let’s start out by creating our very first MCP server by creating a new console app:
dotnet new console -n MyFirstMCP
Now, let’s add a few basic NuGet packages for the MCP C# SDK and to host our server with Microsoft.Extensions.Hosting
.
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting
The ModelContextProtocol
package gives access to new APIs to create clients that connect to MCP servers, creation of MCP servers, and AI helper libraries to integrate with LLMs through Microsoft.Extensions.AI
.
Starting up our server
Let’s update our Program.cs
with some basic scaffolding to create our MCP server, configure standard server transport, and tell our server to search for Tools (or available APIs) from the running assembly.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
With this code we are now ready to build our first tool that we want to expose to our MCP server.
Defining our first tool
Let’s create the most basic tool to just repeat back what we ask it to do. We first define our class that will contain functions that are exposed as tools.
[McpServerToolType]
public static class EchoTool
{
[McpServerTool, Description("Echoes the message back to the client.")]
public static string Echo(string message) => $"Hello from C#: {message}";
[McpServerTool, Description("Echoes in reverse the message sent by the client.")]
public static string ReverseEcho(string message) => new string(message.Reverse().ToArray());
}
In our startup code, the WithToolsFromAssembly
will scan the assembly for classes with the McpServerToolType
attribute and register all methods with the McpServerTool
attribute. Notice that the McpServerTool
has a Description which will be fed into any client connecting to the server. This description helps the client determine which tool to call.
Configure and run in VS Code
With this minimal code, our MCP server is ready for testing! If you haven’t tried out MCP support in VS Code, check out this video for a guided tour. To run our project locally, we just need to add a new server in our mcp.json file in your .vscode folder or your user settings:
{
"inputs": [],
"servers": {
"MyFirstMCP": {
"type": "stdio",
"command": "dotnet",
"args": [
"run",
"--project",
"D:\\source\\MyFirstMCP\\MyFirstMCP\\MyFirstMCP.csproj"
]
}
}
}
When we go into GitHub Copilot and toggle on Agent mode, we will see our new tool configured:
Opening GitHub Copilot’s Agent mode we can now ask it to reverse a message for us. We will be prompted for permission to execute the call to the tool:
Selecting Continue will run the tool and pass the message to our MCP server to execute:
Integrating with our own data and APIs
MCP servers show their power when they integrate into an existing API or service to query real data that can be used by the clients. There is a growing list of servers available to use in clients including ones I use every day such as Git, GitHub, Playwright, and the Filesystem. So, let’s extend our MCP server to connect with an API, take query parameters, and respond back to data. If you have followed me at all, you know I love making demos about monkeys and I thought having a Monkey MCP server available to me at all times would be useful. So, the first thing I did was integrate a simple service that queries my monkey database to return a list of monkeys or information about a specific kind:
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MyFirstMCP;
public class MonkeyService
{
private readonly HttpClient httpClient;
public MonkeyService(IHttpClientFactory httpClientFactory)
{
httpClient = httpClientFactory.CreateClient();
}
List<Monkey> monkeyList = new();
public async Task<List<Monkey>> GetMonkeys()
{
if (monkeyList?.Count > 0)
return monkeyList;
var response = await httpClient.GetAsync("https://www.montemagno.com/monkeys.json");
if (response.IsSuccessStatusCode)
{
monkeyList = await response.Content.ReadFromJsonAsync(MonkeyContext.Default.ListMonkey) ?? [];
}
monkeyList ??= [];
return monkeyList;
}
public async Task<Monkey?> GetMonkey(string name)
{
var monkeys = await GetMonkeys();
return monkeys.FirstOrDefault(m => m.Name?.Equals(name, StringComparison.OrdinalIgnoreCase) == true);
}
}
public partial class Monkey
{
public string? Name { get; set; }
public string? Location { get; set; }
public string? Details { get; set; }
public string? Image { get; set; }
public int Population { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
}
[JsonSerializable(typeof(List<Monkey>))]
internal sealed partial class MonkeyContext : JsonSerializerContext {
}
I can then register this with the built in .NET dependency service so I can use it later:
builder.Services.AddHttpClient();
builder.Services.AddSingleton<MonkeyService>();
This service could call your existing APIs, query a database, process data, or anything else. MCP servers are often configured to take in access tokens or additional parameters on startup so your code has the necessary information it needs to call services. In this case I am just reading from my sample monkey data. To make these available as tools, all I need to do is to define a new McpServerToolType
and setup a few new McpServerTool
methods that call into this service:
using System;
using System.ComponentModel;
using System.Text.Json;
using ModelContextProtocol.server;
namespace MyFirstMCP;
[McpServerToolType]
public static class MonkeyTools
{
[McpServerTool, Description("Get a list of monkeys.")]
public static async Task<string> GetMonkeys(MonkeyService monkeyService)
{
var monkeys = await monkeyService.GetMonkeys();
return JsonSerializer.Serialize(monkeys);
}
[McpServerTool, Description("Get a monkey by name.")]
public static async Task<string> GetMonkey(MonkeyService monkeyService, [Description("The name of the monkey to get details for")] string name)
{
var monkey = await monkeyService.GetMonkey(name);
return JsonSerializer.Serialize(monkey);
}
}
In the above code, I am simply returning the data as JSON, however you can format it in different ways for the LLM to process.
We can now restart our MCP server inside of VS Code and start to try it out! The power is when combining the power of GitHub Copilot and the models it calls to transform the data and apply it to software development. For example I may ask it for a list of monkeys and display it as table.
Or we could ask for information on a specific monkey and to generate a Mermaid diagram for the data type:
Better yet, if I was building a .NET MAUI app, I could have it generate the XAML and the code needed for the data I was exploring:
Publish your MCP server
.NET makes it simple to easily create container images for any .NET app. All that needs to be done is add the necessary configuration into the project file:
<PropertyGroup>
<EnableSdkContainerSupport>true</EnableSdkContainerSupport>
<ContainerRepository>jamesmontemagno/monkeymcp</ContainerRepository>
<ContainerFamily>alpine</ContainerFamily>
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
</PropertyGroup>
Here, we are going to use the alpine container family to create a nice small image and also specify multiple runtime identifiers so users have an optimized image regardless if they are on x64 or an arm64 based machine. The .NET SDK has the ability to create the images by running the dotnet publish /t:PublishContainer
command. Since we have multiple runtime identifiers specified two different images locally will be created. If we want to take these images and upload them, we are able to do it all from the CLI by passing in the specific container register to push to:
dotnet publish /t:PublishContainer -p ContainerRegistry=docker.io
Now a combined image will be sent to docker.io in this case, and then can be configured in VS Code or other clients that work with MCP servers.
{
"inputs": [],
"servers": {
"monkeymcp": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"jamesmontemagno/monkeymcp"
],
"env": {}
}
}
}
The great part about this, is that automatically the correct image will be pulled based on the machine type the user is on. Learn more about this in the containerizing .NET apps documentation.
Server-Sent Events (SSE)
SSE transport enables server-to-client streaming with HTTP POST requests for client-to-server communication. With the MCP C# SDK, implementing SSE in your MCP server is straightforward. The SDK supports configuring server transports to handle streaming data efficiently to connected clients. You can see an implementation of an SSE MonkeyMCP server on GitHub and on the MCP C# SDK samples. You can go a step further and look at remote MCP servers with the new Azure Functions support.
Go even further with MCP
From here you can continue to build new functionality into your MCP server for your company, community, and your services today that can be used in GitHub Copilot or other clients. The MCP C# SDK has great samples of creating MCP servers, clients, and advanced tutorials showing the power of MCP and how easily you can build them with C#.
Any chance the team can add a “Share via Bluesky
” along with LinkedIn and X 
.
I really like the article written by you. I have few more questions and thought you may be able to provide more clarity.
1) At the end you are publishing as docker container. Generally in real world how will it be published? Like an Asp.Net api? If yes, what needs to be done from code perspective and what will the right service to host it? Or will it be like any other Asp.Net service?
2) I noticed in tool MonkeyService is injected at method level but not at constructor, any specific reason?
3) I am not able to debug the...
Love all these questions!!! Thanks Hemant!
1. Docker is one valid option, could global tool it. I added a section and code sample about Server-Sent Events which you could then publish and uses ASP.NET Core under the hood and you could host it. Also added a link to Azure Functions support, which is rad
2. In this case it is all static as it is scanning the assembly, but you could just register using `WithTool` and then inject in constructor like this: https://github.com/jamesmontemagno/MonkeyMCP/pull/2
3. So, a few ways. Unit tests, add debugging and attach to the process that MCPInspector or copilot...
Bro this is awesome! So glad the team is working this. Can finally build self contained, standalone MCP servers and say goodbye to python/Nodejs dependency hell.
I am just now learning about "remote" MCP servers. Do you know of possible examples of this yet or any coming?
I guess this called "SSE" and I do NOT use Azure. Just local infrastructure
Everyone, I highly recommend checking out the Microsoft.Extensions.AI. Their samples are minimal but I've shared an --intentionally minimal-- CLI chat app with "tools" for directory listing, file reading and writing for kickstart learning from
https://github.com/peakflames/maxbot
Found it I think
https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples%2FAspNetCoreSseServer
I added a section about SSE and also I have a version of the monkeymcp with SSE
Great tuto I will try it !
Does it possible to debug,to put a break point ?
A few options.
If using stdio then a simple unit test is a way to go. It is just calling methods. Or you “should” be abl eto debug from VS code by attaching to the server process launched by VS Code or if using MCP Inspector.
Also you could use HTTP transport: https://github.com/halter73/console-to-http-mcp-server/pull/1 as an option and just test the endpoints.
There seems to be a small typo in WithStdioserverTransport → WithStdioServerTransport.
Thanks! Fixed it up