Treat Every MCP Server Like a Stranger: 7 Client-Side Rules for a .NET 10 Chat Host A developer built a .NET 10 chat host on the official MCP C# SDK (ModelContextProtocol 2.2.0) and Microsoft.Extensions.AI 10.11 that enforces seven client-side guardrails for consuming third-party MCP servers, backed by 70 offline tests (43 unit, 27 integration). The rules include disabling host environment inheritance for stdio servers, per-server case-sensitive tool allowlists that fail closed, prefix-based tool renaming to avoid collisions and meet provider name limits, and per-call timeouts, output truncation, fencing and auditing via a DelegatingAIFunction wrapper. The host also pins remote transports to Streamable HTTP rather than allowing fallback to legacy SSE. Connecting a .NET chat app to an MCP server is now a short job. McpClient.CreateAsync , ListToolsAsync , put the tools in ChatOptions.Tools , add UseFunctionInvocation , and the model starts calling tools. It's a great demo. What the demo skips is that every one of those tools belongs to someone else. A server can offer tools you never meant to expose, reuse a name another server already uses, take 30 seconds to answer, return 14 KB of text, or return a page that tells your model to ignore its instructions. And some days the server just isn't there. Tech Skill Builder is $5/month for a very limited time normally $10 — grab it now before it goes back to $10. You get 20+ complete, tested .NET + AI projects with full source code and PDF guides, and a new one added every day. 👉 Join Tech Skill Builder https://elitesolutions1.gumroad.com/l/TechSkillBuilder These are the seven rules I now follow on the client side, the chat host that consumes MCP servers. They come from a host I built on the official MCP C SDK ModelContextProtocol 2.2.0 and Microsoft.Extensions.AI 10.11 in .NET 10, with 70 offline tests 43 unit, 27 integration . The code below is the shape of each rule, not the full implementation; the omitted parts are marked. A stdio server is a process your app launches. By default, StdioClientTransport passes it your host's entire environment, including whatever API keys live there. The server you installed from a package registry five minutes ago doesn't need your model key. new StdioClientTransport new StdioClientTransportOptions { Name = server.Name, Command = server.Command , InheritEnvironmentVariables = false, EnvironmentVariables = StdioClientTransportOptions.GetDefaultEnvironmentVariables , // Arguments and logger omitted here }, loggerFactory ; GetDefaultEnvironmentVariables is the SDK's small curated set things like PATH and HOME . Start there and add only what a given server really needs. For remote servers I also pin the HTTP transport to Streamable HTTP instead of letting it auto-detect and possibly fall back to legacy SSE. Never pass a server's tool list straight to the model. Each server in config gets an exact, case-sensitive allowlist, and an empty list exposes nothing . Fail closed. Don't build this on tool annotations either. The MCP spec says clients must treat annotations as untrusted unless they come from trusted servers, so readOnlyHint: true is a claim, not a guarantee. js var allowed = new HashSet