Most teams reach the same realization after their first AI prototype: a chat box is the easy part. The hard, valuable part is letting the model do something - read a record, file a ticket, run a report - safely and within the rules of your domain. The Model Context Protocol (MCP) is the piece that makes that practical, and the .NET ecosystem now has first-class support for it through Microsoft.Extensions.AI.
This tutorial shows how the two fit together: an agent that reasons with a model, and a set of typed tools exposed over MCP that the agent can call.
Why MCP instead of bespoke function calling
You can wire function calling by hand for one model and one app. The problem is that it does not transfer. MCP standardizes how a host advertises tools, how a model invokes them, and how results flow back. That standardization buys you three things:
- Tools you write once can be consumed by any MCP-aware client, not just your app.
- The transport, schema, and error conventions are defined, so you stop re-inventing plumbing.
- Capabilities are discoverable: an agent can ask what is available rather than relying on hard-coded knowledge.
For a platform like ConnectSoft, where the same tool (say, querying a tenant catalog) is useful to many agents, that reuse is the whole point.
The model abstraction: Microsoft.Extensions.AI
Microsoft.Extensions.AI gives you an IChatClient interface that hides the differences between providers. You program against the abstraction and swap the backing model - OpenAI, Azure OpenAI, a local model - without rewriting your agent:
IChatClient client = new AzureOpenAIClient(endpoint, credential)
.AsChatClient("gpt-4o");
var response = await client.GetResponseAsync(
"Summarize the latest tenant onboarding failures.");
The same interface also carries tool definitions, which is where MCP connects.
Exposing tools over MCP
An MCP server publishes tools as named, schema-described functions. In .NET you describe a tool with a plain method and let the framework generate the schema from its signature. Keep tools narrow and explicit - one verb, typed parameters, a typed result:
[McpServerTool, Description("Look up a tenant by its slug.")]
public static async Task<TenantSummary> GetTenant(
[Description("The tenant slug, lowercase with hyphens.")] string slug,
ITenantCatalog catalog,
CancellationToken ct)
{
return await catalog.FindAsync(slug, ct)
?? throw new McpException($"No tenant found for slug '{slug}'.");
}
Notice the parameter descriptions. The model reads those to decide when and how to call the tool, so treat them as part of the prompt, not as afterthoughts.
Letting the agent call tools
With a client and tools in hand, you hand the tool set to the chat client and let the model decide when to invoke them. The framework handles the call-and-return loop:
var options = new ChatOptions { Tools = mcpTools };
var reply = await client.GetResponseAsync(messages, options);
Under the hood the model may call GetTenant, receive the typed result, and fold it into its answer. Your code never parses free-text intentions - the contract is the tool signature.
Guardrails that matter in production
Giving a model the ability to act is exactly as risky as it sounds, so a few defaults are non-negotiable:
- Authorize inside the tool, not in the prompt. The model is untrusted input; the tool enforces who can do what.
- Make tools idempotent where possible, and require explicit confirmation for destructive actions.
- Log every tool call with a correlation id so you can reconstruct what the agent did and why.
- Validate parameters with the same rigor you would apply to a public API, because effectively that is what a tool is.
Note: An MCP tool is a public surface. Anything an agent can reach, treat as reachable by an adversary who controls the model's input.
Wrapping up
MCP turns AI from a clever text generator into a participant in your system - one that can read and act through interfaces you define and control. Pair it with Microsoft.Extensions.AI and you get provider independence on the model side and a clean, typed tool surface on the application side. Start with one or two narrow, well-authorized tools, get the logging and guardrails right, and grow the surface as trust builds.
Curious how we use agents across the ConnectSoft platform? Get in touch.
