Section 4 — Building MCP Servers in C#
Summary — what this page covers The payoff of Day 1's MCP section: yesterday attendees used the GitHub MCP server; today they build their own in C# to connect Claude to their own database and internal services. Using the official ModelContextProtocol SDK, they define typed tools, wire them through DI to the real BookTracker
DbContextand services, and connect the server to Claude Code for live testing. Lean into the "I used one yesterday, I'm building one today" ah-ha. Pair with Lab 4.
2:15 – 3:15 PM · 60 min — 30 min lecture/demo + 30 min lab
Learning objectives
-
Connect Day 1's MCP host/client/server model to a concrete C# implementation
-
Install/configure the official ModelContextProtocol NuGet package (v1.x GA)
-
Define MCP tools with strongly-typed input schemas in C#
-
Connect Claude to your own data: wire MCP tools through DI to the BookTracker database (EF Core
DbContext) and existing internal services — reuse, don't reimplement -
Connect a custom C# MCP server to Claude Code for live testing
-
Apply tool-description design principles for reliable model usage
-
Understand security: authentication, input validation, audit logging
Content
Block 4A — MCP server architecture in .NET (≈15 min)
Recap from Day 1. MCP is host / client / server with three primitives — tools (callable functions), resources (readable data), prompts (templates). Yesterday you were the host using GitHub's server. Today you write the server.
The C# SDK. The official ModelContextProtocol NuGet package (v1.x GA) lets you build an
MCP server as an ordinary ASP.NET Core host. Project layout: a new BookTracker.Mcp project that
references the existing Core/Data projects so it can reuse your services.
Transport: use Streamable HTTP — the current MCP transport. (Not the older HTTP+SSE transport; the SDK defaults to Streamable HTTP for HTTP-hosted servers.)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
.WithHttpTransport() // Streamable HTTP
.WithToolsFromAssembly(); // discover [McpServerTool] methods
// register the BookTracker services/DbContext the tools will use:
builder.Services.AddDbContext<BookTrackerContext>(/* ... */);
var app = builder.Build();
app.MapMcp();
app.Run();
Block 4B — Define tools & connect to your data (≈25 min)
Define tools as attributed methods with typed parameters. Tool descriptions are the contract
with the model — the same description discipline as skills and in-process tools (say when to use it).
Inject the BookTracker DbContext and existing services so the tool runs your code:
[McpServerToolType]
public class BookTools
{
[McpServerTool, Description("Search BookTracker for books matching a title or author.")]
public async Task<string> SearchBooks(BookTrackerContext db, string query) =>
// reuse your real query path — Claude never touches the DB directly
Serialize(await db.Books.Where(b => b.Title.Contains(query)).Take(10).ToListAsync());
}
Connect Claude to your own data. Example tools: search_books (queries the DB),
get_reading_progress, add_book. The MCP server is a thin, governed gateway over systems you
already own — Claude calls your tools, and your tools decide what's allowed. Reuse services; don't
reimplement them.
Block 4C — Connect to Claude Code & secure it (≈20 min)
Register the running server with Claude Code (claude mcp add), confirm with claude mcp list,
and ask Claude Code to invoke a tool — watch it execute against the real BookTracker database.
Security patterns (a governed gateway is only as safe as its rules):
-
Authentication — require a token; don't expose the server unauthenticated.
-
Input validation — validate tool arguments before they hit the data layer (the same discipline as your API endpoints).
-
Audit logging — log every tool invocation (who, what, when) so you can answer "what did the agent do?"
Demos referenced here
- Defining a tool (attribute + DI to a real service) · Connecting the running server to
Claude Code and invoking a tool live against the database. [Scripts in
_instructor/.]
→ Continue to Lab 4.