From dbc961c869b50aca6d29da44a74c35023d343736 Mon Sep 17 00:00:00 2001
From: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
Date: Fri, 5 Sep 2025 10:57:30 +0100
Subject: [PATCH] .NET: AI agent as an MCP tool (#612)
* add sample demostrating how to expose ai-agent as an mcp tool
* Update dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Program.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update dotnet/samples/GettingStarted/Agents/README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* remove threadId parameter
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
dotnet/Directory.Packages.props | 1 +
dotnet/agent-framework-dotnet.slnx | 1 +
.../AIAgentExtensions.cs | 42 +++++++++++++++++++
.../Agent_Step10_AsMcpTool.csproj | 26 ++++++++++++
.../Agents/Agent_Step10_AsMcpTool/Program.cs | 37 ++++++++++++++++
.../Agents/Agent_Step10_AsMcpTool/README.md | 29 +++++++++++++
.../samples/GettingStarted/Agents/README.md | 1 +
7 files changed, 137 insertions(+)
create mode 100644 dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/AIAgentExtensions.cs
create mode 100644 dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj
create mode 100644 dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Program.cs
create mode 100644 dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/README.md
diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index 7b3464dc5e..85769f892d 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -76,6 +76,7 @@
+
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 6e5ba32c90..cd77622784 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -40,6 +40,7 @@
+
diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/AIAgentExtensions.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/AIAgentExtensions.cs
new file mode 100644
index 0000000000..98f2a9734f
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/AIAgentExtensions.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ComponentModel;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.AI.Agents;
+using ModelContextProtocol.Server;
+
+namespace Agent_Step10_AsMcpTool;
+
+///
+/// Contains extension methods for .
+///
+internal static class AIAgentExtensions
+{
+ ///
+ /// Exposes an as a that can be registered with an MCP server.
+ ///
+ /// The agent to expose as an MCP tool.
+ /// A human-readable title for the tool that can be displayed to users. If not provided, the tool name or agent name will be used.
+ /// The tool name to use. If not provided, the agent's name will be used.
+ /// The tool description to use. If not provided, the agent's description will be used.
+ /// The that wraps the agent.
+ public static McpServerTool AsMcpTool(this AIAgent agent, string? title = null, string? name = null, string? description = null)
+ {
+ async Task RunAgentAsync(
+ [Description("Available information that will guide in performing this operation.")] string query,
+ CancellationToken cancellationToken = default)
+ {
+ AgentRunResponse response = await agent.RunAsync(query, cancellationToken: cancellationToken);
+
+ return response.ToString();
+ }
+
+ return McpServerTool.Create(RunAgentAsync, new McpServerToolCreateOptions()
+ {
+ Title = title ?? name ?? agent.Name,
+ Name = name ?? agent.Name,
+ Description = description ?? agent.Description
+ });
+ }
+}
diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj b/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj
new file mode 100644
index 0000000000..cdb063750c
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Agent_Step10_AsMcpTool.csproj
@@ -0,0 +1,26 @@
+
+
+
+ Exe
+ net9.0
+ 12
+
+ enable
+ disable
+ 3afc9b74-af74-4d8e-ae96-fa1c511d11ac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Program.cs
new file mode 100644
index 0000000000..080462f1c6
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/Program.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample shows how to expose an AI agent as an MCP tool.
+
+using System;
+using Agent_Step10_AsMcpTool;
+using Azure.AI.Agents.Persistent;
+using Azure.Identity;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
+var deploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
+
+const string JokerName = "Joker";
+const string JokerInstructions = "You are good at telling jokes, and you always start each joke with 'Aye aye, captain!'.";
+
+var persistentAgentsClient = new PersistentAgentsClient(endpoint, new AzureCliCredential());
+
+// Create a server side persistent agent
+var agentMetadata = await persistentAgentsClient.Administration.CreateAgentAsync(
+ model: deploymentName,
+ name: JokerName,
+ instructions: JokerInstructions);
+
+// Retrieve the server side persistent agent as an AIAgent.
+AIAgent agent = await persistentAgentsClient.GetAIAgentAsync(agentMetadata.Value.Id);
+
+// Register the MCP server with StdIO transport and expose the agent as an MCP tool.
+HostApplicationBuilder builder = Host.CreateEmptyApplicationBuilder(settings: null);
+builder.Services
+ .AddMcpServer()
+ .WithStdioServerTransport()
+ .WithTools([agent.AsMcpTool()]);
+
+await builder.Build().RunAsync();
diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/README.md b/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/README.md
new file mode 100644
index 0000000000..c56c9a7a68
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Agents/Agent_Step10_AsMcpTool/README.md
@@ -0,0 +1,29 @@
+This sample demonstrates how to expose an existing AI agent as an MCP tool.
+
+## Run the sample
+
+To run the sample, please use one of the following MCP clients: https://modelcontextprotocol.io/clients
+
+Alternatively, use the QuickstartClient sample from this repository: https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/QuickstartClient
+
+## Run the sample using MCP Inspector
+
+To use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector), follow these steps:
+
+1. Open a terminal in the Agent_Step10_AsMcpTool project directory.
+1. Run the `npx @modelcontextprotocol/inspector dotnet run` command to start the MCP Inspector. Make sure you have [node.js](https://nodejs.org/en/download/) and npm installed.
+ ```bash
+ npx @modelcontextprotocol/inspector dotnet run
+ ```
+1. When the inspector is running, it will display a URL in the terminal, like this:
+ ```
+ MCP Inspector is up and running at http://127.0.0.1:6274
+ ```
+1. Open a web browser and navigate to the URL displayed in the terminal. If not opened automatically, this will open the MCP Inspector interface.
+1. In the MCP Inspector interface, add the following environment variables to allow your MCP server to access Azure AI Foundry Project to create and run the agent:
+ - AZURE_FOUNDRY_PROJECT_ENDPOINT = https://your-resource.openai.azure.com/ # Replace with your Azure AI Foundry Project endpoint
+ - AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME = gpt-4o-mini # Replace with your model deployment name
+1. Find and click the `Connect` button in the MCP Inspector interface to connect to the MCP server.
+1. As soon as the connection is established, open the `Tools` tab in the MCP Inspector interface and select the `Joker` tool from the list.
+1. Specify your prompt as a value for the `query` argument, for example: `Tell me a joke about a pirate` and click the `Run Tool` button to run the tool.
+1. The agent will process the request and return a response in accordance with the provided instructions that instruct it to always start each joke with 'Aye aye, captain!'.
\ No newline at end of file
diff --git a/dotnet/samples/GettingStarted/Agents/README.md b/dotnet/samples/GettingStarted/Agents/README.md
index bada46cd12..03e9edc247 100644
--- a/dotnet/samples/GettingStarted/Agents/README.md
+++ b/dotnet/samples/GettingStarted/Agents/README.md
@@ -35,6 +35,7 @@ Before you begin, ensure you have the following prerequisites:
|[3rd party thread storage with a simple agent](./Agent_Step07_3rdPartyThreadStorage/)|This sample demonstrates how to store conversation history in a 3rd party storage solution|
|[Telemetry with a simple agent](./Agent_Step08_Telemetry/)|This sample demonstrates how to add telemetry to a simple agent|
|[Dependency injection with a simple agent](./Agent_Step09_DependencyInjection/)|This sample demonstrates how to add and resolve an agent with a dependency injection container|
+|[Exposing a simple agent as MCP tool](./Agent_Step10_AsMcpTool/)|This sample demonstrates how to expose an agent as an MCP tool|
## Running the samples from the console