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