In my previous post, I demonstrated how to use C# with Microsoft.Extensions.AI
, Ollama, and a nuget package called mcpdotnet
to interact with an existing MCP Server. Since then, mcpdotnet
has been elevated to become the “official C# SDK for Model Context Protocol servers and clients, maintained by Microsoft”.
The Model Context Protocol (MCP) is an open standard that enables seamless integration between AI systems and various data sources, allowing developers to create context-aware applications. In this post, we’ll explore how to build a simple MCP server using C# and the modelcontextprotocol / csharp-sdk library, which simplifies the process of creating and managing MCP servers.
Introduction
The future of AI extends beyond powerful models to their seamless interaction with the data that drives our world. Model Context Protocol (MCP) is a groundbreaking open standard designed to bridge the gap between AI systems and diverse data sources. Developed by Anthropic, MCP simplifies the integration of AI with content repositories, business tools, and development environments. By replacing fragmented, custom integrations with a universal protocol, MCP empowers developers to create secure, scalable, and context-aware AI applications.
Requirements
- Llama 3.2 SLM, a model supporting function calling
- Ollama to run the SLM and handle the integration
- A .NET CLI application using modelcontextprotocol / csharp-sdk for the server
- Another .NET CLI application also using modelcontextprotocol / csharp-sdk for the client
Create MCPServer .NET Console Application
We’ll create a simple .NET CLI application that will serve as our MCP Server.
dotnet new console -n MCPServer
cd MCPServer
dotnet add package ModelContextProtocol --version 0.1.0-preview.1.25171.12
Implementation of an MCP Server in C#
Our console application uses modelcontextprotocol / csharp-sdk
to create an MCP server that implements the STDIO transport protocol. The library provides convenient attributes for defining MCP Tools.
While the server-side SSE transport protocol is currently under development and not yet supported, the client-side SSE transport protocol is fully functional for making MCP server calls.
Let’s start with a simple implementation: a Time MCP Server that provides a time-related Tool.
var builder = Host.CreateEmptyApplicationBuilder(settings: null);
builder.Services
// 👇🏼 We build an MCP Server
.AddMcpServer()
// 👇🏼 uses Stdio as transport protocol
.WithStdioServerTransport()
// 👇🏼 Register all tools with McpToolType attribute
.WithTools();
await builder.Build().RunAsync();
// 👇🏼 Mark our type as a container for MCP tools
[McpToolType]
public static class TimeTool
{
// 👇🏼 Mark a method as an MCP tools
[McpTool, Description("Get the current time for a city")]
public static string GetCurrentTime(string city) =>
$"It is {DateTime.Now.Hour}:{DateTime.Now.Minute} in {city}.";
}
We define a tool by decorating our class with the McpToolType
attribute and the method with the McpTool
attribute. The AI model will invoke this method when it determines it’s the appropriate tool for handling the user’s request.
Testing the MCP Server
The MCP Inspector is an interactive developer tool designed for testing and debugging MCP servers. It provides a user-friendly interface for sending requests to the server, inspecting responses, and visualizing the interaction between the AI model and the MCP server. The inspector is particularly useful for developers working with the Model Context Protocol, as it simplifies the process of testing and validating MCP server functionality.
We can start the inspector from our application folder using the nodejs command npx
with the following command:
npx @modelcontextprotocol/inspector dotnet run
It will run the inspector and start our MCP server. The inspector will automatically detect the MCP server and allow us to test it.
npx @modelcontextprotocol/inspector dotnet run
Starting MCP inspector...
Proxy server listening on port 3000
🔍 MCP Inspector is up and running at http://localhost:5173 🚀
Navigate to http://localhost:5173 click on Connect
then on Tools
tab and finally on List Tools
. You should see the GetCurrentTime
tool we just created and be able to call it passing a city name.
Our MCP server is now working 🎉
Calling the MCP Server from a C# application
Now we want to call our MCP server from a C# application. We will also use the modelcontextprotocol / csharp-sdk
library to be able to call the MCP server and get the current time. The code is almost the same as the one we used in the previous post, except the creation of the McpServerConfig
object which is pointing to our Time MCP Server just created.
Console.WriteLine("Hello, official MCP csharp-sdk and MCP Server!");
var message = "What is the current (CET) time in Illzach, France?";
Console.WriteLine(message);
// 👇🏼 Configure the MCP client options
McpClientOptions options = new()
{
ClientInfo = new() { Name = "Time Client", Version = "1.0.0" }
};
// 👇🏼 Configure the Model Context Protocol server to use
var config = new McpServerConfig
{
Id = "time",
Name = "Time MCP Server",
TransportType = TransportTypes.StdIo,
TransportOptions = new Dictionary<string, string>
{
// 👇🏼 The command executed to start the MCP server
["command"] = @"..\..\..\..\MCPServer\bin\Debug\net9.0\MCPServer.exe"
}
};
using var factory =
LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Trace));
// 👇🏼 Get an MCP session scope used to get the MCP tools
await using var mcpClient = await McpClientFactory.CreateAsync(config, options);
// 👇🏼 Use Ollama as the chat client
var ollamaChatClient =
new OllamaChatClient(new Uri("http://localhost:11434/"), "llama3.2:3b");
var client = new ChatClientBuilder(ollamaChatClient)
// 👇🏼 Add logging to the chat client, wrapping the function invocation client
.UseLogging(factory)
// 👇🏼 Add function invocation to the chat client, wrapping the Ollama client
.UseFunctionInvocation()
.Build();
IList<ChatMessage> messages =
[
new(ChatRole.System, """
You are a helpful assistant delivering time in one sentence
in a short format, like 'It is 10:08 in Paris, France.'
"""),
new(ChatRole.User, message)
];
// 👇🏼 Pass the MCP tools to the chat client
var mcpTools = await mcpClient.GetAIFunctionsAsync();
var response =
await client.GetResponseAsync(
messages,
new ChatOptions { Tools = [..mcpTools] });
Console.WriteLine(response);
The way to configure the command in McpServerConfig
is not really great. We will see in a future post how to improve that.
To run the application, simply execute dotnet run
. The client application starts, then starts our own Time MCP Server, interacts with the AI model, and returns the current time in Illzach, France.
> dotnet run
Hello, official MCP csharp-sdk and MCP Server!
What is the current (CET) time in Illzach, France?
trce: Microsoft.Extensions.AI.LoggingChatClient[805843669]
GetResponseAsync invoked: [
{
"role": "system",
"contents": [
{
"$type": "text",
"text": "You are a helpful assistant delivering time in one sentence\r\nin a short format, like 'It is 10:08 in Paris, France.'"
}
]
},
{
"role": "user",
"contents": [
{
"$type": "text",
"text": "What is the current (CET) time in Illzach, France?"
}
]
}
]. Options: {}. Metadata: {
"providerName": "ollama",
"providerUri": "http://localhost:11434/",
"modelId": "llama3.2:3b"
}.
trce: Microsoft.Extensions.AI.LoggingChatClient[384896670]
GetResponseAsync completed: {
"messages": [
{
"role": "assistant",
"contents": [
{
"$type": "functionCall",
"callId": "5FD80F38",
"name": "GetCurrentTime",
"arguments": {
"city": "Illzach, France"
}
}
]
},
{
"role": "tool",
"contents": [
{
"$type": "functionResult",
"callId": "5FD80F38",
"result": {
"content": [
{
"type": "text",
"text": "It is 9:8 in Illzach, France."
}
],
"isError": false
}
}
]
},
{
"role": "assistant",
"contents": [
{
"$type": "text",
"text": "The current time in Illzach, France (CET) is 9:08."
}
]
}
],
"responseId": "2025-03-22T08:08:44.5023608Z",
"modelId": "llama3.2:3b",
"createdAt": "2025-03-22T08:08:44.5023608+00:00",
"finishReason": "stop",
"usage": {
"inputTokenCount": 376,
"outputTokenCount": 41,
"totalTokenCount": 417,
"additionalCounts": {
"load_duration": 3766997200,
"total_duration": 4563875800,
"prompt_eval_duration": 324652800,
"eval_duration": 468492200
}
}
}.
The current time in Illzach, France (CET) is 9:08.
The trace logs demonstrate the interaction flow between components:
- Our application sends the user’s time query along with available tools
- The LLM responds with a
functionCall
, requesting to execute theGetCurrentTime
function - The
Microsoft.Extensions.AI
client invokes the tool on the MCP server - The server executes the function and returns the current time
- Finally, the assistant formats a user-friendly response based on the result
If you want to enable trace logging for the MCP client, modify your client application code as follows:
// 👇🏼 Get an MCP session scope used to get the MCP tools
await using var mcpClient =
await McpClientFactory.CreateAsync(config, options, loggerFactory: factory);
You would see the following trace logs
Hello, official MCP csharp-sdk and MCP Server!
What is the current (CET) time in Illzach, France?
info: ModelContextProtocol.Client.McpClientFactory[77006853]
Creating client for Client (time: Time MCP Server)
info: ModelContextProtocol.Protocol.Transport.StdioClientTransport[428439416]
Transport for Client (stdio) for (time: Time MCP Server) connecting
info: ModelContextProtocol.Protocol.Transport.StdioClientTransport[567343866]
Creating process for transport for Client (stdio) for (time: Time MCP Server) with command cmd.exe, arguments /c ..\..\..\..\MCPServer\bin\Debug\net9.0\MCPServer.exe, e
...
Conclusion
The Model Context Protocol (MCP) marks a significant milestone in AI integration technology. It introduces a standardized approach for connecting AI agents with tools and data sources, streamlining development while enhancing system flexibility. Through its dynamic discovery capabilities and bidirectional communication, MCP sets itself apart from conventional APIs, establishing a robust foundation for building more intelligent and responsive AI systems.
The future of AI has never looked brighter, with MCP driving seamless integrations and powering intelligent solutions across numerous use cases. It’s truly an exciting time for AI enthusiasts and professionals as we embark on a new era of connectivity and possibility! 🚀
Get the source code on GitHub aiPlayground/MCPServer and if you like it please give it a star ⭐️.
References
- Introducing the Model Context Protocol
- Model Context Protocol
- Model Context Protocol Inspector
- MCP official csharp-sdk
- npx
- Introducing Microsoft.Extensions.AI Preview - Unified AI Building Blocks for .NET
- Time MCP Server
- Integrating Model Context Protocol Tools with Semantic Kernel: A Step-by-Step Guide