Create Your First C# MCP Server Using ASP.NET Core
The usefulness of an AI software depends on the tools it can access. These tools have a uniform form thanks to the Model Context Protocol (MCP). Any MCP-aware client, such an agent, VS Code, or the MCP Inspector, may find and call a server that advertises its capabilities. Now that the official C# SDK is at version 1.x, developers may create servers using the ASP.NET Core components they are already familiar with.
In this post, we use two technologies to create a simple inventory server that runs locally. It takes five minutes or so.
What you need
- The .NET 8 SDK or later.
- Node.js, only if you want to test with the MCP Inspector.
Step 1: Create the project
dotnet new web -n InventoryMcp
cd InventoryMcp
dotnet add package ModelContextProtocol.AspNetCore
The ModelContextProtocol.AspNetCore package is the one for HTTP-based servers. It brings in the core SDK as a dependency.
Step 2: Wire up the server
Replace the contents of Program.cs with this:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddMcpServer()
.WithHttpTransport()
.WithToolsFromAssembly();
var app = builder.Build();
app.MapMcp("/mcp");
app.Run();
AddMcpServer registers the server, WithHttpTransport makes it reachable over HTTP, and WithToolsFromAssembly scans your project for tools. MapMcp exposes everything at /mcp.
Step 3: Add two tools
Create a file named InventoryTools.cs:
using System.ComponentModel;
using ModelContextProtocol.Server;
[McpServerToolType]
public static class InventoryTools
{
// Demo data. In a real server this would call your ERP or a database.
private static readonly StockInfo[] Data =
[
new("PLANT-1020", "MAT-9920", 450, 500),
new("PLANT-1020", "MAT-1104", 80, 120),
new("PLANT-3040", "MAT-9920", 1200, 300),
];
[McpServerTool, Description("Get stock on hand and reorder level for one material at one plant.")]
public static StockInfo? GetStock(
[Description("Plant ID, for example PLANT-1020")] string plantId,
[Description("Material number, for example MAT-9920")] string materialNumber)
=> Data.FirstOrDefault(s => s.PlantId == plantId && s.MaterialNumber == materialNumber);
[McpServerTool, Description("List the materials at a plant that are below their reorder level.")]
public static StockInfo[] ListLowStock(
[Description("Plant ID, for example PLANT-1020")] string plantId)
=> Data.Where(s => s.PlantId == plantId && s.OnHand < s.ReorderLevel).ToArray();
}
public record StockInfo(string PlantId, string MaterialNumber, int OnHand, int ReorderLevel);
Two attributes do the work. [McpServerToolType] marks the class, and [McpServerTool] marks each method that becomes a tool. The SDK builds the input schema from the method signature, so you do not write any JSON schema by hand.
Step 4: Run it and try it
Start the server on a fixed port so the URL is easy to remember:
dotnet run --urls http://localhost:5000
In a second terminal, start the MCP Inspector:
npx @modelcontextprotocol/inspector
In the Inspector, choose the Streamable HTTP transport, enter http://localhost:5000/mcp, and connect. Open the Tools tab and list the tools. You should see GetStock and ListLowStock with their descriptions. Call ListLowStock with PLANT-1020 and you get both materials back, since each one is below its reorder level.
Why the descriptions matter
The model never sees your C# code. It sees the tool name, the description, and the parameter descriptions. If those are vague, the model will pick the wrong tool or pass the wrong values. Put an example value in each parameter description, as the code above does, and the calls get noticeably more accurate.
Before you use it for real
- Keep each tool to one job and return small results. Large responses use up the model’s context.
- Treat every input as untrusted. The values come from a model, so validate them like you would any user input.
- Add authentication before the endpoint leaves your machine. The mapped endpoint works with the usual ASP.NET Core authentication and authorization, for example by calling
RequireAuthorizationon it. - Return only what the caller should see. A tool result goes straight back into the model’s context.
In the next article we connect an AI agent written in C# to this server, so the agent can answer stock questions on its own.
ASP.NET Core 11.0 Hosting Recommendation
HostForLIFE.eu
HostForLIFE.eu is a popular recommendation that offers various hosting choices. Starting from shared hosting to dedicated servers, you will find options fit for beginners and popular websites. It offers various hosting choices if you want to scale up. Also, you get flexible billing plans where you can choose to purchase a subscription even for one or six months.
