# MCP in Microsoft Agent Framework: Exposing AI Agent as an MCP Tool-with a twist

The title of the article is not clickbait. It is intentional. There is a very specific reason behind it.

I will explain the reason, the problems and why I think that the title reflects what I am about to discuss in this article.

### Model Context Protocol (MCP)

To get started with MCP, there are many online resources available that cover everything from MCP fundamental concepts and architecture to tools, resources, prompts and so on and so forth.

You can refer to the following links to understand what MCP is all about

*   **Introduction to MCP** >> [https://modelcontextprotocol.io/docs/2026-07-28/getting-started/intro](https://modelcontextprotocol.io/docs/2026-07-28/getting-started/intro)
    
*   **C# MCP SDK >>** [https://csharp.sdk.modelcontextprotocol.io/v1/api/ModelContextProtocol.Server.McpServerTool.html](https://csharp.sdk.modelcontextprotocol.io/v1/api/ModelContextProtocol.Server.McpServerTool.html)
    
*   **Build MCP Server in C# >>** [https://devblogs.microsoft.com/dotnet/build-a-model-context-protocol-mcp-server-in-csharp/](https://devblogs.microsoft.com/dotnet/build-a-model-context-protocol-mcp-server-in-csharp/)
    

I don't want to repeat the same MCP concepts in this article that are already covered in abundance in the above resources and are more technically superior and in-depth compared to my abilities to explain them :)

The intention of this article is to focus on a specific issue that I faced.

### Problem Statement

So, I had a scenario where I wanted to expose an **AIAgent** as an MCP tool . The conventional approach is to expose it as the **.AsAIFunction()** method to the [**McpServerTool**](https://csharp.sdk.modelcontextprotocol.io/v1/api/ModelContextProtocol.Server.McpServerTool.html) class.

```csharp
McpServerTool tool = McpServerTool.Create(agent.AsAIFunction());
```

and then add McpServerTool `(tool)` to the builder services collection

```csharp
builder.Services
    .AddMcpServer()
    .WithHttpTransport()
    .WithTools([tool]);
```

This registers the McpServerTool with the MCP server, adding it to the server's tool collection so that the tool can be exposed to MCP clients.

Assume that there is an agent called **SomeAIAgent** with **SomeAIFunction** registered as one of its tools.

```csharp
AIAgent SomeAIAgent = new OpenAIClient(apiKey)
   .......,
   .......,
   .AsAIAgent(.....),
   tools: [AIFunctionFactory.Create(SomeAIFunction)]
   );
```

But here comes the twist.

What if your AIFunctions are registered with DI (Dependency Injection) and you have to resolve and invoke these AIFunctions as MCP endpoint ?

Look at the following example where an AIFunction is registered with the DI container.

```csharp
builder.Services.AddSingleton<AIFunction>(sp =>
    {
 return AIFunctionFactory.Create(SomeAIFunction, new AIFunctionFactoryOptions { Name = "SomeAIFunctionName", Description = "Some Description", SerializerOptions = SomeeSerializerContext.Default.Options });
    });
```

This is where things starts to get interesting.

Normally, to use the above function I will have to rewrite them in a class marked **\[McpServerToolType\]** and expose methods with the \[**McpServerTool\]** annotation.

For example the following screenshot taken from the example [here](https://devblogs.microsoft.com/dotnet/build-a-model-context-protocol-mcp-server-in-csharp/#defining-our-first-tool) shows how MCP Server tool is defined so that they can be exposed through a MCP endpoint.

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/076e11ad-c4bd-4e83-9ee5-a6517b74dce9.png align="center")

Of course I didn't wanted to redesign all my AIFunctions to be inline and compatible with the above design. This would had required a lot of code refactoring.

So I had to somehow figure out a way where in I can use the DI registered AIFunctions without having to rewrite them all over again.

### MCP Inspector

But before we get into more details of the issue , lets first walkthrough the basic steps of setting up and installing MCP inspector to test our MCP endpoints.

To run MCP inspector you need **Node.js** in your system. To verify if Node.js is installed , run the following command in PowerShell

```csharp
node -v
```

If it does not return a version number then it indicates that Node.js is not installed.

Execute the following command in **PowerShell** to install Node.js

```csharp
winget install OpenJS.NodeJS.LTS
```

Accept the license agreement and verify the installation by re executing the following command.

```csharp
node -v
```

It should now return the node version number.

![MCP , Install MCP inspector](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/3c06aa96-300b-4fe2-b13e-ef0b86d9b9cc.png align="left")

Once its verified that node.js is installed , execute the following command to install MCP inspector

```csharp
npm install -g @modelcontextprotocol/inspector
```

Now execute the following command in PowerShell to run MCP inspector.

```csharp
 npx -y @modelcontextprotocol/inspector
```

![MCP , Install MCP inspector](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/96755ade-247f-4f48-b262-0d9cc4845077.png align="center")

This will open the MCP inspector UI in a new browser page.

![MCP , Install MCP inspector](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/b3e9c330-630e-4e22-8e0c-1adc8c51e171.png align="center")

We will have to add MCP server details which basically is going to be the AIAgent that we are going to expose as a MCP tool. We will come back to this later in the article.

### **Project Setup**

Create a new **ASP.Net** core project and add the following packages

```csharp
dotnet add package Azure;
dotnet add package Azure.AI.OpenAI;
dotnet add package Microsoft.Agents.AI;
dotnet add package Microsoft.Extensions.AI;
dotnet add package ModelContextProtocol.Server;
dotnet add package System.Text.Json.Serialization;
dotnet add package Microsoft.Extensions.Configuration;
dotnet add package Microsoft.Extensions.DependencyInjection;
dotnet add package Microsoft.Extensions.Logging;
```

Add **appsetting.json** to the project

```csharp
"AppSettings": { 
    "Chat_DeploymentName": "Deployment Name",
    "EndPoint": "Azure OpenAI endpoint",
    "ApiKey": "Azure OpenAI API key"
}
```

### **Code**

Once all the artifacts in place, add the following code to **Program.cs**

**Program.cs >>**

```csharp
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false)
.Build();
```

**Create a web application instance**

```csharp
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
```

**Read credentials and register** `Chatclient`

```csharp
var credential = new AzureKeyCredential(configuration["AppSettings:ApiKey"]);

servicecollection.AddKeyedChatClient(
    "ChatClient",
    (
        sp =>
            new AzureOpenAIClient(new Uri(configuration["AppSettings:EndPoint"]), credential)                .GetChatClient(configuration["AppSettings:Chat_DeploymentName"])
   .AsIChatClient()
    )
);
```

**Inject and register a** `ChatClientAgent`

```csharp
 builder.Services.AddSingleton<ChatClientAgent>(sp =>
 {
     return new ChatClientAgent(sp.GetKeyedService<IChatClient>("ChatClient"));
 });
```

Lets have a function called **ReturnCityTemperature** that returns the temperature of a given city. This function is to be registered with the DI container as an AIFunction.

We define the required request and response objects for **CityTemperature**

**CityTemperatureSearchRequest & CityTemperatureSearchResponse** **\>>**

```csharp
 public class CityTemperatureSearchRequest
 {
     public string City { get; set; }
 }

 public class CityTemperatureSearchResponse
 {
     public string City { get; set; }
     public string Temperature { get; set; }
 }
```

Next, create serialization metadata for both request and response types.

```csharp
[JsonSerializable(typeof(CityTemperatureSearchRequest))]
[JsonSerializable(typeof(CityTemperatureSearchResponse))]
internal sealed partial class CityTemperatureSerializerContext : JsonSerializerContext;
```

The following delegate returns the underlying data

**ReturnCityTemperature >>**

```csharp
public static Func<CityTemperatureSearchRequest, CityTemperatureSearchResponse> ReturnCityTemperature = (CityTemperatureSearchRequest) =>

{
    switch (CityTemperatureSearchRequest.City)
    {

        case "Mumbai":

            return new CityTemperatureSearchResponse
            {

                City = "Mumbai",
                Temperature = "40"
            };

            break;

        case "Pune":

            return new CityTemperatureSearchResponse
            {

                City = "Pune",
                Temperature = "41"
            };

            break;

        case "Delhi":

            return new CityTemperatureSearchResponse
            {

                City = "Delhi",
                Temperature = "42"
            };

            break;

        case "Chennai":

            return new CityTemperatureSearchResponse
            {

                City = "Chennai",
                Temperature = "43"
            };
            break;
    }

    return new CityTemperatureSearchResponse
    {

        City = CityTemperatureSearchRequest.City,
        Temperature = "Unknown"
    };

};
```

Register the above delegate function with the DI container as an **AIFunction** along with the corresponding **SerializerOptions** that were defined earlier.

```csharp
  builder.Services.AddSingleton <AIFunction> (sp => {
   return AIFunctionFactory.Create(ReturnCityTemperature, new AIFunctionFactoryOptions {
     Name = "ReturnCityTemperature", Description = "Gets the temperature of a city", SerializerOptions = CityTemperatureSerializerContext.Default.Options
   });
 });
```

**Register the MCP Server >>**

MCP server is added to the service collection and with streamable HTTP configured through **WithHttpTransport** extension and mapped to **"api/mcp".**

```csharp
builder.Services.AddMcpServer()
.WithHttpTransport(options =>
{      
    options.Stateless = true;
}
);
    WebApplication app = builder.Build();        
    app.MapMcp(pattern: "api/mcp");
    app.Run();
```

There is a very important extension called **.WithToolsFromAssembly()** that auto discovers all classes and tools annotated with **\[McpServerToolType\]** and \[**McpServerTool\]** respectively.

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/40524c41-905e-46cf-a2b6-6f55e35f38f8.png align="left")

which unfortunately will be of no use for me, as my tools are DI registered.

Now, here comes the interesting and the important aspect.

How to expose all the DI registered tools through the MCP Server ?

This is how to do it

```csharp
builder.Services.AddSingleton < McpServerTool > (sp => {
  var chatClientList = sp.GetRequiredKeyedService < IChatClient > ("ChatClient");

  var aifunctions = sp.GetServices < AIFunction > ();

  List < AITool > functions = new(aifunctions);

  var options = new ChatClientAgentOptions {
    ChatOptions = new ChatOptions {

      Tools = functions
    }
  };
  var agent = chatClientList.AsAIAgent(options);

  return McpServerTool.Create(agent.AsAIFunction(new AIFunctionFactoryOptions {
    Name = "WeatherAgent"
  }));

});

builder.Services.AddMcpServer()
  .WithHttpTransport(options => {
    options.Stateless = true;

  });

WebApplication app = builder.Build();
app.MapMcp(pattern: "api/mcp");
app.Run();
```

**Lets dig into the above code step by step >>**

Register `McpServerTool` as a singleton in the DI container.

```csharp
builder.Services.AddSingleton<McpServerTool>
```

Fetch keyed **IChatClient** and **AIFunction** from the DI container.

Add them to the collection variable **functions** of type **List**

```csharp
var chatClientList = sp.GetRequiredKeyedService<IChatClient>("ChatClient");

  var aifunctions = sp.GetServices<AIFunction>();

  List<AITool> functions = new(aifunctions);

  var options = new ChatClientAgentOptions
  {
      ChatOptions = new ChatOptions
      {
          Tools = functions
      }
  };

  var agent = chatClientList.AsAIAgent(options);
```

Set **ChatClientAgentOptions** and assign functions variable to **Tools** property in **ChatOptions** and then assign it to chatClientList with an **.AsAIAgent()** extension method chain to create an AIAgent.

```csharp
var agent = chatClientList.AsAIAgent(options);
```

Now return the agent as an AIFunction with the required options

```csharp
return McpServerTool.Create(agent.AsAIFunction(new AIFunctionFactoryOptions
   {
       Name = "WeatherAgent"
   }));
```

Now things even get more interesting.

Let me send a prompt that queries the underlying data.

```csharp
What is the temperature in Pune?
```

The result is just the response text with no additional details that you would normally expect from an agent response.

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/737eb586-ddc3-410e-9505-fd80c1ce3f22.png align="center")

