| |

Microsoft Agent Framework: Exposing an Existing AI Agent as an MCP Tool

In earlier posts in this series, we’ve explored how to extend AI agents using function tools, how to compose agents together as function tools, and how to build and debug MCP servers in .NET.

In this post, we look at how you can take an existing AI agent built with the Microsoft Agent Framework and expose it as an MCP tool, thereby making it available to any MCP-compatible client.

This is where the two worlds collide.

  • We already know how to build agents.
  • We already know how to build MCP servers.

 

Now we combine them.

The idea is simple: take an agent, convert it to a function, wrap it as an MCP tool, and serve it over HTTP.

In this blog post, we cover the following:

  • What it means to expose an agent as an MCP tool
  • Why you would want to do this
  • The key NuGet packages required
  • How to create an AI agent using direct OpenAI
  • How to convert that agent to an MCP tool
  • How to host the MCP server with SSE transport
  • Testing with MCP Inspector

 

Source code and video demo is included.

Let’s dig in.

~

What Does It Mean to Expose an Agent as an MCP Tool

In the agents as function tools post, we saw how an agent can be converted to an AIFunction using the .AsAIFunction() method.

This allows one agent to call another agent as if it were a regular function tool.

The Model Context Protocol takes this a step further. Instead of one agent calling another agent internally, we expose that agent-as-function to the outside world via an MCP server.

Any MCP-compatible client, VS Code, Claude Desktop, MCP Inspector, or your own custom client can discover and invoke the agent.

The conversion chain looks like this:

AI Agent → AIFunction → McpServerTool → MCP Server

 

The agent’s name becomes the tool name. The agent’s description becomes the tool description. MCP clients see it as just another tool, they don’t need to know there’s an entire AI agent behind it.

~

Why Expose an Agent as an MCP Tool

There are several reasons you might want to do this:

  • Reusability – an agent you’ve already built and tested can be consumed by any MCP-compatible client without writing new integration code
  • Standardised access – MCP provides a well-defined protocol so you build once and connect everywhere
  • Composability – other agents or tools can discover and call your agent through MCP, enabling multi-agent workflows across different systems
  • Separation of concerns – the agent logic lives in your server, and clients simply invoke it over the wire

 

If you’ve been following this series, you’ll recognise the pattern.

In the function tools post, we gave agents the ability to call functions.

In the agents as function tools post, we let agents call other agents.

Now we’re letting anything call our agent -as long as it speaks MCP.

~

Setting Up the Project

The project uses the ASP.NET Core Web SDK since we’re hosting the MCP server over HTTP with SSE transport.

Here’s the project file with the required NuGet packages:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Agents.AI" Version="1.0.0-preview.260205.1" />
    <PackageReference Include="Microsoft.Agents.AI.OpenAI" Version="1.0.0-preview.260205.1" />
    <PackageReference Include="ModelContextProtocol" Version="0.8.0-preview.1" />
    <PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.8.0-preview.1" />
    <PackageReference Include="OpenAI" Version="2.8.0" />
    <PackageReference Include="System.ClientModel" Version="1.8.1" />
  </ItemGroup>
</Project>

 

The key packages to note:

  • Microsoft.Agents.AI and Microsoft.Agents.AI.OpenAI – the Microsoft Agent Framework and its OpenAI integration
  • ModelContextProtocol and ModelContextProtocol.AspNetCore – the .NET MCP SDK with ASP.NET Core SSE transport support
  • OpenAI — the official OpenAI .NET SDK for direct API access

~

The Agent We’re Exposing

For this example, we’re using a nutrition agent that searches for high protein recipes.

If you’ve been following the series, you’ll recognise this from the agents as function tools post where it was used as a sub-agent within our Iron Mind AI personal trainer.

The agent has a single function tool SearchRecipesAsync . This takes a search term, a minimum protein threshold, and a maximum number of results:

public class NutritionAgent
{
    [Description("Searches for high protein recipes optimized for strength training. " +
                 "Returns recipes with detailed nutrition information. " +
                 "Best for finding recipes for 5x5 training, muscle building, or post-workout meals.")]
    public static Task<List<string>> SearchRecipesAsync(
        [Description("Search term (e.g., 'chicken', 'beef', 'protein', 'breakfast')")] string searchTerm,
        [Description("Minimum protein grams per serving (default 20)")] int minProtein = 20,
        [Description("Maximum number of recipes to return (default 3)")] int maxResults = 3)
    {
        var results = _stubRecipes
            .Where(r => r.Title.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) ||
                        r.Ingredients.Any(i => i.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)))
            .Where(r => r.Nutrition != null && r.Nutrition.ProteinGrams >= minProtein)
            .Take(maxResults)
            .Select(r => JsonSerializer.Serialize(r))
            .ToList();

        return Task.FromResult(results);
    }
}

 

The [Description] attributes are crucial here. They tell the AI agent (and ultimately the MCP client) what the function does and what each parameter means. This is the same pattern we covered in the function tools post.

Each recipe is a simple model with nutritional information:

public class Recipe
{
    public string Title { get; set; } = string.Empty;
    public string Url { get; set; } = string.Empty;
    public List<string> Ingredients { get; set; } = new();
    public List<string> Instructions { get; set; } = new();
    public NutritionInfo? Nutrition { get; set; }
    public string PrepTime { get; set; } = string.Empty;
    public string CookTime { get; set; } = string.Empty;
    public int Servings { get; set; }
}

 

