chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using Microsoft.SemanticKernel.ChatCompletion;
using ModelContextProtocol.Protocol;
namespace MCPClient;
/// <summary>
/// Extension methods for the <see cref="AuthorRole"/>.
/// </summary>
internal static class AuthorRoleExtensions
{
/// <summary>
/// Converts a <see cref="AuthorRole"/> to a <see cref="Role"/>.
/// </summary>
/// <param name="role">The author role to convert.</param>
/// <returns>The corresponding <see cref="Role"/>.</returns>
public static Role ToMCPRole(this AuthorRole role)
{
if (role == AuthorRole.User)
{
return Role.User;
}
if (role == AuthorRole.Assistant)
{
return Role.Assistant;
}
throw new InvalidOperationException($"Unexpected role '{role}'");
}
}
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Linq;
using Microsoft.SemanticKernel;
using ModelContextProtocol.Protocol;
namespace MCPClient;
/// <summary>
/// Extension methods for <see cref="ChatMessageContent"/>.
/// </summary>
public static class ChatMessageContentExtensions
{
/// <summary>
/// Converts a <see cref="ChatMessageContent"/> to a <see cref="CreateMessageResult"/>.
/// </summary>
/// <param name="chatMessageContent">The <see cref="ChatMessageContent"/> to convert.</param>
/// <returns>The corresponding <see cref="CreateMessageResult"/>.</returns>
public static CreateMessageResult ToCreateMessageResult(this ChatMessageContent chatMessageContent)
{
// Using the same heuristic as in the original MCP SDK code: McpClientExtensions.ToCreateMessageResult for consistency.
// ChatMessageContent can contain multiple items of different modalities, while the CreateMessageResult
// can only have a single content type: text, image, or audio. First, look for image or audio content,
// and if not found, fall back to the text content type by concatenating the text of all text contents.
ContentBlock? content = null;
foreach (KernelContent item in chatMessageContent.Items)
{
if (item is ImageContent image)
{
content = new ImageContentBlock
{
Data = Convert.ToBase64String(image.Data!.Value.Span),
MimeType = image.MimeType ?? "image/jpeg"
};
break;
}
else if (item is AudioContent audio)
{
content = new AudioContentBlock
{
Data = Convert.ToBase64String(audio.Data!.Value.Span),
MimeType = audio.MimeType ?? "audio/mpeg"
};
break;
}
}
content ??= new TextContentBlock
{
Text = string.Concat(chatMessageContent.Items.OfType<TextContent>()),
};
return new CreateMessageResult
{
Role = chatMessageContent.Role.ToMCPRole(),
Model = chatMessageContent.ModelId ?? "unknown",
Content = content
};
}
}
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using Microsoft.SemanticKernel;
using ModelContextProtocol.Protocol;
namespace MCPClient;
/// <summary>
/// Extension methods for the <see cref="ContentBlock"/> class.
/// </summary>
public static class ContentBlockExtensions
{
/// <summary>
/// Converts a <see cref="ContentBlock"/> object to a <see cref="KernelContent"/> object.
/// </summary>
/// <param name="content">The <see cref="ContentBlock"/> object to convert.</param>
/// <returns>The corresponding <see cref="KernelContent"/> object.</returns>
public static KernelContent ToKernelContent(this ContentBlock content)
{
return content switch
{
TextContentBlock textContentBlock => new TextContent(textContentBlock.Text),
ImageContentBlock imageContentBlock => new ImageContent(Convert.FromBase64String(imageContentBlock.Data!), imageContentBlock.MimeType),
AudioContentBlock audioContentBlock => new AudioContent(Convert.FromBase64String(audioContentBlock.Data!), audioContentBlock.MimeType),
_ => throw new InvalidOperationException($"Unexpected message content type '{content.Type}'"),
};
}
}
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using ModelContextProtocol.Protocol;
namespace MCPClient;
/// <summary>
/// Extension methods for <see cref="GetPromptResult"/>.
/// </summary>
internal static class PromptResultExtensions
{
/// <summary>
/// Converts a <see cref="GetPromptResult"/> to chat message contents.
/// </summary>
/// <param name="result">The prompt result to convert.</param>
/// <returns>The corresponding <see cref="ChatHistory"/>.</returns>
public static IList<ChatMessageContent> ToChatMessageContents(this GetPromptResult result)
{
return [.. result.Messages.Select(ToChatMessageContent)];
}
/// <summary>
/// Converts a <see cref="PromptMessage"/> to a <see cref="ChatMessageContent"/>.
/// </summary>
/// <param name="message">The <see cref="PromptMessage"/> to convert.</param>
/// <returns>The corresponding <see cref="ChatMessageContent"/>.</returns>
public static ChatMessageContent ToChatMessageContent(this PromptMessage message)
{
return new ChatMessageContent(role: message.Role.ToAuthorRole(), items: [message.Content.ToKernelContent()]);
}
}
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using ModelContextProtocol.Protocol;
namespace MCPClient;
/// <summary>
/// Extension methods for <see cref="ReadResourceResult"/>.
/// </summary>
public static class ReadResourceResultExtensions
{
/// <summary>
/// Converts a <see cref="ReadResourceResult"/> to a <see cref="ChatMessageContentItemCollection"/>.
/// </summary>
/// <param name="readResourceResult">The MCP read resource result to convert.</param>
/// <returns>The corresponding <see cref="ChatMessageContentItemCollection"/>.</returns>
public static ChatMessageContentItemCollection ToChatMessageContentItemCollection(this ReadResourceResult readResourceResult)
{
if (readResourceResult.Contents.Count == 0)
{
throw new InvalidOperationException("The resource does not contain any contents.");
}
ChatMessageContentItemCollection result = [];
foreach (var resourceContent in readResourceResult.Contents)
{
Dictionary<string, object?> metadata = new()
{
["uri"] = resourceContent.Uri
};
if (resourceContent is TextResourceContents textResourceContent)
{
result.Add(new TextContent()
{
Text = textResourceContent.Text,
MimeType = textResourceContent.MimeType,
Metadata = metadata,
});
}
else if (resourceContent is BlobResourceContents blobResourceContent)
{
if (blobResourceContent.MimeType?.StartsWith("image", System.StringComparison.InvariantCulture) ?? false)
{
result.Add(new ImageContent()
{
Data = Convert.FromBase64String(blobResourceContent.Blob),
MimeType = blobResourceContent.MimeType,
Metadata = metadata,
});
}
else if (blobResourceContent.MimeType?.StartsWith("audio", System.StringComparison.InvariantCulture) ?? false)
{
result.Add(new AudioContent
{
Data = Convert.FromBase64String(blobResourceContent.Blob),
MimeType = blobResourceContent.MimeType,
Metadata = metadata,
});
}
else
{
result.Add(new BinaryContent
{
Data = Convert.FromBase64String(blobResourceContent.Blob),
MimeType = blobResourceContent.MimeType,
Metadata = metadata,
});
}
}
}
return result;
}
}
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using Microsoft.SemanticKernel.ChatCompletion;
using ModelContextProtocol.Protocol;
namespace MCPClient;
/// <summary>
/// Extension methods for the <see cref="Role"/> enum.
/// </summary>
internal static class RoleExtensions
{
/// <summary>
/// Converts a <see cref="Role"/> to a <see cref="AuthorRole"/>.
/// </summary>
/// <param name="role">The MCP role to convert.</param>
/// <returns>The corresponding <see cref="AuthorRole"/>.</returns>
public static AuthorRole ToAuthorRole(this Role role)
{
return role switch
{
Role.User => AuthorRole.User,
Role.Assistant => AuthorRole.Assistant,
_ => throw new InvalidOperationException($"Unexpected role '{role}'")
};
}
}
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using Microsoft.SemanticKernel;
using ModelContextProtocol.Protocol;
namespace MCPClient;
/// <summary>
/// Extension methods for <see cref="SamplingMessage"/>.
/// </summary>
public static class SamplingMessageExtensions
{
/// <summary>
/// Converts a collection of <see cref="SamplingMessage"/> to a list of <see cref="ChatMessageContent"/>.
/// </summary>
/// <param name="samplingMessages">The collection of <see cref="SamplingMessage"/> to convert.</param>
/// <returns>The corresponding list of <see cref="ChatMessageContent"/>.</returns>
public static List<ChatMessageContent> ToChatMessageContents(this IEnumerable<SamplingMessage> samplingMessages)
{
return [.. samplingMessages.Select(ToChatMessageContent)];
}
/// <summary>
/// Converts a <see cref="SamplingMessage"/> to a <see cref="ChatMessageContent"/>.
/// </summary>
/// <param name="message">The <see cref="SamplingMessage"/> to convert.</param>
/// <returns>The corresponding <see cref="ChatMessageContent"/>.</returns>
public static ChatMessageContent ToChatMessageContent(this SamplingMessage message)
{
return new ChatMessageContent(role: message.Role.ToAuthorRole(), items: [message.Content.ToKernelContent()]);
}
}
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using ModelContextProtocol.Protocol;
namespace MCPClient;
/// <summary>
/// A filter that intercepts function invocations to allow for human-in-the-loop processing.
/// </summary>
public class HumanInTheLoopFilter : IFunctionInvocationFilter
{
/// <inheritdoc />
public Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next)
{
// Intercept the MCP sampling handler before invoking it
if (context.Function.Name == "MCPSamplingHandler")
{
CreateMessageRequestParams request = (CreateMessageRequestParams)context.Arguments["request"]!;
if (!GetUserApprovalForSamplingMessages(request))
{
context.Result = new FunctionResult(context.Result, "Operation was rejected due to PII.");
return Task.CompletedTask;
}
}
// Proceed with the handler invocation
return next.Invoke(context);
}
/// <summary>
/// Checks if the user approves the messages for further sampling request processing.
/// </summary>
/// <remarks>
/// This method serves as a placeholder for the actual implementation, which may involve user interaction through a user interface.
/// The user will be presented with a list of messages and given two options: to approve or reject the request.
/// </remarks>
/// <param name="request">The sampling request.</param>
/// <returns>Returns true if the user approves; otherwise, false.</returns>
private static bool GetUserApprovalForSamplingMessages(CreateMessageRequestParams request)
{
// Approve the request
return true;
}
}
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
<NoWarn>$(NoWarn);CA2249;CS0612;SKEXP0001;SKEXP0110;VSTHRD111;CA2007</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ModelContextProtocol" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Agents\AzureAI\Agents.AzureAI.csproj" />
<ProjectReference Include="..\..\..\..\src\Agents\Core\Agents.Core.csproj" />
<ProjectReference Include="..\..\..\..\src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
<ProjectReference Include="..\..\..\..\src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
<ProjectReference Include="..\..\..\..\src\SemanticKernel.Core\SemanticKernel.Core.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Threading.Tasks;
using MCPClient.Samples;
namespace MCPClient;
internal sealed class Program
{
/// <summary>
/// Main method to run all the samples.
/// </summary>
public static async Task Main(string[] args)
{
await MCPToolsSample.RunAsync();
await MCPPromptSample.RunAsync();
await MCPResourcesSample.RunAsync();
await MCPResourceTemplatesSample.RunAsync();
await MCPSamplingSample.RunAsync();
await ChatCompletionAgentWithMCPToolsSample.RunAsync();
await AzureAIAgentWithMCPToolsSample.RunAsync();
await AgentAvailableAsMCPToolSample.RunAsync();
}
}
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using ModelContextProtocol.Client;
namespace MCPClient.Samples;
/// <summary>
/// Demonstrates how to use SK agent available as MCP tool.
/// </summary>
internal sealed class AgentAvailableAsMCPToolSample : BaseSample
{
/// <summary>
/// Demonstrates how to use SK agent available as MCP tool.
/// The code in this method:
/// 1. Creates an MCP client.
/// 2. Retrieves the list of tools provided by the MCP server.
/// 3. Creates a kernel and registers the MCP tools as Kernel functions.
/// 4. Sends the prompt to AI model together with the MCP tools represented as Kernel functions.
/// 5. The AI model calls the `Agents_SalesAssistant` function, which calls the MCP tool that calls the SK agent on the server.
/// 6. The agent calls the `OrderProcessingUtils-PlaceOrder` function to place the order for the `Grande Mug`.
/// 7. The agent calls the `OrderProcessingUtils-ReturnOrder` function to return the `Wide Rim Mug`.
/// 8. The agent summarizes the transactions and returns the result as part of the `Agents_SalesAssistant` function call.
/// 9. Having received the result from the `Agents_SalesAssistant`, the AI model returns the answer to the prompt.
/// </summary>
public static async Task RunAsync()
{
Console.WriteLine($"Running the {nameof(AgentAvailableAsMCPToolSample)} sample.");
// Create an MCP client
McpClient mcpClient = await CreateMcpClientAsync();
// Retrieve and display the list provided by the MCP server
IList<McpClientTool> tools = await mcpClient.ListToolsAsync();
DisplayTools(tools);
// Create a kernel and register the MCP tools
Kernel kernel = CreateKernelWithChatCompletionService();
kernel.Plugins.AddFromFunctions("Tools", tools.Select(aiFunction => aiFunction.AsKernelFunction()));
// Enable automatic function calling
OpenAIPromptExecutionSettings executionSettings = new()
{
Temperature = 0,
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })
};
string prompt = "I'd like to order the 'Grande Mug' and return the 'Wide Rim Mug' bought last week.";
Console.WriteLine(prompt);
// Execute a prompt using the MCP tools. The AI model will automatically call the appropriate MCP tools to answer the prompt.
FunctionResult result = await kernel.InvokePromptAsync(prompt, new(executionSettings));
Console.WriteLine(result);
Console.WriteLine();
// The expected output is: The order for the "Grande Mug" has been successfully placed.
// Additionally, the return process for the "Wide Rim Mug" has been successfully initiated.
// If you have any further questions or need assistance with anything else, feel free to ask!
}
}
@@ -0,0 +1,108 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.AzureAI;
using ModelContextProtocol.Client;
namespace MCPClient.Samples;
/// <summary>
/// Demonstrates how to use <see cref="AzureAIAgent"/> with MCP tools represented as Kernel functions.
/// </summary>
internal sealed class AzureAIAgentWithMCPToolsSample : BaseSample
{
/// <summary>
/// Demonstrates how to use <see cref="AzureAIAgent"/> with MCP tools represented as Kernel functions.
/// The code in this method:
/// 1. Creates an MCP client.
/// 2. Retrieves the list of tools provided by the MCP server.
/// 3. Creates a kernel and registers the MCP tools as Kernel functions.
/// 4. Defines Azure AI agent with instructions, name, kernel, and arguments.
/// 5. Invokes the agent with a prompt.
/// 6. The agent sends the prompt to the AI model, together with the MCP tools represented as Kernel functions.
/// 7. The AI model calls DateTimeUtils-GetCurrentDateTimeInUtc function to get the current date time in UTC required as an argument for the next function.
/// 8. The AI model calls WeatherUtils-GetWeatherForCity function with the current date time and the `Boston` arguments extracted from the prompt to get the weather information.
/// 9. Having received the weather information from the function call, the AI model returns the answer to the agent and the agent returns the answer to the user.
/// </summary>
public static async Task RunAsync()
{
Console.WriteLine($"Running the {nameof(AzureAIAgentWithMCPToolsSample)} sample.");
// Create an MCP client
McpClient mcpClient = await CreateMcpClientAsync();
// Retrieve and display the list provided by the MCP server
IList<McpClientTool> tools = await mcpClient.ListToolsAsync();
DisplayTools(tools);
// Create a kernel and register the MCP tools as Kernel functions
Kernel kernel = new();
kernel.Plugins.AddFromFunctions("Tools", tools.Select(aiFunction => aiFunction.AsKernelFunction()));
// Define the agent using the kernel with registered MCP tools
AzureAIAgent agent = await CreateAzureAIAgentAsync(
name: "WeatherAgent",
instructions: "Answer questions about the weather.",
kernel: kernel
);
// Invokes agent with a prompt
string prompt = "What is the likely color of the sky in Boston today?";
Console.WriteLine(prompt);
AgentResponseItem<ChatMessageContent> response = await agent.InvokeAsync(message: prompt).FirstAsync();
Console.WriteLine(response.Message);
Console.WriteLine();
// The expected output is: Today in Boston, the weather is 61°F and rainy. Due to the rain, the likely color of the sky will be gray.
// Delete the agent thread after use
await response!.Thread.DeleteAsync();
// Delete the agent after use
await agent.Client.Administration.DeleteAgentAsync(agent.Id);
}
/// <summary>
/// Creates an instance of <see cref="AzureAIAgent"/> with the specified name and instructions.
/// </summary>
/// <param name="kernel">The kernel instance.</param>
/// <param name="name">The name of the agent.</param>
/// <param name="instructions">The instructions for the agent.</param>
/// <returns>An instance of <see cref="AzureAIAgent"/>.</returns>
private static async Task<AzureAIAgent> CreateAzureAIAgentAsync(Kernel kernel, string name, string instructions)
{
// Load and validate configuration
IConfigurationRoot config = new ConfigurationBuilder()
.AddUserSecrets<Program>()
.AddEnvironmentVariables()
.Build();
if (config["AzureAI:Endpoint"] is not { } endpoint)
{
const string Message = "Please provide a valid `AzureAI:ConnectionString` secret to run this sample. See the associated README.md for more details.";
Console.Error.WriteLine(Message);
throw new InvalidOperationException(Message);
}
string modelId = config["AzureAI:ChatModelId"] ?? "gpt-4o-mini";
// Create the Azure AI Agent
PersistentAgentsClient agentsClient = AzureAIAgent.CreateAgentsClient(endpoint, new AzureCliCredential());
PersistentAgent agent = await agentsClient.Administration.CreateAgentAsync(modelId, name, null, instructions);
return new AzureAIAgent(agent, agentsClient)
{
Kernel = kernel
};
}
}
@@ -0,0 +1,133 @@
// 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;
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<McpClient> CreateMcpClientAsync(
Kernel? kernel = null,
Func<Kernel, CreateMessageRequestParams?, IProgress<ProgressNotificationValue>, CancellationToken, Task<CreateMessageResult>>? samplingRequestHandler = null)
{
KernelFunction? skSamplingHandler = null;
// Create and return the MCP client
return McpClient.CreateAsync(
clientTransport: new StdioClientTransport(new StdioClientTransportOptions
{
Name = "MCPServer",
Command = GetMCPServerPath(), // Path to the MCPServer executable
}),
clientOptions: samplingRequestHandler != null ? new McpClientOptions()
{
Handlers = new()
{
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");
}
}
@@ -0,0 +1,73 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using ModelContextProtocol.Client;
namespace MCPClient.Samples;
/// <summary>
/// Demonstrates how to use <see cref="ChatCompletionAgent"/> with MCP tools represented as Kernel functions.
/// </summary>
internal sealed class ChatCompletionAgentWithMCPToolsSample : BaseSample
{
/// <summary>
/// Demonstrates how to use <see cref="ChatCompletionAgent"/> with MCP tools represented as Kernel functions.
/// The code in this method:
/// 1. Creates an MCP client.
/// 2. Retrieves the list of tools provided by the MCP server.
/// 3. Creates a kernel and registers the MCP tools as Kernel functions.
/// 4. Defines chat completion agent with instructions, name, kernel, and arguments.
/// 5. Invokes the agent with a prompt.
/// 6. The agent sends the prompt to the AI model, together with the MCP tools represented as Kernel functions.
/// 7. The AI model calls DateTimeUtils-GetCurrentDateTimeInUtc function to get the current date time in UTC required as an argument for the next function.
/// 8. The AI model calls WeatherUtils-GetWeatherForCity function with the current date time and the `Boston` arguments extracted from the prompt to get the weather information.
/// 9. Having received the weather information from the function call, the AI model returns the answer to the agent and the agent returns the answer to the user.
/// </summary>
public static async Task RunAsync()
{
Console.WriteLine($"Running the {nameof(ChatCompletionAgentWithMCPToolsSample)} sample.");
// Create an MCP client
McpClient mcpClient = await CreateMcpClientAsync();
// Retrieve and display the list provided by the MCP server
IList<McpClientTool> tools = await mcpClient.ListToolsAsync();
DisplayTools(tools);
// Create a kernel and register the MCP tools as kernel functions
Kernel kernel = CreateKernelWithChatCompletionService();
kernel.Plugins.AddFromFunctions("Tools", tools.Select(aiFunction => aiFunction.AsKernelFunction()));
// Enable automatic function calling
OpenAIPromptExecutionSettings executionSettings = new()
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })
};
string prompt = "What is the likely color of the sky in Boston today?";
Console.WriteLine(prompt);
// Define the agent
ChatCompletionAgent agent = new()
{
Instructions = "Answer questions about the weather.",
Name = "WeatherAgent",
Kernel = kernel,
Arguments = new KernelArguments(executionSettings),
};
// Invokes agent with a prompt
ChatMessageContent response = await agent.InvokeAsync(prompt).FirstAsync();
Console.WriteLine(response);
Console.WriteLine();
// The expected output is: The sky in Boston today is likely gray due to rainy weather.
}
}
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
namespace MCPClient.Samples;
/// <summary>
/// Demonstrates how to use the Model Context Protocol (MCP) prompt with the Semantic Kernel.
/// </summary>
internal sealed class MCPPromptSample : BaseSample
{
/// <summary>
/// Demonstrates how to use the MCP prompt with the Semantic Kernel.
/// The code in this method:
/// 1. Creates an MCP client.
/// 2. Retrieves the list of prompts provided by the MCP server.
/// 3. Gets the current weather for Boston and Sydney using the `GetCurrentWeatherForCity` prompt.
/// 4. Adds the MCP server prompts to the chat history and prompts the AI model to compare the weather in the two cities and suggest the best place to go for a walk.
/// 5. After receiving and processing the weather data for both cities and the prompt, the AI model returns an answer.
/// </summary>
public static async Task RunAsync()
{
Console.WriteLine($"Running the {nameof(MCPPromptSample)} sample.");
// Create an MCP client
McpClient mcpClient = await CreateMcpClientAsync();
// Retrieve and display the list of prompts provided by the MCP server
IList<McpClientPrompt> prompts = await mcpClient.ListPromptsAsync();
DisplayPrompts(prompts);
// Create a kernel
Kernel kernel = CreateKernelWithChatCompletionService();
// Get weather for Boston using the `GetCurrentWeatherForCity` prompt from the MCP server
GetPromptResult bostonWeatherPrompt = await mcpClient.GetPromptAsync("GetCurrentWeatherForCity", new Dictionary<string, object?>() { ["city"] = "Boston", ["time"] = DateTime.UtcNow.ToString() });
// Get weather for Sydney using the `GetCurrentWeatherForCity` prompt from the MCP server
GetPromptResult sydneyWeatherPrompt = await mcpClient.GetPromptAsync("GetCurrentWeatherForCity", new Dictionary<string, object?>() { ["city"] = "Sydney", ["time"] = DateTime.UtcNow.ToString() });
// Add the prompts to the chat history
ChatHistory chatHistory = [];
chatHistory.AddRange(bostonWeatherPrompt.ToChatMessageContents());
chatHistory.AddRange(sydneyWeatherPrompt.ToChatMessageContents());
chatHistory.AddUserMessage("Compare the weather in the two cities and suggest the best place to go for a walk.");
// Execute a prompt using the MCP tools and prompt
IChatCompletionService chatCompletion = kernel.GetRequiredService<IChatCompletionService>();
ChatMessageContent result = await chatCompletion.GetChatMessageContentAsync(chatHistory, kernel: kernel);
Console.WriteLine(result);
Console.WriteLine();
// The expected output is: Given these conditions, Sydney would be the better choice for a pleasant walk, as the sunny and warm weather is ideal for outdoor activities.
// The rain in Boston could make walking less enjoyable and potentially inconvenient.
}
/// <summary>
/// Displays the list of available MCP prompts.
/// </summary>
/// <param name="prompts">The list of the prompts to display.</param>
private static void DisplayPrompts(IList<McpClientPrompt> prompts)
{
Console.WriteLine("Available MCP prompts:");
foreach (var prompt in prompts)
{
Console.WriteLine($"- Name: {prompt.Name}, Description: {prompt.Description}");
}
Console.WriteLine();
}
}
@@ -0,0 +1,83 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
namespace MCPClient.Samples;
/// <summary>
/// Demonstrates how to use the Model Context Protocol (MCP) resource templates with the Semantic Kernel.
/// </summary>
internal sealed class MCPResourceTemplatesSample : BaseSample
{
/// <summary>
/// Demonstrates how to use the MCP resource templates with the Semantic Kernel.
/// The code in this method:
/// 1. Creates an MCP client.
/// 2. Retrieves the list of resource templates provided by the MCP server.
/// 3. Reads relevant to the prompt records from the `vectorStore://records/{prompt}` MCP resource template.
/// 4. Adds the records to the chat history and prompts the AI model to explain what SK is.
/// </summary>
public static async Task RunAsync()
{
Console.WriteLine($"Running the {nameof(MCPResourceTemplatesSample)} sample.");
// Create an MCP client
McpClient mcpClient = await CreateMcpClientAsync();
// Retrieve list of resource templates provided by the MCP server and display them
IList<McpClientResourceTemplate> resourceTemplates = await mcpClient.ListResourceTemplatesAsync();
DisplayResourceTemplates(resourceTemplates);
// Create a kernel
Kernel kernel = CreateKernelWithChatCompletionService();
// Enable automatic function calling
OpenAIPromptExecutionSettings executionSettings = new()
{
Temperature = 0,
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })
};
string prompt = "What is the Semantic Kernel?";
// Retrieve relevant to the prompt records via MCP resource template
ReadResourceResult resource = await mcpClient.ReadResourceAsync(new Uri($"vectorStore://records/{prompt}"));
// Add the resource content/records to the chat history and prompt the AI model to explain what SK is
ChatHistory chatHistory = [];
chatHistory.AddUserMessage(resource.ToChatMessageContentItemCollection());
chatHistory.AddUserMessage(prompt);
// Execute a prompt using the MCP resource and prompt added to the chat history
IChatCompletionService chatCompletion = kernel.GetRequiredService<IChatCompletionService>();
ChatMessageContent result = await chatCompletion.GetChatMessageContentAsync(chatHistory, executionSettings, kernel);
Console.WriteLine(result);
Console.WriteLine();
// The expected output is: The Semantic Kernel (SK) is a lightweight software development kit (SDK) designed for use in .NET applications.
// It acts as an orchestrator that facilitates interaction between AI models and available plugins, enabling them to work together to produce desired outputs.
}
/// <summary>
/// Displays the list of resource templates provided by the MCP server.
/// </summary>
/// <param name="resourceTemplates">The list of resource templates to display.</param>
private static void DisplayResourceTemplates(IList<McpClientResourceTemplate> resourceTemplates)
{
Console.WriteLine("Available MCP resource templates:");
foreach (var template in resourceTemplates)
{
Console.WriteLine($"- Name: {template.Name}, Description: {template.Description}");
}
Console.WriteLine();
}
}
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
namespace MCPClient.Samples;
/// <summary>
/// Demonstrates how to use the Model Context Protocol (MCP) resources with the Semantic Kernel.
/// </summary>
internal sealed class MCPResourcesSample : BaseSample
{
/// <summary>
/// Demonstrates how to use the MCP resources with the Semantic Kernel.
/// The code in this method:
/// 1. Creates an MCP client.
/// 2. Retrieves the list of resources provided by the MCP server.
/// 3. Retrieves the `image://cat.jpg` resource content from the MCP server.
/// 4. Adds the image to the chat history and prompts the AI model to describe the content of the image.
/// </summary>
public static async Task RunAsync()
{
Console.WriteLine($"Running the {nameof(MCPResourcesSample)} sample.");
// Create an MCP client
McpClient mcpClient = await CreateMcpClientAsync();
// Retrieve list of resources provided by the MCP server and display them
IList<McpClientResource> resources = await mcpClient.ListResourcesAsync();
DisplayResources(resources);
// Create a kernel
Kernel kernel = CreateKernelWithChatCompletionService();
// Enable automatic function calling
OpenAIPromptExecutionSettings executionSettings = new()
{
Temperature = 0,
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })
};
// Retrieve the `image://cat.jpg` resource from the MCP server
ReadResourceResult resource = await mcpClient.ReadResourceAsync(new Uri("image://cat.jpg"));
// Add the resource to the chat history and prompt the AI model to describe the content of the image
ChatHistory chatHistory = [];
chatHistory.AddUserMessage(resource.ToChatMessageContentItemCollection());
chatHistory.AddUserMessage("Describe the content of the image?");
// Execute a prompt using the MCP resource and prompt added to the chat history
IChatCompletionService chatCompletion = kernel.GetRequiredService<IChatCompletionService>();
ChatMessageContent result = await chatCompletion.GetChatMessageContentAsync(chatHistory, executionSettings, kernel);
Console.WriteLine(result);
Console.WriteLine();
// The expected output is: The image features a fluffy cat sitting in a lush, colorful garden.
// The garden is filled with various flowers and plants, creating a vibrant and serene atmosphere...
}
/// <summary>
/// Displays the list of resources provided by the MCP server.
/// </summary>
/// <param name="resources">The list of resources to display.</param>
private static void DisplayResources(IList<McpClientResource> resources)
{
Console.WriteLine("Available MCP resources:");
foreach (var resource in resources)
{
Console.WriteLine($"- Name: {resource.Name}, Uri: {resource.Uri}, Description: {resource.Description}");
}
Console.WriteLine();
}
}
@@ -0,0 +1,132 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using ModelContextProtocol;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
namespace MCPClient.Samples;
/// <summary>
/// Demonstrates how to use the Model Context Protocol (MCP) sampling with the Semantic Kernel.
/// </summary>
internal sealed class MCPSamplingSample : BaseSample
{
/// <summary>
/// Demonstrates how to use the MCP sampling with the Semantic Kernel.
/// The code in this method:
/// 1. Creates an MCP client and register the sampling request handler.
/// 2. Retrieves the list of tools provided by the MCP server and registers them as Kernel functions.
/// 3. Prompts the AI model to create a schedule based on the latest unread emails in the mailbox.
/// 4. The AI model calls the `MailboxUtils-SummarizeUnreadEmails` function to summarize the unread emails.
/// 5. The `MailboxUtils-SummarizeUnreadEmails` function creates a few sample emails with attachments and
/// sends a sampling request to the client to summarize them:
/// 5.1. The client receive sampling request from server and invokes the sampling request handler.
/// 5.2. SK intercepts the sampling request invocation via `HumanInTheLoopFilter` filter to enable human-in-the-loop processing.
/// 5.3. The `HumanInTheLoopFilter` allows invocation of the sampling request handler.
/// 5.5. The sampling request handler sends the sampling request to the AI model to summarize the emails.
/// 5.6. The AI model processes the request and returns the summary to the handler which sends it back to the server.
/// 5.7. The `MailboxUtils-SummarizeUnreadEmails` function receives the result and returns it to the AI model.
/// 7. Having received the summary, the AI model creates a schedule based on the unread emails.
/// </summary>
public static async Task RunAsync()
{
Console.WriteLine($"Running the {nameof(MCPSamplingSample)} sample.");
// Create a kernel
Kernel kernel = CreateKernelWithChatCompletionService();
// Register the human-in-the-loop filter that intercepts function calls allowing users to review and approve or reject them
kernel.FunctionInvocationFilters.Add(new HumanInTheLoopFilter());
// Create an MCP client with a custom sampling request handler
McpClient mcpClient = await CreateMcpClientAsync(kernel, SamplingRequestHandlerAsync);
// Import MCP tools as Kernel functions so AI model can call them
IList<McpClientTool> tools = await mcpClient.ListToolsAsync();
kernel.Plugins.AddFromFunctions("Tools", tools.Select(aiFunction => aiFunction.AsKernelFunction()));
// Enable automatic function calling
OpenAIPromptExecutionSettings executionSettings = new()
{
Temperature = 0,
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })
};
// Execute a prompt
string prompt = "Create a schedule for me based on the latest unread emails in my inbox.";
IChatCompletionService chatCompletion = kernel.GetRequiredService<IChatCompletionService>();
ChatMessageContent result = await chatCompletion.GetChatMessageContentAsync(prompt, executionSettings, kernel);
Console.WriteLine(result);
Console.WriteLine();
// The expected output is:
// ### Today
// - **Review Sales Report:**
// - **Task:** Provide feedback on the Carretera Sales Report for January to June 2014.
// - **Deadline:** End of the day.
// - **Details:** Check the attached spreadsheet for sales data.
//
// ### Tomorrow
// - **Update Employee Information:**
// - **Task:** Update the list of employee birthdays and positions.
// - **Deadline:** By the end of the day.
// - **Details:** Refer to the attached table for employee details.
//
// ### Saturday
// - **Attend BBQ:**
// - **Event:** BBQ Invitation
// - **Details:** Join the BBQ as mentioned in the sales report email.
//
// ### Sunday
// - **Join Hike:**
// - **Event:** Hiking Invitation
// - **Details:** Participate in the hike as mentioned in the HR email.
}
/// <summary>
/// Handles sampling requests from the MCP client.
/// </summary>
/// <param name="kernel">The kernel instance.</param>
/// <param name="request">The sampling request.</param>
/// <param name="progress">The progress notification.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The result of the sampling request.</returns>
private static async Task<CreateMessageResult> SamplingRequestHandlerAsync(Kernel kernel, CreateMessageRequestParams? request, IProgress<ProgressNotificationValue> progress, CancellationToken cancellationToken)
{
if (request is null)
{
throw new ArgumentNullException(nameof(request));
}
// Map the MCP sampling request to the Semantic Kernel prompt execution settings
OpenAIPromptExecutionSettings promptExecutionSettings = new()
{
Temperature = request.Temperature,
MaxTokens = request.MaxTokens,
StopSequences = request.StopSequences?.ToList(),
};
// Create a chat history from the MCP sampling request
ChatHistory chatHistory = [];
if (!string.IsNullOrEmpty(request.SystemPrompt))
{
chatHistory.AddSystemMessage(request.SystemPrompt);
}
chatHistory.AddRange(request.Messages.ToChatMessageContents());
// Prompt the AI model to generate a response
IChatCompletionService chatCompletion = kernel.GetRequiredService<IChatCompletionService>();
ChatMessageContent result = await chatCompletion.GetChatMessageContentAsync(chatHistory, promptExecutionSettings, cancellationToken: cancellationToken);
return result.ToCreateMessageResult();
}
}
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using ModelContextProtocol.Client;
namespace MCPClient.Samples;
/// <summary>
/// This sample demonstrates how to use the Model Context Protocol (MCP) tools with the Semantic Kernel.
/// </summary>
internal sealed class MCPToolsSample : BaseSample
{
/// <summary>
/// Demonstrates how to use the MCP tools with the Semantic Kernel.
/// The code in this method:
/// 1. Creates an MCP client.
/// 2. Retrieves the list of tools provided by the MCP server.
/// 3. Creates a kernel and registers the MCP tools as Kernel functions.
/// 4. Sends the prompt to AI model together with the MCP tools represented as Kernel functions.
/// 5. The AI model calls DateTimeUtils-GetCurrentDateTimeInUtc function to get the current date time in UTC required as an argument for the next function.
/// 6. The AI model calls WeatherUtils-GetWeatherForCity function with the current date time and the `Boston` arguments extracted from the prompt to get the weather information.
/// 7. Having received the weather information from the function call, the AI model returns the answer to the prompt.
/// </summary>
public static async Task RunAsync()
{
Console.WriteLine($"Running the {nameof(MCPToolsSample)} sample.");
// Create an MCP client
McpClient mcpClient = await CreateMcpClientAsync();
// Retrieve and display the list provided by the MCP server
IList<McpClientTool> tools = await mcpClient.ListToolsAsync();
DisplayTools(tools);
// Create a kernel and register the MCP tools
Kernel kernel = CreateKernelWithChatCompletionService();
kernel.Plugins.AddFromFunctions("Tools", tools.Select(aiFunction => aiFunction.AsKernelFunction()));
// Enable automatic function calling
OpenAIPromptExecutionSettings executionSettings = new()
{
Temperature = 0,
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })
};
string prompt = "What is the likely color of the sky in Boston today?";
Console.WriteLine(prompt);
// Execute a prompt using the MCP tools. The AI model will automatically call the appropriate MCP tools to answer the prompt.
FunctionResult result = await kernel.InvokePromptAsync(prompt, new(executionSettings));
Console.WriteLine(result);
Console.WriteLine();
// The expected output is: The likely color of the sky in Boston today is gray, as it is currently rainy.
}
}