forked from microsoft/semantic-kernel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBaseSample.cs
137 lines (119 loc) · 4.99 KB
/
BaseSample.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using ModelContextProtocol;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;
using ModelContextProtocol.Protocol.Types;
namespace MCPClient.Samples;
internal abstract class BaseSample
{
/// <summary>
/// Creates an MCP client and connects it to the MCPServer server.
/// </summary>
/// <param name="kernel">Optional kernel instance to use for the MCP client.</param>
/// <param name="samplingRequestHandler">Optional handler for MCP sampling requests.</param>
/// <returns>An instance of <see cref="IMcpClient"/>.</returns>
protected static Task<IMcpClient> CreateMcpClientAsync(
Kernel? kernel = null,
Func<Kernel, CreateMessageRequestParams?, IProgress<ProgressNotificationValue>, CancellationToken, Task<CreateMessageResult>>? samplingRequestHandler = null)
{
KernelFunction? skSamplingHandler = null;
// Create and return the MCP client
return McpClientFactory.CreateAsync(
clientTransport: new StdioClientTransport(new StdioClientTransportOptions
{
Name = "MCPServer",
Command = GetMCPServerPath(), // Path to the MCPServer executable
}),
clientOptions: samplingRequestHandler != null ? new McpClientOptions()
{
Capabilities = new ClientCapabilities
{
Sampling = new SamplingCapability
{
SamplingHandler = InvokeHandlerAsync
},
},
} : null
);
async ValueTask<CreateMessageResult> InvokeHandlerAsync(CreateMessageRequestParams? request, IProgress<ProgressNotificationValue> progress, CancellationToken cancellationToken)
{
if (request is null)
{
throw new ArgumentNullException(nameof(request));
}
skSamplingHandler ??= KernelFunctionFactory.CreateFromMethod(
(CreateMessageRequestParams? request, IProgress<ProgressNotificationValue> progress, CancellationToken ct) =>
{
return samplingRequestHandler(kernel!, request, progress, ct);
},
"MCPSamplingHandler"
);
// The argument names must match the parameter names of the delegate the SK Function is created from
KernelArguments kernelArguments = new()
{
["request"] = request,
["progress"] = progress
};
FunctionResult functionResult = await skSamplingHandler.InvokeAsync(kernel!, kernelArguments, cancellationToken);
return functionResult.GetValue<CreateMessageResult>()!;
}
}
/// <summary>
/// Creates an instance of <see cref="Kernel"/> with the OpenAI chat completion service registered.
/// </summary>
/// <returns>An instance of <see cref="Kernel"/>.</returns>
protected static Kernel CreateKernelWithChatCompletionService()
{
// Load and validate configuration
IConfigurationRoot config = new ConfigurationBuilder()
.AddUserSecrets<Program>()
.AddEnvironmentVariables()
.Build();
if (config["OpenAI:ApiKey"] is not { } apiKey)
{
const string Message = "Please provide a valid OpenAI:ApiKey to run this sample. See the associated README.md for more details.";
Console.Error.WriteLine(Message);
throw new InvalidOperationException(Message);
}
string modelId = config["OpenAI:ChatModelId"] ?? "gpt-4o-mini";
// Create kernel
var kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.Services.AddOpenAIChatCompletion(modelId: modelId, apiKey: apiKey);
return kernelBuilder.Build();
}
/// <summary>
/// Displays the list of available MCP tools.
/// </summary>
/// <param name="tools">The list of the tools to display.</param>
protected static void DisplayTools(IList<McpClientTool> tools)
{
Console.WriteLine("Available MCP tools:");
foreach (var tool in tools)
{
Console.WriteLine($"- Name: {tool.Name}, Description: {tool.Description}");
}
Console.WriteLine();
}
/// <summary>
/// Returns the path to the MCPServer server executable.
/// </summary>
/// <returns>The path to the MCPServer server executable.</returns>
private static string GetMCPServerPath()
{
// Determine the configuration (Debug or Release)
string configuration;
#if DEBUG
configuration = "Debug";
#else
configuration = "Release";
#endif
return Path.Combine("..", "..", "..", "..", "MCPServer", "bin", configuration, "net8.0", "MCPServer.exe");
}
}