We also have a NutrionInfo class :

public class NutritionInfo
{
    public int Calories { get; set; }
    public int ProteinGrams { get; set; }
    public int CarbsGrams { get; set; }
    public int FatGrams { get; set; }
}

~

Creating the Agent and Exposing It as an MCP Tool

This is where it all comes together. The Program.cs file does three things:

  1. Creates an AI agent using direct OpenAI
  2. Converts the agent to an MCP tool
  3. Hosts the MCP server with SSE transport

 

Here’s the full code:

using Agent_Framework_7_Existin_Agent_as_MCP_Tool.Agent;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using ModelContextProtocol.AspNetCore;
using ModelContextProtocol.Server;
using OpenAI;

string apiKey = "your-openai-api-key";
string model = "gpt-4o-mini";

// Create the nutrition agent using direct OpenAI
AIAgent nutritionAgent = new OpenAIClient(apiKey)
    .GetChatClient(model)
    .AsIChatClient()
    .AsAIAgent(
        instructions: "You can fetch recipes and nutrition data.",
        name: "NutritionAgent",
        description: "An agent that searches for high protein recipes and nutrition information.",
        tools: [AIFunctionFactory.Create(NutritionAgent.SearchRecipesAsync)]
    );

// Convert the agent to an AIFunction and then to an MCP tool.
// The agent name and description will be used as the MCP tool name and description.
McpServerTool tool = McpServerTool.Create(nutritionAgent.AsAIFunction());


// Register the MCP server with SSE transport and expose the tool via the server.
var builder = WebApplication.CreateBuilder(args);
builder.Services
    .AddMcpServer()
    .WithHttpTransport()
    .WithTools([tool]);

var app = builder.Build();

app.MapMcp();
await app.RunAsync();

 

Let’s walk through each part.

~

Creating the Agent

We create an OpenAIClient with our API key, get a chat client for the model, and then use the .AsIChatClient().AsAIAgent() extension method chain to create an AIAgent.

This is the same pattern from earlier posts in the series, but note two things:

  • We use .AsIChatClient() before .AsAIAgent().
  • We provide a description parameter. This is important because it becomes the MCP tool description that clients will see.

 

Next, we will create the MCP tool from the agent.

~

Converting to an MCP Tool

This is the key line:

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

Two things happen here.

First, .AsAIFunction() converts the entire agent , including its instructions, tools, and conversation handling, into a single callable function. We used this same method in the agents as function tools post to let one agent call another.

Second, McpServerTool.Create() wraps that function as an MCP tool that can be served to clients.

~

Hosting the MCP Server

We use WebApplication.CreateBuilder and the ModelContextProtocol.AspNetCore package to host the MCP server with HTTP/SSE transport.

The three key calls are:

  • .AddMcpServer()  – registers the MCP server services
  • .WithHttpTransport() – configures SSE transport over HTTP
  • .WithTools([tool]) – registers our agent-as-MCP-tool

 

Finally, app.MapMcp() maps the MCP endpoints and app.RunAsync() starts the server.

By default, the server runs on http://localhost:5000.

~

Testing with MCP Inspector

MCP Inspector is a handy tool for testing MCP servers. You can use it to connect to your server, discover tools, and invoke them.

Step 1: Run the project:

dotnet run

Step 2: Open MCP Inspector tool: npx @modelcontextprotocol/inspector dotnet runand configure the connection:

  • Transport Type: SSE
  • URL: http://localhost:5000/sse

Step 3: Click Connect. You should see the NutritionAgent tool listed with its description.

Step 4: Select the tool and provide a search term (e.g., “chicken“). Click Run Tool:

The MCP Inspector sends the request to your server. Your server invokes the AI agent, which uses its SearchRecipesAsync function tool to find matching recipes. The results are returned to the MCP Inspector as the tool response.

What’s happening behind the scenes is worth highlighting.

The MCP client sends a tools/call request. Your MCP server receives it and invokes the McpServerTool.

The tool calls the AIFunction, which runs the full AI agent. The agent processes the request using its instructions and function tools, and the result flows back through the chain to the MCP client.

~

Demo

You can see this in action in the 5 minute demo:

~

Summary

In this post, we’ve covered how to take an existing AI agent built with the Microsoft Agent Framework and expose it as an MCP tool.

The key part is that the conversion chain :

Agent → AIFunction → McpServerTool

This bridges two powerful concepts we’ve explored separately in this series.

The important pieces to remember:

  • .AsAIFunction() converts an agent into a callable function
  • McpServerTool.Create() wraps that function as an MCP tool
  • .WithHttpTransport() serves the tool over HTTP with SSE transport
  • The agent’s name and description become the MCP tool’s name and description
  • Any MCP compatible client can discover and invoke the agent

 

Being able to take an agent you’ve already built and make it available over a standardised protocol is a practical step forward.  You build the agent once, and any MCP client can use it.

~

Further Reading and Resources

Some additional resources you might find helpful.

~

Enjoy what you’ve read, have questions about this content, or would like to see another topic?

Drop me a note below.

You can schedule a call using my Calendly link to discuss consulting and development services.

~

JOIN MY EXCLUSIVE EMAIL LIST
Get the latest content and code from the blog posts!
I respect your privacy. No spam. Ever.

Similar Posts

Leave a Reply