Now, let me make a few changes in how I expose the agent to the MCP Server.

```csharp
    builder.Services.AddSingleton<McpServerTool>(sp =>
   {
       var chatClientList = sp.GetRequiredKeyedService<IChatClient>("ChatClient");

       var aifunctions = sp.GetServices<AIFunction>();

       List<AITool> functions = new(aifunctions);

       var options = new ChatClientAgentOptions
       {
           ChatOptions = new ChatOptions
           {

               Tools = functions
           }
       };
       var agent = chatClientList.AsAIAgent(options);

      var agentFunction = AIFunctionFactory.Create(

     async (string message) =>
     {
         var response = await agent.RunAsync(message);

         return response;
     },
     new AIFunctionFactoryOptions
     {
         Name = "WeatherAgent"
     });

       return McpServerTool.Create(agentFunction);

   });

    builder.Services.AddMcpServer()
   .WithHttpTransport(options =>
   {
       options.Stateless = true;

   }
   );

    WebApplication app = builder.Build();
    app.MapMcp(pattern: "api/mcp");
    app.Run();
}
```

So the changes that I have done is that I am executing the agent by capturing the user i/p and agent response in the AIFunction itself and then exposing the AIFunction(agentFunction) to the McpServerTool.

I created an on the fly AIFunction that takes user input (message argument) and executed the agent within the AIFunction and it then returns the agent response.

