{"id":4570,"date":"2025-03-28T06:32:16","date_gmt":"2025-03-28T13:32:16","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/semantic-kernel\/?p=4570"},"modified":"2025-04-14T08:08:47","modified_gmt":"2025-04-14T15:08:47","slug":"building-a-model-context-protocol-server-with-semantic-kernel","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/agent-framework\/building-a-model-context-protocol-server-with-semantic-kernel\/","title":{"rendered":"Building a Model Context Protocol Server with Semantic Kernel"},"content":{"rendered":"<p>This is second MCP related blog post that is part of a series of blog posts that will cover how to use Semantic Kernel (SK) with the Model Context Protocol (MCP).<\/p>\n<p>This blog post demonstrates how to build an MCP server using <a href=\"https:\/\/github.com\/modelcontextprotocol\/csharp-sdk\" target=\"_blank\" rel=\"noopener\">MCP C# SDK<\/a> and SK,\nexpose SK plugins as MCP tools and call the tools from client side via SK.<\/p>\n<p>Here are a few reasons why you might want to build an MCP server with SK:<\/p>\n<ul>\n<li><strong>Interoperability<\/strong>: Existing SK plugins need to be reused and exposed as MCP tools so they can be consumed by non-SK applications or by SK for a different platform.<\/li>\n<li><strong>Content safety<\/strong>: Each tool call needs to be validated before it is executed using <a href=\"https:\/\/learn.microsoft.com\/en-us\/semantic-kernel\/concepts\/enterprise-readiness\/filters?pivots=programming-language-csharp\" target=\"_blank\" rel=\"noopener\">SK Filters<\/a>.<\/li>\n<li><strong>Observability<\/strong>: Tool call-related logs, traces, and metrics need to be collected(see: <a href=\"https:\/\/learn.microsoft.com\/en-us\/semantic-kernel\/concepts\/enterprise-readiness\/observability\/?pivots=programming-language-csharp\" target=\"_blank\" rel=\"noopener\">SK observability<\/a>) and saved to an existing monitoring infrastructure.<\/li>\n<\/ul>\n<p>For more information about MCP, please refer to the <a href=\"https:\/\/modelcontextprotocol.io\/introduction\" target=\"_blank\" rel=\"noopener\">documentation<\/a>.<\/p>\n<p>The sample described below uses the official <a href=\"https:\/\/www.nuget.org\/packages\/mcpdotnet\" target=\"_blank\" rel=\"noopener\">ModelContextProtocol<\/a> nuget package. Its runnable source code is available in the <a href=\"https:\/\/github.com\/microsoft\/semantic-kernel\/tree\/main\/dotnet\/samples\/Demos\/ModelContextProtocolClientServer\" target=\"_blank\" rel=\"noopener\">Semantic Kernel repository<\/a>.<\/p>\n<h4>Build MCP Server and Expose SK Plugins as MCP Tools<\/h4>\n<p>Let&#8217;s start by declaring SK plugins that will be exposed as MCP tools by the server in the next step:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">internal sealed class DateTimeUtils\r\n{\r\n    [KernelFunction, Description(\"Retrieves the current date time in UTC.\")]\r\n    public static string GetCurrentDateTimeInUtc()\r\n    {\r\n        return DateTime.UtcNow.ToString(\"yyyy-MM-dd HH:mm:ss\");\r\n    }\r\n}\r\n\r\ninternal sealed class WeatherUtils\r\n{\r\n    [KernelFunction, Description(\"Gets the current weather for the specified city and specified date time.\")]\r\n    public static string GetWeatherForCity(string cityName, string currentDateTimeInUtc)\r\n    {\r\n        return cityName switch\r\n        {\r\n            \"Boston\" =&gt; \"61 and rainy\",\r\n            \"London\" =&gt; \"55 and cloudy\",\r\n            ...\r\n        };\r\n    }\r\n}<\/code><\/pre>\n<p>Now we will create an MCP server and import the SK plugins:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">\/\/ Create a kernel builder and add plugins\r\nIKernelBuilder kernelBuilder = Kernel.CreateBuilder();\r\nkernelBuilder.Plugins.AddFromType&lt;DateTimeUtils&gt;();\r\nkernelBuilder.Plugins.AddFromType&lt;WeatherUtils&gt;();\r\n\r\n\/\/ Build the kernel\r\nKernel kernel = kernelBuilder.Build();\r\n\r\nvar builder = Host.CreateEmptyApplicationBuilder(settings: null);\r\nbuilder.Services\r\n    .AddMcpServer()\r\n    .WithStdioServerTransport()\r\n    \/\/ Add all functions from the kernel plugins to the MCP server as tools\r\n    .WithTools(kernel);\r\nawait builder.Build().RunAsync();<\/code><\/pre>\n<p>The code above creates kernel and imports SK plugins. Then it creates an MCP server and registers all functions from all SK plugins as MCP tools using the WithTools extension method:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, KernelPluginCollection plugins)\r\n{\r\n    foreach (var plugin in plugins)\r\n    {\r\n        foreach (var function in plugin)\r\n        {\r\n            builder.Services.AddSingleton(services =&gt; McpServerTool.Create(function.AsAIFunction()));\r\n        }\r\n    }\r\n\r\n    return builder;\r\n}<\/code><\/pre>\n<p>The code can be further extended to register function invocation filters to intercept function calls and validate them before execution in addition to enabling observability to collect tool call-related logs, traces, and metrics.<\/p>\n<h4>Build MCP Client and Use MCP tools in SK<\/h4>\n<p>Next we will create an MCP client:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">await using IMcpClient mcpClient = await McpClientFactory.CreateAsync(new StdioClientTransport(new()\r\n{\r\n    Name = \"MCPServer\",\r\n    \/\/ Point the client to the MCPServer server executable\r\n    Command = Path.Combine(\"..\", \"..\", \"..\", \"..\", \"MCPServer\", \"bin\", \"Debug\", \"net8.0\", \"MCPServer.exe\")\r\n}));<\/code><\/pre>\n<p>Then we get MCP tools from the server, import them to SK, add OpenAI chat completion service and build the kernel:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">IList&lt;AIFunction&gt; tools = await mcpClient.ListToolsAsync();\r\n\r\nIKernelBuilder kernelBuilder = Kernel.CreateBuilder();\r\nkernelBuilder.Plugins.AddFromFunctions(\"Tools\", tools.Select(aiFunction =&gt; aiFunction.AsKernelFunction()));\r\nkernelBuilder.Services.AddOpenAIChatCompletion(serviceId: \"openai\", modelId: config[\"OpenAI:ChatModelId\"] ?? \"gpt-4o-mini\", apiKey: apiKey);\r\n\r\nKernel kernel = kernelBuilder.Build();<\/code><\/pre>\n<p>The code above iterates over the MCP tools represented by AI functions, convert them to SK functions and add them to the kernel so they can be used by AI models.<\/p>\n<p>Now, we can prompt the AI model and the AI model will automatically call the imported kernel functions which will delegate the calls to the MCP tools to answer the prompt:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">\/\/ Enable automatic function calling\r\nOpenAIPromptExecutionSettings executionSettings = new()\r\n{\r\n    Temperature = 0,\r\n    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })\r\n};\r\n\r\n\/\/ Execute a prompt using the MCP tools. The AI model will automatically call the appropriate MCP tools to answer the prompt.\r\nvar prompt = \"What is the likely color of the sky in Boston today?\";\r\nvar result = await kernel.InvokePromptAsync(prompt, new(executionSettings));\r\nConsole.WriteLine(result);<\/code><\/pre>\n<p>After calling the MCP tools, AI model will return the following result:<\/p>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\">The likely color of the sky in Boston today is gray, as it is currently rainy.<\/code><\/pre>\n<h4>What&#8217;s Next?<\/h4>\n<p>We recommend trying out the sample code in the <a href=\"https:\/\/github.com\/microsoft\/semantic-kernel\/tree\/main\/dotnet\/samples\/Demos\/ModelContextProtocolClientServer\" target=\"_blank\" rel=\"noopener\">Semantic Kernel repository<\/a> to get started.<\/p>\n<p>Next, we plan to create more samples to demonstrate how to use Semantic Kernel for building MCP <a href=\"https:\/\/modelcontextprotocol.io\/docs\/concepts\/prompts\" target=\"_blank\" rel=\"noopener\">prompts<\/a> and <a href=\"https:\/\/modelcontextprotocol.io\/docs\/concepts\/resources\" target=\"_blank\" rel=\"noopener\">resources<\/a> on the server side, and how to use them with the SK client side.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is second MCP related blog post that is part of a series of blog posts that will cover how to use Semantic Kernel (SK) with the Model Context Protocol (MCP). This blog post demonstrates how to build an MCP server using MCP C# SDK and SK, expose SK plugins as MCP tools and call [&hellip;]<\/p>\n","protected":false},"author":157200,"featured_media":4591,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[78,2,1],"tags":[79,48,133,9],"class_list":["post-4570","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net","category-samples","category-semantic-kernel","tag-net","tag-ai","tag-mcp","tag-semantic-kernel"],"acf":[],"blog_post_summary":"<p>This is second MCP related blog post that is part of a series of blog posts that will cover how to use Semantic Kernel (SK) with the Model Context Protocol (MCP). This blog post demonstrates how to build an MCP server using MCP C# SDK and SK, expose SK plugins as MCP tools and call [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts\/4570","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/users\/157200"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/comments?post=4570"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/posts\/4570\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/media\/4591"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/media?parent=4570"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/categories?post=4570"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/agent-framework\/wp-json\/wp\/v2\/tags?post=4570"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}