Build Your Own AI Model Context Server with C# in 10 Minutes
Model Context Protocol (MCP) servers are a powerful tool for developers. This publication has previously explored how to integrate them into code editors like VS Code with GitHub Copilot. The key advantage of MCP servers is the standardized protocol they use to provide additional context to AI models through various clients, such as Copilot.
This standardization means you can integrate numerous tools. For instance, with Playwright integration, you can use natural language to test different parts of a website you're building, and it will perform the actions for you. Similarly, with GitHub integration, you can provide context to have it update issues, pull information, or search for pull requests on any repository, even ones you aren't actively working in. These actions—creating issues, managing pull requests, and more—can be performed seamlessly through natural language commands, provided you grant permission.
While using existing MCP servers is beneficial, what about building your own? The beauty of MCP is that its standardized protocol allows you to create custom servers. You can expose any API or custom logic through an MCP server, which can then be deployed to Docker, a remote server, or run locally. You can then distribute it for anyone to use.
This article will guide you through building a real MCP server from scratch using C# and Visual Studio Code.
Getting Started with the C# SDK
Before diving in, it's helpful to review the official Microsoft for Developers blog, which has posts explaining the C# SDK for MCP, what it is, and why you might want to use it. This will lead you to the official C# SDK, which is available as a NuGet package. For those interested in deployment, the Azure Functions team has also released a method for building and deploying MCP servers using server-sent events (SSE).
Project Setup in VS Code
Let's begin by setting up the project.
- Open VS Code and create a new C# Console App.
- Name the project something like
FancyMCP
.
This creates a standard console application. Next, we need to install a few NuGet packages.
Note: The MCP support package is a pre-release version, so you'll need to enable pre-release packages in your NuGet settings.
Install the following packages:
* Microsoft.ModelStudio.ModelContextProtocol.Server
* Microsoft.Extensions.Hosting
* System.Text.Json
(optional, but useful for JSON manipulation)
Implementing the Core Server Logic
With the project set up, we can implement the server. The C# SDK GitHub repository provides a great starting point. The following code sets up the basic server structure.
// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.ModelStudio.ModelContextProtocol.Server;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddLogging();
builder.Services.AddMcpServerSupport(options =>
{
options.Transport = new StandardIOServerTransport();
});
builder.Services.AddToolsFromAssembly(typeof(Program).Assembly);
var app = builder.Build();
app.Run();
// Tool implementation will go here
Let's break this down:
* We use Host.CreateApplicationBuilder()
for setting up the application.
* AddMcpServerSupport()
configures the MCP server. We specify StandardIOServerTransport
for communication over standard I/O.
* AddToolsFromAssembly()
tells the server to scan the current assembly for any classes and methods decorated as MCP tools.
Creating Your First Tools
The SDK makes it easy to expose methods as tools. A class can be designated as a tool container, and methods within it can be exposed as callable tools.
Here is an example of a simple tool class:
// EchoTool.cs
using Microsoft.ModelStudio.ModelContextProtocol.Server;
using System.ComponentModel;
[Description("A collection of simple echo tools.")]
public class EchoTool
{
[McpServerTool, Description("Echoes back the message from the client.")]
public static string Echo(string message)
{
return message;
}
[McpServerTool, Description("Echoes back the message in reverse.")]
public static string Reverse(string message)
{
var charArray = message.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
[McpServerTool, Description("Returns the length of the message.")]
public static int Length(string message)
{
return message.Length;
}
}
- The
[McpServerTool]
attribute marks a method as a tool that the AI model can call. - The
[Description]
attribute is crucial. The AI model uses this text to understand what the tool does and when to use it. A clear and descriptive text is vital for the tool to be used correctly.
With just this code, our MCP server is functionally complete. It will expose three distinct tools: Echo
, Reverse
, and Length
.
Configuring and Testing the Server
To use the server with an agent like GitHub Copilot, you need to configure it.
- In your project's root, create a
.vscode
directory if one doesn't exist. - Inside
.vscode
, create a file namedmcp.json
. - Add the following configuration to
mcp.json
:
{
"servers": [
{
"name": "FancyMCP",
"command": "dotnet",
"args": [
"run",
"--project",
"/path/to/your/FancyMCP.csproj"
],
"type": "stdio"
}
]
}
Note: Replace /path/to/your/FancyMCP.csproj
with the absolute path to your project file.
After saving the file, restart the Copilot agent. It will detect the new server and its tools. You can now test it in the chat:
"Get me the length of the following message: The quick brown fox jumped over the lazy dog"
The agent will recognize the intent, identify the Length
tool as the appropriate one to use, and ask for permission to run it. Upon confirmation, it will execute the command and return the result: 45 characters
. You can then follow up with other commands.
"Can you reverse that string?"
The agent will use the conversation history and the Reverse
tool to reverse the original string.
Building a More Advanced Tool
Let's create a more practical tool that calls a real-world API. We'll build a MonkeyService
that fetches data from a REST endpoint.
First, create the service class that handles the HTTP logic.
// MonkeyService.cs
public class MonkeyService
{
private HttpClient _client;
private List<Monkey> _monkeyList;
public MonkeyService()
{
_client = new HttpClient();
}
public async Task<List<Monkey>> GetMonkeys()
{
if (_monkeyList?.Count > 0)
return _monkeyList;
var response = await _client.GetAsync("https://www.montemagno.com/monkeys.json");
if (response.IsSuccessStatusCode)
{
_monkeyList = await response.Content.ReadFromJsonAsync<List<Monkey>>();
}
return _monkeyList;
}
public async Task<Monkey> GetMonkeyByName(string name)
{
var monkeys = await GetMonkeys();
return monkeys.FirstOrDefault(m => m.Name.ToLower().Contains(name.ToLower()));
}
}
public 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; }
}
Next, register this service for dependency injection in Program.cs
:
// Program.cs (add this line before building the app)
builder.Services.AddSingleton<MonkeyService>();
Now, create the MonkeyTools
class that will expose the functionality.
// MonkeyTools.cs
using Microsoft.ModelStudio.ModelContextProtocol.Server;
using System.ComponentModel;
using System.Text.Json;
[Description("Tools for getting information about monkeys.")]
public class MonkeyTools
{
[McpServerTool, Description("Get a list of monkeys.")]
public static async Task<string> GetMonkeyList(MonkeyService monkeyService)
{
var monkeys = await monkeyService.GetMonkeys();
return JsonSerializer.Serialize(monkeys);
}
[McpServerTool, Description("Get a specific monkey by name.")]
public static async Task<string> GetMonkeyByName(MonkeyService monkeyService, string name)
{
var monkey = await monkeyService.GetMonkeyByName(name);
return JsonSerializer.Serialize(monkey);
}
}
After restarting the server, you'll have two new tools available. You can now ask:
"Get me a list of monkeys and display them in a table for easy reading."
The agent will call the GetMonkeyList
tool, receive the JSON data, and format it into a readable table. You can then ask follow-up questions.
"Get me information about the baboon monkey."
The agent will use the GetMonkeyByName
tool, passing "baboon" as the parameter, and return the specific details.
Containerizing and Deploying with Docker
One of the most powerful aspects of .NET is its seamless integration with Docker. To containerize the MCP server, add the following properties to your .csproj
file:
<PropertyGroup>
<EnableContainerSupport>true</EnableContainerSupport>
<ContainerRepository>your-docker-hub-username/monkeymcp</ContainerRepository>
<ContainerFamily>alpine</ContainerFamily>
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
</PropertyGroup>
-
EnableContainerSupport
: Enables Docker support via the .NET SDK. -
ContainerRepository
: Sets the name of the image repository on Docker Hub. -
ContainerFamily
: Specifies the base image.alpine
is used for its small size. -
RuntimeIdentifiers
: Defines the target architectures to build for, enabling multi-arch images.
With this configuration, you can build and publish the container with a single command:
dotnet publish -t:PublishContainer
This command will build and package your application into a Docker image for both linux-x64
and linux-arm64
architectures.
To publish it to Docker Hub, run:
dotnet publish -t:PublishContainer -p:ContainerRegistry=docker.io
Using the Deployed Docker Image
Once published, anyone can use your tool. To configure it in VS Code, update your mcp.json
to point to the Docker image:
{
"servers": [
{
"name": "Monkey MCP Server",
"image": "your-docker-hub-username/monkeymcp:latest",
"type": "docker"
}
]
}
The agent will now pull and run the Docker container, giving you access to the tools from anywhere.
In just a few steps, we have built a new MCP server, tested it locally, created a real-world tool that calls an API, containerized it, and deployed it to Docker Hub. The possibilities are vast—consider the internal APIs and daily scripts you use at your business. By exposing them through an MCP server, you can integrate them directly into your development workflow, staying in the flow and boosting productivity.
Join the 10xdev Community
Subscribe and get 8+ free PDFs that contain detailed roadmaps with recommended learning periods for each programming language or field, along with links to free resources such as books, YouTube tutorials, and courses with certificates.