Now instead of passing the agent as AIFunction (agent.AsAIFunction) to the MCPServerTool, I am sending the agentFunction to it

```csharp
 var agentFunction = AIFunctionFactory.Create(

  async (string message) =>
  {
      var response = await agent.RunAsync(message);

      return response;
  },
  new AIFunctionFactoryOptions
  {
      Name = "WeatherAgent"
  });

    return McpServerTool.Create(agentFunction);

});
```

and now try the execution with the same prompt that I tried earlier

```csharp
What is the temperature in Pune?
```

and this time the agent output format changes, which is lot more detailed

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/f2424a70-db9b-4c01-ad70-7e2baf5f47c8.png align="center")

The reason for this behavior ? Honestly I don't know :).

I only found out this behavior by accident. I will have to put in some time to understand this behavior.

Lets now look into how to configure MCP to test the agent.

Once MCP inspector is installed and running, click the **Add Server** option on top right and select **Add manually** option

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/652cfb16-d530-452c-a024-3072b49cba3d.png align="center")

In the pop up that follows, select **streamable-http**

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/9a377730-0d0f-449a-aa56-e13270088113.png align="center")

Select **WithHttpTransport** because we have used this option to set our MCP Server

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/e9421cc7-69ad-4a24-9b0a-6d9fa749b287.png align="left")

