Model Context ProtocolMCP.NETMicrosoft Agent Framework

Model Context Protocol for .NET Developers: Giving Your Agent Real Tools

A practical guide to Model Context Protocol (MCP) for .NET developers: connect a Microsoft Agent Framework agent to an MCP server and expose your own agent as one.

Every agent is only as useful as the tools it can call. You can hand-write a [Description]-annotated C# method for “get the weather” or “look up an order,” and that works fine for one agent, one codebase. But the moment you want that same capability available to a second agent, a different team, or a client written in Python instead of C#, you are back to copy-pasting tool definitions. Model Context Protocol (MCP) exists to solve exactly that problem, and the Microsoft Agent Framework has first-class support for it in .NET.

This article shows how to connect a Microsoft Agent Framework agent to an existing MCP server so it can call real, external tools, and — as a bonus — how to flip that around and expose your own agent as an MCP server that other clients can call.

What MCP actually is

MCP is an open, JSON-RPC-based protocol that standardizes how an application exposes tools and contextual data to an LLM-based client. Instead of every vendor inventing its own “function calling” wire format, an MCP server advertises a list of tools (with names, descriptions, and JSON schemas for their inputs), and any MCP client — regardless of language or vendor — can discover and invoke them the same way.

For .NET developers, the practical benefit is reuse. Point your agent at the official GitHub MCP server, a filesystem server, or an internal server your platform team maintains, and you get a working tool integration without writing the tool yourself. The official MCP C# SDK — co-maintained by Microsoft and Anthropic — ships as the ModelContextProtocol NuGet package and plugs directly into Microsoft.Extensions.AI and the Agent Framework’s AIAgent abstraction.

Setting up the packages

You need two packages: the Agent Framework itself, and the MCP client SDK.

dotnet add package Microsoft.Agents.AI
dotnet add package ModelContextProtocol --prerelease

At the time of writing, the MCP C# SDK’s agent-facing pieces still ship under a prerelease version, so keep an eye on release notes as it stabilizes toward 1.0.

Connecting to an MCP server

An MCP server can run as a local process (communicating over stdio) or as a remote HTTP endpoint. For a local server — say, the community-maintained GitHub MCP server, which runs as a Node package — you spin it up and connect with McpClientFactory:

using Microsoft.Extensions.AI;
using ModelContextProtocol.Client;

// Launch and connect to the GitHub MCP server over stdio
await using var mcpClient = await McpClientFactory.CreateAsync(
    new StdioClientTransport(new()
    {
        Name = "GitHubMcp",
        Command = "npx",
        Arguments = ["-y", "--verbose", "@modelcontextprotocol/server-github"],
    }));

The StdioClientTransport here launches the server as a child process and talks to it over standard input/output — no ports, no network config, just a subprocess. For a remote server exposed over HTTP, the SDK offers an HTTP-based transport instead; the rest of the flow — discovering tools, wiring them to an agent — stays identical either way.

Discovering and wiring up tools

Once connected, ask the server what it can do:

// Ask the server for its list of tools
var mcpTools = await mcpClient.ListToolsAsync();

ListToolsAsync() returns a collection already shaped as AITool instances — the same abstraction Microsoft.Extensions.AI uses for locally defined functions. That means an MCP tool and a plain C# method annotated with AIFunctionFactory.Create(...) are interchangeable from the agent’s point of view. You hand both to the tools parameter the same way:

using Microsoft.Agents.AI;
using Azure.AI.OpenAI;
using Azure.Identity;

var chatClient = new AzureOpenAIClient(
        new Uri("https://<your-resource>.openai.azure.com"),
        new DefaultAzureCredential())
    .GetChatClient("gpt-4o-mini");

AIAgent agent = chatClient.CreateAIAgent(
    name: "RepoAssistant",
    instructions: "You answer questions about GitHub repositories using the tools available to you.",
    tools: [.. mcpTools.Cast<AITool>()]);

var reply = await agent.RunAsync(
    "Summarize the last four commits to the microsoft/semantic-kernel repository.");

Console.WriteLine(reply);

The agent decides, turn by turn, whether it needs to call list_commits, get_file_contents, or any of the dozen other tools the GitHub server exposes — you never hand-code the branching logic. The Agent Framework handles marshaling the model’s tool-call request into an MCP tools/call invocation and feeding the result back into the conversation.

The other direction: exposing your agent as an MCP tool

The same standard that lets your agent consume tools also lets you publish an agent as a tool for someone else’s client — a VS Code Copilot session, another team’s agent, or an orchestration graph elsewhere in your organization. Wrap the agent as an AIFunction, register it with an MCP server, and run that server over stdio:

using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Server;

AIAgent jokeAgent = new AIProjectClient(
        new Uri("<your-foundry-project-endpoint>"),
        new DefaultAzureCredential())
    .AsAIAgent(
        model: "gpt-4o-mini",
        instructions: "You are good at telling jokes.",
        name: "Joker");

McpServerTool tool = McpServerTool.Create(jokeAgent.AsAIFunction());

HostApplicationBuilder builder = Host.CreateEmptyApplicationBuilder(settings: null);
builder.Services
    .AddMcpServer()
    .WithStdioServerTransport()
    .WithTools([tool]);

await builder.Build().RunAsync();

Now any MCP-compatible client can discover and call the Joker agent exactly like any other tool, with the agent’s name and instructions surfaced as the tool’s metadata.

Note: the MCP C# SDK is evolving quickly, and method names like McpClientFactory.CreateAsync or McpServerTool.Create may shift between prereleases. Treat the snippets above as a conceptual map of the flow — connect, list tools, wire to an agent, or wrap an agent as a tool — and verify exact signatures against the version you install before shipping.

Security is not optional

Because MCP makes it trivially easy to bolt on tools from servers you did not write, treat every third-party MCP server the way you would treat a third-party NuGet package with network access and side effects:

  • Only connect to servers from providers you trust, ideally hosted directly by that provider rather than through a proxy.
  • Log the data you send to and receive from remote servers for auditing.
  • For authenticated remote servers, pass credentials as per-request headers rather than baking them into a shared client, so secrets don’t leak across sessions.
  • DefaultAzureCredential is fine for local development, but in production prefer a specific credential type (such as ManagedIdentityCredential) to avoid the latency and probing behavior of the fallback chain.

Takeaway

MCP turns “tools my agent can call” from a per-project, per-language chore into a reusable, standardized capability. In the Microsoft Agent Framework, that integration is close to seamless: an MCP tool and a hand-written C# function both end up as an AITool, so your agent code barely needs to know the difference. Start by pointing an agent at one well-known server — GitHub or filesystem access are good first targets — before you invest in writing or exposing your own.

For where this goes next, see multi-agent orchestration with graph workflows and building your own MCP server in C#.


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