Getting started
This guide walks you through installing the MCP C# SDK and building a minimal MCP client and server.
Choosing a package
The SDK ships as three NuGet packages. Pick the one that matches your scenario:
| Package | Use when... |
|---|---|
| ModelContextProtocol.Core | You only need the client or low-level server APIs and want the minimum set of dependencies. |
| ModelContextProtocol | You're building a client or a stdio-based server and want hosting, dependency injection, and attribute-based tool/prompt/resource discovery. References ModelContextProtocol.Core. This is the right starting point for most projects. |
| ModelContextProtocol.AspNetCore | You're building an HTTP-based MCP server hosted in ASP.NET Core. References ModelContextProtocol, so you get everything above plus the HTTP transport. |
Tip
If you're unsure, start with the ModelContextProtocol package. You can always add ModelContextProtocol.AspNetCore later if you need HTTP transport support.
Building an MCP server
Tip
You can also use the MCP Server project template to quickly scaffold a new MCP server project.
Create a new console app and add the required packages:
dotnet new console
dotnet add package ModelContextProtocol
dotnet add package Microsoft.Extensions.Hosting
Replace Program.cs with the following code to get a working MCP server that exposes a single tool over stdio:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
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();
[McpServerToolType]
public static class EchoTool
{
[McpServerTool, Description("Echoes the message back to the client.")]
public static string Echo(string message) => $"hello {message}";
}
The call to WithToolsFromAssembly discovers every class marked with [McpServerToolType] in the assembly and registers every [McpServerTool] method as a tool. Prompts and resources work the same way with [McpServerPromptType] / [McpServerPrompt] and [McpServerResourceType] / [McpServerResource].
For HTTP-based servers using ASP.NET Core, add the following packages:
dotnet new web
dotnet add package ModelContextProtocol.AspNetCore
And add the following code:
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
.WithHttpTransport(options =>
{
// Stateless mode is recommended for servers that don't need
// server-to-client requests like sampling or elicitation.
// See the Stateless and Stateful documentation for details.
options.Stateless = true;
})
.WithToolsFromAssembly();
var app = builder.Build();
app.MapMcp();
app.Run("http://localhost:3001");
[McpServerToolType]
public static class EchoTool
{
[McpServerTool, Description("Echoes the message back to the client.")]
public static string Echo(string message) => $"hello {message}";
}
Host name validation
For local HTTP servers, keep the set of accepted host names limited to loopback values. This helps protect against DNS rebinding, where a browser reaches a local server through an attacker-controlled DNS name while sending that DNS name in the HTTP Host header. ASP.NET Core's Kestrel server doesn't validate Host headers by default, so configure AllowedHosts with known host names rather than "*".
For production servers, configure the exact public host names for the deployment, and validate the host name at the proxy or load balancer when one is responsible for forwarding client requests. This also avoids reflecting untrusted host names through ASP.NET Core features such as absolute URL generation. See Host filtering with ASP.NET Core Kestrel web server | Microsoft Learn and URL generation concepts | Microsoft Learn.
Browser cross-origin access
Only enable cross-origin requests (CORS) if you intentionally want browser-based cross-origin access to this server.
CORS is not a substitute for host name validation. When browser-based cross-origin access is required, limit which browser origins can call the MCP endpoint by using the most restrictive ASP.NET Core CORS policy possible. See Enable Cross-Origin Requests (CORS) in ASP.NET Core | Microsoft Learn.
For the full HTTP security examples, including AllowedHosts and restrictive CORS on MapMcp, see Streamable HTTP transport.
Building an MCP client
Create a new console app and add the following packages:
dotnet new console
dotnet add package ModelContextProtocol
Replace Program.cs with the following code. This client connects to the MCP "everything" reference server, lists its tools, and calls one:
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
var clientTransport = new StdioClientTransport(new StdioClientTransportOptions
{
Name = "Everything",
Command = "npx",
Arguments = ["-y", "@modelcontextprotocol/server-everything"],
});
var client = await McpClient.CreateAsync(clientTransport);
// Print the list of tools available from the server.
foreach (var tool in await client.ListToolsAsync())
{
Console.WriteLine($"{tool.Name} ({tool.Description})");
}
// Execute a tool (this would normally be driven by LLM tool invocations).
var result = await client.CallToolAsync(
"echo",
new Dictionary<string, object?>() { ["message"] = "Hello MCP!" },
cancellationToken: CancellationToken.None);
// echo always returns one and only one text content object
Console.WriteLine(result.Content.OfType<TextContentBlock>().First().Text);
Clients can connect to any MCP server, not just ones created with this library. The protocol is server-agnostic.
Using tools with an LLM
McpClientTool inherits from AIFunction, so the tools returned by ListToolsAsync can be given directly to any IChatClient:
// Get available tools.
IList<McpClientTool> tools = await client.ListToolsAsync();
// Call the chat client using the tools.
IChatClient chatClient = ...;
var response = await chatClient.GetResponseAsync(
"your prompt here",
new() { Tools = [.. tools] });
Next steps
Explore the rest of the conceptual documentation to learn about tools, prompts, resources, transports, and Docker deployment for HTTP-based servers. You can also browse the samples directory for complete end-to-end examples.