In the next pop up, add the project endpoint

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/cce4e003-57c0-423c-ac24-1a2d149207cf.png align="left")

In this example its running on : **http://localhost:5176**

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/ba1f2644-cc4b-4e74-a534-d4219a1807ae.png align="center")

Under Servers option the registered MCP server should be visible.

If everything has been configured properly the server should display the Connected status which the indicates the client has successfully connected to the server.

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/b6ab2640-240d-4d36-ab89-b04b2a1c56a8.png align="center")

Select the Tools option and it will list the Name of the AIFunction that was used to set the MCPServerTool.

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/355c5e91-4031-4a11-a99f-1ef799b69731.png align="left")

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/9bf4016b-3aaf-480d-a665-d35157f17b4a.png align="center")

Run the prompt and now you will have a more detailed output.

![MCP and Microsoft Agent Framework](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/0ffa491f-dcc6-447c-8127-26de416e9fea.png align="center")

### Execution

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/9cafc526-642f-4cd7-a1d8-8d76cd8e2e7d.gif align="center")

### Final Take

Though this is an edge case there might be possibility that you might face such a situation where your agentic architecture revolves more around DI pattern.

Rather than introducing a completely separate approach for exposing the agent through MCP, you can leverage the existing DI-based approach that i have suggested in this article to integrate MCP with it. This allows the agent to remain consistent with the application's DI pattern while making it compatible with the MCP requirements.

Thanks for reading !!!
