Don't Hand Your AI Agent the Keys: Building a Secure Remote MCP Server in ASP.NET Core A developer published a walkthrough for building a secure remote Model Context Protocol server in ASP.NET Core using the official MCP C# SDK 2.2 on .NET 10, wrapping the stateless 2026-07-28 spec with API-key authentication, role-based tool authorization, per-caller rate limiting, call auditing and human approval. The support-desk example exposes read, write and destructive tools gated by roles, and ships as a project with 52 passing tests that runs without an AI key. The author notes the 2.x SDK removed the initialize handshake and Mcp-Session-Id header, so security can no longer rely on session state. Originally published on Medium https://medium.com/@michaelmaurice410/dont-hand-your-ai-agent-the-keys-building-a-secure-remote-mcp-server-in-asp-net-core-e3eb311b141e . Full source code for this project is in Tech Skill Builder: https://elitesolutions1.gumroad.com/l/TechSkillBuilder https://elitesolutions1.gumroad.com/l/TechSkillBuilder The MCP C SDK 2.x went stateless with the 2026-07-28 spec. Here's how to wrap it in authentication, role-based tools, auditing and human approval, with a tested .NET 10 project you can run in five minutes. Most Model Context Protocol demos look the same. You get one Echo tool on stdio, a Claude or Copilot screenshot, and that's it. Then someone asks for the same thing as a shared HTTP service that agents across the company can call, and the hard questions start. Who is calling? Which tools should they see? What happens when the model decides to close a customer's ticket at 3 a.m.? This article answers those questions with the official MCP C SDK 2.2 on .NET 10. We'll build a support desk MCP server where: Everything here comes from a complete project with 52 passing tests. It runs without an AI key. An MCP tool is a remote procedure an LLM can call with arguments it made up. That makes it an API endpoint with a very creative client. All the usual API rules apply, plus a few new ones: close ticket , a prompt injection is one sentence away from using it. add comment author, text takes the author as a parameter, the model can claim to be anyone. The timing matters too. Version 2.0 of the C SDK July 2026 aligned with the 2026-07-28 MCP specification. That revision removes the initialize handshake and the Mcp-Session-Id header from the wire format. Clients bootstrap with server/discover , and the SDK now defaults HTTP servers to stateless mode. Stateless servers scale behind any load balancer, which is exactly what you want for a shared service. It also means you can't lean on session state for security. Agent IChatClient + FunctionInvokingChatClient │ Streamable HTTP, X-Api-Key header, MCP-Protocol-Version: 2026-07-28 ▼ ASP.NET Core pipeline Host filtering AllowedHosts → Authentication API key → ClaimsPrincipal → Authorization endpoint requires an authenticated user → Rate limiting partitioned per caller ▼ MapMcp "/mcp" SessionMode = Stateless AddAuthorizationFilters Authorize Roles = ... on tool classes Call-tool audit filter caller, tool, outcome, duration ▼ Tools: get ticket · list my tickets · search knowledge base any role add ticket comment · escalate ticket Agent, Admin close ticket Admin, destructive The rule is defense in depth. ASP.NET Core decides whether you may talk to the server at all. The MCP layer decides which tools you get. The agent decides whether a human must confirm . dotnet new web -n SupportDesk.McpServer dotnet add package ModelContextProtocol.AspNetCore --version 2.2.0 js builder.Services.AddMcpServer o = { o.ServerInfo = new Implementation { Name = "support-desk", Version = "1.0.0" }; o.ServerInstructions = "Look tickets up before changing them..."; } .WithHttpTransport http = http.SessionMode = HttpServerSessionMode.Stateless .AddAuthorizationFilters .WithTools