.NETMCPAI AgentsC#

Building Your Own MCP Server in C#

Expose your tools to any AI agent by building an MCP server in C#. A practical walkthrough of the Model Context Protocol server SDK, defining tools, and choosing a transport.

We’ve covered consuming MCP servers — pointing your agent at someone else’s tools. This article flips it around: building your own MCP server in C# so that any MCP-compatible client — your agent, a colleague’s Python agent, a VS Code Copilot session — can discover and call your capabilities over a standard protocol. If a capability needs to be shared beyond a single app, publishing it as an MCP server is how you do it.

For when to build a server versus a plain C# tool, see function calling vs MCP.

What a server actually does

An MCP server advertises a list of tools (each with a name, description, and input schema) and executes them when a client sends a tools/call request. That’s it. The protocol handles discovery and invocation; you just write the tools. The official C# SDK — the ModelContextProtocol package, co-maintained by Microsoft and Anthropic — turns your annotated C# methods into a compliant server with very little ceremony.

Setting up the project

Create a host project and add the MCP server package:

dotnet new console -n MyMcpServer
cd MyMcpServer
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting

Defining your tools

Tools are C# methods decorated so the SDK can expose them. You annotate the class and methods, and describe each parameter — exactly the descriptions the client’s model reads to decide when to call your tool:

using System.ComponentModel;
using ModelContextProtocol.Server;

[McpServerToolType]
public static class OrderTools
{
    [McpServerTool, Description("Gets the delivery status of an order by its ID.")]
    public static string GetOrderStatus(
        [Description("The order ID, e.g. 'ORD-4821'")] string orderId)
    {
        // Your real logic — hit a database, call an API, etc.
        return $"Order {orderId} shipped and arrives in 2 days.";
    }

    [McpServerTool, Description("Lists open orders for a customer.")]
    public static string[] ListOpenOrders(
        [Description("The customer ID")] string customerId) =>
        new[] { "ORD-4821", "ORD-5140" };
}

The descriptions are the contract the model reads — write them as carefully as you’d write a good API doc, because they directly determine whether clients call your tools correctly.

Wiring up the server

Register the server with the generic host, tell it to discover your tools, and pick a transport:

using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Server;

var builder = Host.CreateApplicationBuilder(args);

builder.Services
    .AddMcpServer()
    .WithStdioServerTransport()      // talk over stdin/stdout
    .WithToolsFromAssembly();        // discover [McpServerTool] methods

await builder.Build().RunAsync();

WithToolsFromAssembly scans for your annotated methods, so adding a new tool is just adding a new method — no registration boilerplate.

Choosing a transport: stdio vs HTTP

MCP servers speak over one of two transports, and the choice is about where the server runs:

  • stdio — the server runs as a local subprocess of the client, communicating over standard input/output. Perfect for local tools (filesystem access, local dev utilities) and for shipping a server someone runs on their own machine. No ports, no network.
  • HTTP (Streamable HTTP / SSE) — the server runs as a remote service clients connect to over the network. Use this when the capability is centralized — a shared internal server your whole team’s agents call. It brings the usual web concerns: hosting, and crucially auth, since a networked tool server is a real attack surface (see securing agents).

Start with stdio for local and single-user tools; move to HTTP when the server needs to be shared across a network.

Testing it

Because your server is a standard MCP server, any MCP client can exercise it — including your own .NET agent using the client SDK, or the MCP Inspector tooling for manual poking. Point a client at your server, list its tools, and call one; if the description and schema look right to the client, they’ll look right to every client.

Note: the MCP C# SDK is pre-1.0 and its attributes and builder methods ([McpServerTool], WithToolsFromAssembly, transport methods) may shift between releases. Verify against the official C# SDK repo; the shape — annotate methods, register the server, pick a transport — is stable.

Takeaway

Building an MCP server in C# is mostly writing good tools: annotate methods with [McpServerTool], describe them well, register the server, and choose stdio for local use or HTTP for a shared service. The payoff is reach — a capability you’d otherwise reimplement per app becomes a standard tool that any MCP client, in any language, can discover and call. When something you’ve built deserves to be used beyond your own agent, wrap it as an MCP server and publish it once.


Have a correction or a topic you want covered? Email mani.bc72@gmail.com.