Skip to content

Commit a6db655

Browse files
SergeyMenshykhglorious-beard
authored andcommitted
.Net: Refactor MCP client for improved code readability (microsoft#11306)
### Motivation, Context and Description Refactor the MCPClient to improve code readability by moving details unrelated to MCP into private methods.
1 parent 924bf3b commit a6db655

File tree

1 file changed

+70
-41
lines changed
  • dotnet/samples/Demos/ModelContextProtocolClientServer/MCPClient

1 file changed

+70
-41
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

33
using System;
4+
using System.Collections.Generic;
45
using System.IO;
56
using System.Linq;
67
using System.Threading.Tasks;
78
using Microsoft.Extensions.Configuration;
8-
using Microsoft.Extensions.DependencyInjection;
9-
using Microsoft.Extensions.Logging;
109
using Microsoft.SemanticKernel;
1110
using Microsoft.SemanticKernel.Connectors.OpenAI;
1211
using ModelContextProtocol;
@@ -18,21 +17,71 @@ namespace MCPClient;
1817
internal sealed class Program
1918
{
2019
public static async Task Main(string[] args)
20+
{
21+
// Create an MCP client
22+
await using IMcpClient mcpClient = await CreateMcpClientAsync();
23+
24+
// Retrieve and display the list provided by the MCP server
25+
IList<McpClientTool> tools = await mcpClient.ListToolsAsync();
26+
DisplayTools(tools);
27+
28+
// Create a kernel and register the MCP tools
29+
Kernel kernel = CreateKernelWithChatCompletionService();
30+
kernel.Plugins.AddFromFunctions("Tools", tools.Select(aiFunction => aiFunction.AsKernelFunction()));
31+
32+
// Enable automatic function calling
33+
OpenAIPromptExecutionSettings executionSettings = new()
34+
{
35+
Temperature = 0,
36+
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })
37+
};
38+
39+
string prompt = "What is the likely color of the sky in Boston today?";
40+
Console.WriteLine(prompt);
41+
42+
// Execute a prompt using the MCP tools. The AI model will automatically call the appropriate MCP tools to answer the prompt.
43+
FunctionResult result = await kernel.InvokePromptAsync(prompt, new(executionSettings));
44+
45+
Console.WriteLine(result);
46+
47+
// The expected output is: The likely color of the sky in Boston today is gray, as it is currently rainy.
48+
}
49+
50+
/// <summary>
51+
/// Creates an instance of <see cref="Kernel"/> with the OpenAI chat completion service registered.
52+
/// </summary>
53+
/// <returns>An instance of <see cref="Kernel"/>.</returns>
54+
private static Kernel CreateKernelWithChatCompletionService()
2155
{
2256
// Load and validate configuration
23-
var config = new ConfigurationBuilder()
57+
IConfigurationRoot config = new ConfigurationBuilder()
2458
.AddUserSecrets<Program>()
2559
.AddEnvironmentVariables()
2660
.Build();
2761

2862
if (config["OpenAI:ApiKey"] is not { } apiKey)
2963
{
30-
Console.Error.WriteLine("Please provide a valid OpenAI:ApiKey to run this sample. See the associated README.md for more details.");
31-
return;
64+
const string Message = "Please provide a valid OpenAI:ApiKey to run this sample. See the associated README.md for more details.";
65+
Console.Error.WriteLine(Message);
66+
throw new InvalidOperationException(Message);
3267
}
3368

34-
// Create an MCP client
35-
await using var mcpClient = await McpClientFactory.CreateAsync(
69+
string modelId = config["OpenAI:ChatModelId"] ?? "gpt-4o-mini";
70+
71+
// Create kernel
72+
var kernelBuilder = Kernel.CreateBuilder();
73+
kernelBuilder.Services.AddOpenAIChatCompletion(serviceId: "openai", modelId: modelId, apiKey: apiKey);
74+
75+
return kernelBuilder.Build();
76+
}
77+
78+
/// <summary>
79+
/// Creates an MCP client and connects it to the MCPServer server.
80+
/// </summary>
81+
/// <returns>An instance of <see cref="IMcpClient"/>.</returns>
82+
private static Task<IMcpClient> CreateMcpClientAsync()
83+
{
84+
return McpClientFactory.CreateAsync(
3685
new McpServerConfig()
3786
{
3887
Id = "MCPServer",
@@ -48,40 +97,7 @@ public static async Task Main(string[] args)
4897
{
4998
ClientInfo = new() { Name = "MCPClient", Version = "1.0.0" }
5099
}
51-
);
52-
53-
// Retrieve and display the list of tools available on the MCP server
54-
Console.WriteLine("Available MCP tools:");
55-
var tools = await mcpClient.ListToolsAsync().ConfigureAwait(false);
56-
foreach (var tool in tools)
57-
{
58-
Console.WriteLine($"{tool.Name}: {tool.Description}");
59-
}
60-
61-
// Prepare and build kernel with the MCP tools as Kernel functions
62-
var kernelBuilder = Kernel.CreateBuilder();
63-
kernelBuilder.Plugins.AddFromFunctions("Tools", tools.Select(aiFunction => aiFunction.AsKernelFunction()));
64-
kernelBuilder.Services
65-
.AddLogging(c => c.AddDebug().SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace))
66-
.AddOpenAIChatCompletion(serviceId: "openai", modelId: config["OpenAI:ChatModelId"] ?? "gpt-4o-mini", apiKey: apiKey);
67-
68-
Kernel kernel = kernelBuilder.Build();
69-
70-
// Enable automatic function calling
71-
OpenAIPromptExecutionSettings executionSettings = new()
72-
{
73-
Temperature = 0,
74-
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })
75-
};
76-
77-
// Execute a prompt using the MCP tools. The AI model will automatically call the appropriate MCP tools to answer the prompt.
78-
var prompt = "What is the likely color of the sky in Boston today?";
79-
var result = await kernel.InvokePromptAsync(prompt, new(executionSettings)).ConfigureAwait(false);
80-
Console.WriteLine($"\n\n{prompt}\n{result}");
81-
82-
// The expected output is:
83-
// What is the likely color of the sky in Boston today?
84-
// The likely color of the sky in Boston today is gray, as it is currently rainy.
100+
);
85101
}
86102

87103
/// <summary>
@@ -101,4 +117,17 @@ private static string GetMCPServerPath()
101117

102118
return Path.Combine("..", "..", "..", "..", "MCPServer", "bin", configuration, "net8.0", "MCPServer.exe");
103119
}
120+
121+
/// <summary>
122+
/// Displays the list of available MCP tools.
123+
/// </summary>
124+
/// <param name="tools">The list of the tools to display.</param>
125+
private static void DisplayTools(IList<McpClientTool> tools)
126+
{
127+
Console.WriteLine("Available MCP tools:");
128+
foreach (var tool in tools)
129+
{
130+
Console.WriteLine($"- {tool.Name}: {tool.Description}");
131+
}
132+
}
104133
}

0 commit comments

Comments
 (0)