From 20cc59b0ebef249194ccc927389af5c6d069984c Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Wed, 25 Jun 2025 22:54:54 -0400 Subject: [PATCH 01/21] Start on MCP server template --- src/ProjectTemplates/GeneratedContent.targets | 8 +++ .../Microsoft.Extensions.AI.Templates.csproj | 2 +- .../McpServer-CSharp/.mcp/server.json | 35 +++++++++++ .../.template.config/dotnetcli.host.json | 7 +++ .../.template.config/ide.host.json | 4 ++ .../.template.config/template.json | 21 +++++++ .../McpServer-CSharp.csproj.in | 34 ++++++++++ .../src/McpServer/McpServer-CSharp/Program.cs | 62 +++++++++++++++++++ .../src/McpServer/McpServer-CSharp/README.md | 40 ++++++++++++ 9 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.mcp/server.json create mode 100644 src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/dotnetcli.host.json create mode 100644 src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide.host.json create mode 100644 src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/template.json create mode 100644 src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in create mode 100644 src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs create mode 100644 src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md diff --git a/src/ProjectTemplates/GeneratedContent.targets b/src/ProjectTemplates/GeneratedContent.targets index c3287408034..ce6ba2bc502 100644 --- a/src/ProjectTemplates/GeneratedContent.targets +++ b/src/ProjectTemplates/GeneratedContent.targets @@ -10,6 +10,7 @@ <_LocalChatTemplateVariant>aspire <_ChatWithCustomDataContentRoot>$(MSBuildThisFileDirectory)Microsoft.Extensions.AI.Templates\src\ChatWithCustomData\ + <_McpServerContentRoot>$(MSBuildThisFileDirectory)Microsoft.Extensions.AI.Templates\src\McpServer\ @@ -34,9 +35,11 @@ 1.14.0 11.6.0 9.4.1-beta.291 + 10.0.0-preview.5.25277.114 9.3.0 1.53.0 1.53.0-preview + 0.3.0-preview.1 5.1.18 1.12.0 0.1.10 @@ -64,9 +67,11 @@ TemplatePackageVersion_AzureIdentity=$(TemplatePackageVersion_AzureIdentity); TemplatePackageVersion_AzureSearchDocuments=$(TemplatePackageVersion_AzureSearchDocuments); TemplatePackageVersion_CommunityToolkitAspire=$(TemplatePackageVersion_CommunityToolkitAspire); + TemplatePackageVersion_MicrosoftExtensionsHosting=$(TemplatePackageVersion_MicrosoftExtensionsHosting); TemplatePackageVersion_MicrosoftExtensionsServiceDiscovery=$(TemplatePackageVersion_MicrosoftExtensionsServiceDiscovery); TemplatePackageVersion_MicrosoftSemanticKernel=$(TemplatePackageVersion_MicrosoftSemanticKernel); TemplatePackageVersion_MicrosoftSemanticKernel_Preview=$(TemplatePackageVersion_MicrosoftSemanticKernel_Preview); + TemplatePackageVersion_ModelContextProtocol=$(TemplatePackageVersion_ModelContextProtocol); TemplatePackageVersion_OllamaSharp=$(TemplatePackageVersion_OllamaSharp); TemplatePackageVersion_OpenTelemetry=$(TemplatePackageVersion_OpenTelemetry); TemplatePackageVersion_PdfPig=$(TemplatePackageVersion_PdfPig); @@ -100,6 +105,9 @@ + <_GeneratedContentEnablingJustBuiltPackages diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj index 2827734c794..6f3525a5ae8 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj @@ -46,7 +46,7 @@ ", + "name": "io.github./", + "packages": [ + { + "registry_name": "nuget", + "name": "", + "version": "0.1.0-beta", + "package_arguments": [ + { + "description": "Start the MCP server tool in server mode", + "is_required": true, + "format": "string", + "value": "start-mcp", + "default": "start-mcp", + "type": "positional", + "value_hint": "start-mcp" + } + ], + "environment_variables": [ + { + "description": "The maximum number to return from the random number generator", + "name": "MAX_RANDOM_NUMBER" + } + ] + } + ], + "repository": { + "url": "https://github.com//", + "source": "github" + }, + "version_detail": { + "version": "0.1.0-beta" + } +} \ No newline at end of file diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/dotnetcli.host.json new file mode 100644 index 00000000000..5be51dd6357 --- /dev/null +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/dotnetcli.host.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://json.schemastore.org/dotnetcli.host", + "symbolInfo": {}, + "usageExamples": [ + "" + ] +} \ No newline at end of file diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide.host.json new file mode 100644 index 00000000000..2033019c550 --- /dev/null +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide.host.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://json.schemastore.org/ide.host", + "symbolInfo": [] +} diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/template.json b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/template.json new file mode 100644 index 00000000000..1759db92738 --- /dev/null +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/template.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Microsoft", + "classifications": [ "Common", "AI", "MCP" ], + "identity": "Microsoft.Extensions.AI.Templates.McpServer.CSharp", + "name": "Local MCP Server Console App", + "description": "A project template for creating a local MCP server using the .NET MCP SDK.", + "shortName": "mcpserver", + "defaultName": "McpServer", + "sourceName": "McpServer-CSharp", + "preferNameDirectory": true, + "tags": { + "language": "C#", + "type": "project" + }, + "primaryOutputs": [ + { + "path": "./McpServer-CSharp.csproj" + } + ] +} diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in new file mode 100644 index 00000000000..5583a7c7da8 --- /dev/null +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in @@ -0,0 +1,34 @@ + + + + net10.0 + Exe + enable + enable + + + true + McpServer + + + README.md + 0.1.0-beta + AI; MCP; server; stdio + An MCP server using the .NET MCP SDK. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs new file mode 100644 index 00000000000..10092cf5edc --- /dev/null +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -0,0 +1,62 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using ModelContextProtocol.Server; +using System.ComponentModel; + +var builder = Host.CreateApplicationBuilder(args); + +if (args.Length == 0 || args[0] != "start-mcp") +{ + Console.Error.WriteLine("Error: invalid command. Use the 'start-mcp' command like argument to start the MCP server."); + return 1; +} + +var maxNumberEnv = Environment.GetEnvironmentVariable("MAX_RANDOM_NUMBER"); +if (!int.TryParse(maxNumberEnv, out var maxNumber) || maxNumber <= 0) +{ + Console.Error.WriteLine("Error: you must set the MAX_RANDOM_NUMBER environment variable to a positive integer."); + return 1; +} + +builder.Logging.AddConsole(consoleLogOptions => +{ + // configure all logs to go to stderr + // stdout is used for the MCP protocol messages + consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace; +}); + +// add the MCP services, discover tools from the current assembly +builder.Services + .AddMcpServer() + .WithStdioServerTransport() + .WithToolsFromAssembly(); + +// add dependencies for the MCP server tools +builder.Services + .AddSingleton(_ => new RandomNumberService(maxNumber)); + +await builder.Build().RunAsync(); +return 0; + +public class RandomNumberService(int max) +{ + public int Max => max; + public int GetRandomNumber() => Random.Shared.Next(1, max + 1); +} + +[McpServerToolType] +public class RandomNumberTool(RandomNumberService service) +{ + [McpServerTool, Description("Returns the maximum random number that can be generated by the tool.")] + public int MaxRandomNumber() + { + return service.Max; + } + + [McpServerTool, Description("Returns a random number between 1 and the maximum number set by the tool.")] + public int GetRandomNumber() + { + return service.GetRandomNumber(); + } +} diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md new file mode 100644 index 00000000000..54850d39647 --- /dev/null +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md @@ -0,0 +1,40 @@ +# MCP Server + +This README was created using the .NET MCP server template project. It demonstrates how you can easily create an MCP server using .NET and then package it in a NuGet package. + +See [aka.ms/nuget/mcp/guide](https://aka.ms/nuget/mcp/guide) for the full guide. + +## Checklist before publishing to NuGet.org + +- Update package metadata in the .csproj file +- Update the `.mcp/server.json` to declare your MCP server's inputs +- Test the MCP server locally using the steps below + +## Developing locally + +To test this MCP server from source code (locally) without using a built MCP server package, use the following MCP configuration in VS Code. See [Use MCP servers in VS Code (Preview)](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more information about using MCP servers in VS Code. + +```json +{ + "mcp": { + "servers": { + "my-custom-mcp": { + "type": "stdio", + "command": "dotnet", + "args": [ + "run", + "--project", + "", + "--", + "start-mcp" + ], + "env": { + "MAX_RANDOM_NUMBER": 100 + } + } + } + } +} +``` + +Then ask the Copilot chat for a random number. It should prompt to use the `GetRandomNumber` tool on the `my-custom-mcp` MCP server. From 5ef5c50b18d05ecbe9b070ff5c2893c9985535c3 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 26 Jun 2025 14:05:19 -0400 Subject: [PATCH 02/21] Improve Test commands: BUILD + INSTALL .\build.cmd -pack -projects .\src\ProjectTemplates\Microsoft.Extensions.AI.Templates\Microsoft.Extensions.AI.Templates.csproj; dotnet new uninstall Microsoft.Extensions.AI.Templates; dotnet new install Microsoft.Extensions.AI.Templates::9.7.0-dev --add-source Q:\src\dotnet-extensions\artifacts\packages\Debug\Shipping IN A NEW DIR + TEST TEMPLATE: gci Q:\trash\2025-06-25\template-try-2\* -Recurse | Remove-Item -Force -Recurse -Confirm:$false; dotnet new mcpserver; dotnet build; dotnet pack --- .../McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in | 7 +++---- .../src/McpServer/McpServer-CSharp/Program.cs | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in index 5583a7c7da8..323533c6edd 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in @@ -6,19 +6,18 @@ enable enable - + true McpServer - + README.md + SampleMcpServer 0.1.0-beta AI; MCP; server; stdio An MCP server using the .NET MCP SDK. - - diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index 10092cf5edc..9bba3e44c19 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -33,8 +33,7 @@ .WithToolsFromAssembly(); // add dependencies for the MCP server tools -builder.Services - .AddSingleton(_ => new RandomNumberService(maxNumber)); +builder.Services.AddTransient(_ => new RandomNumberService(maxNumber)); await builder.Build().RunAsync(); return 0; From 8b06c6f170b573efac22b4cdca02537a42f0fce0 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 26 Jun 2025 16:50:41 -0400 Subject: [PATCH 03/21] Update src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs Co-authored-by: Stephen Toub --- .../src/McpServer/McpServer-CSharp/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index 9bba3e44c19..e6fafa9810e 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -30,7 +30,7 @@ builder.Services .AddMcpServer() .WithStdioServerTransport() - .WithToolsFromAssembly(); + .WithTool(); // add dependencies for the MCP server tools builder.Services.AddTransient(_ => new RandomNumberService(maxNumber)); From cd6a0e2167204a3c92fcde287f8c0da1abd3a689 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 26 Jun 2025 16:50:48 -0400 Subject: [PATCH 04/21] Update src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs Co-authored-by: Stephen Toub --- .../src/McpServer/McpServer-CSharp/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index e6fafa9810e..ff7a16f6782 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -44,7 +44,6 @@ public class RandomNumberService(int max) public int GetRandomNumber() => Random.Shared.Next(1, max + 1); } -[McpServerToolType] public class RandomNumberTool(RandomNumberService service) { [McpServerTool, Description("Returns the maximum random number that can be generated by the tool.")] From 36c90b16907c57dbf6e9d18eb47c8d4e7ceb3a0d Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 26 Jun 2025 16:51:00 -0400 Subject: [PATCH 05/21] Update src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs Co-authored-by: Stephen Toub --- .../src/McpServer/McpServer-CSharp/Program.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index ff7a16f6782..36045d2fa41 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -21,8 +21,7 @@ builder.Logging.AddConsole(consoleLogOptions => { - // configure all logs to go to stderr - // stdout is used for the MCP protocol messages + // Configure all logs to go to stderr (stdout is used for the MCP protocol messages). consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace; }); From 5935e56da749a726ada1579b568edef0cdfe73db Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 26 Jun 2025 16:51:24 -0400 Subject: [PATCH 06/21] Update src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs --- .../src/McpServer/McpServer-CSharp/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index 36045d2fa41..49feebe455b 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -32,7 +32,7 @@ .WithTool(); // add dependencies for the MCP server tools -builder.Services.AddTransient(_ => new RandomNumberService(maxNumber)); +builder.Services.AddSingleton(_ => new RandomNumberService(maxNumber)); await builder.Build().RunAsync(); return 0; From 412795582ee3091581d11716cc27c3650702c496 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 26 Jun 2025 16:51:40 -0400 Subject: [PATCH 07/21] Update src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs Co-authored-by: Stephen Toub --- .../src/McpServer/McpServer-CSharp/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index 49feebe455b..e2ea9a13418 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -51,7 +51,7 @@ public int MaxRandomNumber() return service.Max; } - [McpServerTool, Description("Returns a random number between 1 and the maximum number set by the tool.")] + [McpServerTool, Description("Returns a random number between 1 and the maximum number allowed by the tool.")] public int GetRandomNumber() { return service.GetRandomNumber(); From 28363b4fbb3fca5062cfc7e76bc6c4a537695f4c Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 26 Jun 2025 16:51:59 -0400 Subject: [PATCH 08/21] Update src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in Co-authored-by: Stephen Toub --- .../src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in index 323533c6edd..ac4dc6284bd 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in @@ -15,7 +15,7 @@ SampleMcpServer 0.1.0-beta AI; MCP; server; stdio - An MCP server using the .NET MCP SDK. + An MCP server using the MCP C# SDK. From 4e647754532c060d141aee59218cb7c69c03a45c Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 26 Jun 2025 16:57:29 -0400 Subject: [PATCH 09/21] Update src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs Co-authored-by: Stephen Toub --- .../src/McpServer/McpServer-CSharp/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index e2ea9a13418..954c98563a4 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -8,7 +8,7 @@ if (args.Length == 0 || args[0] != "start-mcp") { - Console.Error.WriteLine("Error: invalid command. Use the 'start-mcp' command like argument to start the MCP server."); + Console.Error.WriteLine("Error: invalid command. Use the 'start-mcp' command-line argument to start the MCP server."); return 1; } From a2ac345dd2be318d8eee03ce8ef5e912b6e3d64e Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Thu, 26 Jun 2025 18:08:27 -0400 Subject: [PATCH 10/21] Fix type, improve readme --- .../src/McpServer/McpServer-CSharp/Program.cs | 2 +- .../src/McpServer/McpServer-CSharp/README.md | 42 ++++++++++++++++--- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index 954c98563a4..a5680f5112b 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -29,7 +29,7 @@ builder.Services .AddMcpServer() .WithStdioServerTransport() - .WithTool(); + .WithTools(); // add dependencies for the MCP server tools builder.Services.AddSingleton(_ => new RandomNumberService(maxNumber)); diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md index 54850d39647..cb59f2fac24 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md @@ -6,13 +6,45 @@ See [aka.ms/nuget/mcp/guide](https://aka.ms/nuget/mcp/guide) for the full guide. ## Checklist before publishing to NuGet.org -- Update package metadata in the .csproj file -- Update the `.mcp/server.json` to declare your MCP server's inputs -- Test the MCP server locally using the steps below +- Update the package metadata in the .csproj file +- Update `.mcp/server.json` to declare your MCP server's inputs +- Test the MCP server locally using the steps below + +## Using the MCP Server in VS Code + +Once the MCP server package is published to NuGet.org, you can use the following VS Code user configuration to download and install the MCP server package. See [Use MCP servers in VS Code (Preview)](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more information about using MCP servers in VS Code. + +```json +{ + "mcp": { + "servers": { + "my-custom-mcp": { + "type": "stdio", + "command": "dotnet", + "args": [ + "tool", + "exec", + "", + "--version", + "", + "--yes", + "--", + "start-mcp" + ], + "env": { + "MAX_RANDOM_NUMBER": 100 + } + } + } + } +} +``` + +Now you can ask Copilot Chat for a random number, for example, `Give me 3 random numbers`. It should prompt you to use the `GetRandomNumber` tool on the `my-custom-mcp` MCP server and show you the results. ## Developing locally -To test this MCP server from source code (locally) without using a built MCP server package, use the following MCP configuration in VS Code. See [Use MCP servers in VS Code (Preview)](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more information about using MCP servers in VS Code. +To test this MCP server from source code (locally) without using a built MCP server package, use the following VS Code configuration: ```json { @@ -36,5 +68,3 @@ To test this MCP server from source code (locally) without using a built MCP ser } } ``` - -Then ask the Copilot chat for a random number. It should prompt to use the `GetRandomNumber` tool on the `my-custom-mcp` MCP server. From 06d22470f83b0760ef3940eb3141d8688f9323fd Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Mon, 30 Jun 2025 13:34:07 -0400 Subject: [PATCH 11/21] Update src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in --- .../src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in index ac4dc6284bd..1b1fdeef76e 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in @@ -19,7 +19,7 @@ - + From 3a6a3e813617abb887360b144dc741ae3df0377a Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 1 Jul 2025 13:54:52 -0400 Subject: [PATCH 12/21] Address comments --- .../Microsoft.Extensions.AI.Templates.csproj | 10 +++- .../McpServer-CSharp/.mcp/server.json | 14 +----- .../.template.config/template.json | 29 ++++++++++- .../McpServer-CSharp.csproj.in | 9 ++-- .../src/McpServer/McpServer-CSharp/Program.cs | 47 ++++-------------- .../src/McpServer/McpServer-CSharp/README.md | 48 ++++++++++++------ .../Tools/RandomNumberTools.cs | 49 +++++++++++++++++++ 7 files changed, 134 insertions(+), 72 deletions(-) create mode 100644 src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj index 6f3525a5ae8..3ae465d0979 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj @@ -46,7 +46,7 @@ + diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.mcp/server.json b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.mcp/server.json index b6dd997601d..0ec7270e177 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.mcp/server.json +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.mcp/server.json @@ -6,17 +6,7 @@ "registry_name": "nuget", "name": "", "version": "0.1.0-beta", - "package_arguments": [ - { - "description": "Start the MCP server tool in server mode", - "is_required": true, - "format": "string", - "value": "start-mcp", - "default": "start-mcp", - "type": "positional", - "value_hint": "start-mcp" - } - ], + "package_arguments": [], "environment_variables": [ { "description": "The maximum number to return from the random number generator", @@ -32,4 +22,4 @@ "version_detail": { "version": "0.1.0-beta" } -} \ No newline at end of file +} diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/template.json b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/template.json index 1759db92738..1fdc9128e81 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/template.json @@ -1,10 +1,14 @@ { "$schema": "http://json.schemastore.org/template", "author": "Microsoft", - "classifications": [ "Common", "AI", "MCP" ], + "classifications": [ + "Common", + "AI", + "MCP" + ], "identity": "Microsoft.Extensions.AI.Templates.McpServer.CSharp", "name": "Local MCP Server Console App", - "description": "A project template for creating a local MCP server using the .NET MCP SDK.", + "description": "A project template for creating a Model Context Protocol (MCP) server using C# and the ModelContextProtocol package.", "shortName": "mcpserver", "defaultName": "McpServer", "sourceName": "McpServer-CSharp", @@ -13,9 +17,30 @@ "language": "C#", "type": "project" }, + "symbols": { + "hostIdentifier": { + "type": "bind", + "binding": "HostIdentifier" + } + }, "primaryOutputs": [ + { + "path": "./README.md" + }, { "path": "./McpServer-CSharp.csproj" } + ], + "postActions": [ + { + "condition": "(hostIdentifier != \"dotnetcli\" && hostIdentifier != \"dotnetcli-preview\")", + "description": "Opens README file in the editor", + "manualInstructions": [], + "actionId": "84C0DA21-51C8-4541-9940-6CA19AF04EE6", + "args": { + "files": "0" + }, + "continueOnError": true + } ] } diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in index ac4dc6284bd..1d2f384f74b 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in @@ -5,7 +5,7 @@ Exe enable enable - + true McpServer @@ -17,11 +17,10 @@ AI; MCP; server; stdio An MCP server using the MCP C# SDK. - - + - + @@ -30,4 +29,4 @@ - \ No newline at end of file + diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index a5680f5112b..59597ac0c44 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -1,23 +1,20 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using ModelContextProtocol.Server; -using System.ComponentModel; +using McpServer_CSharp.Tools; var builder = Host.CreateApplicationBuilder(args); -if (args.Length == 0 || args[0] != "start-mcp") +// Uncomment the following lines if you want to enforce the MCP command-line arguments. +// You can use command-line arguments to support multiple modes or commands in your application. +// These would be specified in the "package_arguments" property in the .mcp/server.json to inform client tools. +/* +if (args.Length == 0 || args[0] != "mcp") { - Console.Error.WriteLine("Error: invalid command. Use the 'start-mcp' command-line argument to start the MCP server."); - return 1; -} - -var maxNumberEnv = Environment.GetEnvironmentVariable("MAX_RANDOM_NUMBER"); -if (!int.TryParse(maxNumberEnv, out var maxNumber) || maxNumber <= 0) -{ - Console.Error.WriteLine("Error: you must set the MAX_RANDOM_NUMBER environment variable to a positive integer."); + Console.Error.WriteLine("Error: invalid command. Use the 'mcp' command-line argument to start the MCP server."); return 1; } +*/ builder.Logging.AddConsole(consoleLogOptions => { @@ -25,35 +22,11 @@ consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace; }); -// add the MCP services, discover tools from the current assembly +// Add the MCP services: the transport to use (stdio) and the tools to register. builder.Services .AddMcpServer() .WithStdioServerTransport() - .WithTools(); - -// add dependencies for the MCP server tools -builder.Services.AddSingleton(_ => new RandomNumberService(maxNumber)); + .WithTools(); await builder.Build().RunAsync(); return 0; - -public class RandomNumberService(int max) -{ - public int Max => max; - public int GetRandomNumber() => Random.Shared.Next(1, max + 1); -} - -public class RandomNumberTool(RandomNumberService service) -{ - [McpServerTool, Description("Returns the maximum random number that can be generated by the tool.")] - public int MaxRandomNumber() - { - return service.Max; - } - - [McpServerTool, Description("Returns a random number between 1 and the maximum number allowed by the tool.")] - public int GetRandomNumber() - { - return service.GetRandomNumber(); - } -} diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md index cb59f2fac24..ce2bd0ef01b 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md @@ -1,14 +1,17 @@ # MCP Server -This README was created using the .NET MCP server template project. It demonstrates how you can easily create an MCP server using .NET and then package it in a NuGet package. +This README was created using the C# MCP server template project. It demonstrates how you can easily create an MCP server using C# and then package it in a NuGet package. See [aka.ms/nuget/mcp/guide](https://aka.ms/nuget/mcp/guide) for the full guide. ## Checklist before publishing to NuGet.org -- Update the package metadata in the .csproj file -- Update `.mcp/server.json` to declare your MCP server's inputs -- Test the MCP server locally using the steps below +- Test the MCP server locally using the steps below. +- Update the package metadata in the .csproj file. +- Update `.mcp/server.json` to declare your MCP server's inputs. +- Pack the project using `dotnet pack`. + +The `bin/Release` directory will contain the package file (.nupkg), which can be [published to NuGet.org](https://learn.microsoft.com/nuget/nuget-org/publish-a-package). ## Using the MCP Server in VS Code @@ -18,7 +21,7 @@ Once the MCP server package is published to NuGet.org, you can use the following { "mcp": { "servers": { - "my-custom-mcp": { + "McpServer-CSharp": { "type": "stdio", "command": "dotnet", "args": [ @@ -27,9 +30,7 @@ Once the MCP server package is published to NuGet.org, you can use the following "", "--version", "", - "--yes", - "--", - "start-mcp" + "--yes" ], "env": { "MAX_RANDOM_NUMBER": 100 @@ -40,25 +41,42 @@ Once the MCP server package is published to NuGet.org, you can use the following } ``` -Now you can ask Copilot Chat for a random number, for example, `Give me 3 random numbers`. It should prompt you to use the `GetRandomNumber` tool on the `my-custom-mcp` MCP server and show you the results. +Now you can ask Copilot Chat for a random number, for example, `Give me 3 random numbers`. It should prompt you to use the `get_random_number` tool on the `my-custom-mcp` MCP server and show you the results. + +## Developing locally in VS Code + +To test this MCP server from source code (locally) without using a built MCP server package, create a `.vscode/mcp.json` file (a VS Code workspace settings file) in your project directory and add the following configuration: -## Developing locally +```json +{ + "servers": { + "McpServer-CSharp": { + "type": "stdio", + "command": "dotnet", + "args": [ + "run" + ], + "env": { + "MAX_RANDOM_NUMBER": 100 + } + } + } +} +``` -To test this MCP server from source code (locally) without using a built MCP server package, use the following VS Code configuration: +Alternatively, you can configure your VS Code user settings to use your local project: ```json { "mcp": { "servers": { - "my-custom-mcp": { + "McpServer-CSharp": { "type": "stdio", "command": "dotnet", "args": [ "run", "--project", - "", - "--", - "start-mcp" + "" ], "env": { "MAX_RANDOM_NUMBER": 100 diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs new file mode 100644 index 00000000000..af81df4c97c --- /dev/null +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs @@ -0,0 +1,49 @@ +using System.ComponentModel; +using ModelContextProtocol.Server; + +namespace McpServer_CSharp.Tools; + +/// +/// Sample MCP tools for demonstration purposes. +/// These tools can be invoked by MCP clients to perform various operations. +/// +public class RandomNumberTools +{ + private readonly int _maxNumber; + + public RandomNumberTools() + { + // Process configuration settings from the environment variables. + // These will be provided by the MCP client application, such as VS Code. + // Configuration settings could be provided via dependency injection and the IOptions pattern. + var maxNumberEnv = Environment.GetEnvironmentVariable("MAX_RANDOM_NUMBER"); + if (!int.TryParse(maxNumberEnv, out var maxNumber) || maxNumber <= 0) + { + throw new InvalidOperationException("Error: you must set the MAX_RANDOM_NUMBER environment variable to a positive integer."); + } + + _maxNumber = maxNumber; + } + + /// + /// Returns the maximum random number that can be generated by the tool. + /// + /// The maximum random number. + [McpServerTool(Name = "get_max_random_number")] + [Description("Returns the maximum random number that can be generated by the tool.")] + public int GetMaxRandomNumber() + { + return _maxNumber; + } + + /// + /// Returns a random number between 1 and the maximum number allowed by the tool (inclusive). + /// + /// A random number. + [McpServerTool(Name = "get_random_number")] + [Description("Returns a random number between 1 and the maximum number allowed by the tool.")] + public int GetRandomNumber() + { + return Random.Shared.Next(1, _maxNumber + 1); + } +} From fb056fc489ec7470a56e178ef8a486e38873604d Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 1 Jul 2025 13:56:55 -0400 Subject: [PATCH 13/21] Make package path consistent --- .../src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in index 1d2f384f74b..cb2dd0f95b4 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in @@ -20,7 +20,7 @@ - + From 6e3047037367af837f177e351c2d4de2e9b4b48f Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 1 Jul 2025 14:34:27 -0400 Subject: [PATCH 14/21] Add icon --- .../.template.config/ide.host.json | 2 ++ .../.template.config/ide/icon.ico | Bin 0 -> 38045 bytes 2 files changed, 2 insertions(+) create mode 100644 src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide/icon.ico diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide.host.json index 2033019c550..5edf447bbd4 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide.host.json +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide.host.json @@ -1,4 +1,6 @@ { "$schema": "https://json.schemastore.org/ide.host", + "order": 0, + "icon": "ide/icon.ico", "symbolInfo": [] } diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide/icon.ico b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..954709ffd6b9d360fbc0b121a63a29657e0fe768 GIT binary patch literal 38045 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 Date: Tue, 1 Jul 2025 14:52:34 -0400 Subject: [PATCH 15/21] Add snapshot tests for MCP server template --- .../McpServerSnapshotTests.cs | 95 +++++++++++++++++++ .../mcpserver/.mcp/server.json | 25 +++++ .../mcpserver/Program.cs | 32 +++++++ .../mcpserver/README.md | 88 +++++++++++++++++ .../mcpserver/Tools/RandomNumberTools.cs | 49 ++++++++++ .../mcpserver/mcpserver.csproj | 32 +++++++ 6 files changed, 321 insertions(+) create mode 100644 test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/McpServerSnapshotTests.cs create mode 100644 test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/.mcp/server.json create mode 100644 test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs create mode 100644 test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md create mode 100644 test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs create mode 100644 test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/mcpserver.csproj diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/McpServerSnapshotTests.cs b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/McpServerSnapshotTests.cs new file mode 100644 index 00000000000..a3f3dedd1b5 --- /dev/null +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/McpServerSnapshotTests.cs @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.AI.Templates.Tests; +using Microsoft.Extensions.Logging; +using Microsoft.TemplateEngine.Authoring.TemplateVerifier; +using Microsoft.TemplateEngine.TestHelper; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Extensions.AI.Templates.Tests; + +public class McpServerSnapshotTests +{ + // Keep the exclude patterns below in sync with those in Microsoft.Extensions.AI.Templates.csproj. + private static readonly string[] _verificationExcludePatterns = [ + "**/bin/**", + "**/obj/**", + "**/.vs/**", + "**/*.sln", + "**/*.in", + ]; + + private readonly ILogger _log; + + public McpServerSnapshotTests(ITestOutputHelper log) + { +#pragma warning disable CA2000 // Dispose objects before losing scope + _log = new XunitLoggerProvider(log).CreateLogger("TestRun"); +#pragma warning restore CA2000 // Dispose objects before losing scope + } + + [Fact] + public async Task BasicTest() + { + await TestTemplateCoreAsync(scenarioName: "Basic"); + } + + private async Task TestTemplateCoreAsync(string scenarioName, IEnumerable? templateArgs = null) + { + string workingDir = TestUtils.CreateTemporaryFolder(); + string templateShortName = "mcpserver"; + + // Get the template location + string templateLocation = Path.Combine(WellKnownPaths.TemplateFeedLocation, "Microsoft.Extensions.AI.Templates", "src", "McpServer"); + + var verificationExcludePatterns = Path.DirectorySeparatorChar is '/' + ? _verificationExcludePatterns + : _verificationExcludePatterns.Select(p => p.Replace('/', Path.DirectorySeparatorChar)).ToArray(); + + TemplateVerifierOptions options = new TemplateVerifierOptions(templateName: templateShortName) + { + TemplatePath = templateLocation, + TemplateSpecificArgs = templateArgs, + SnapshotsDirectory = "Snapshots", + OutputDirectory = workingDir, + DoNotPrependCallerMethodNameToScenarioName = true, + DoNotAppendTemplateArgsToScenarioName = true, + ScenarioName = scenarioName, + VerificationExcludePatterns = verificationExcludePatterns, + } + .WithCustomScrubbers( + ScrubbersDefinition.Empty.AddScrubber((path, content) => + { + string filePath = path.UnixifyDirSeparators(); + + if (filePath.EndsWith(".csproj")) + { + // Scrub references to just-built packages and remove the suffix, if it exists. + // This allows the snapshots to remain the same regardless of where the repo is built (e.g., locally, public CI, internal CI). + var pattern = @"(?<=)"; + content.ScrubByRegex(pattern, replacement: "$1"); + } + })); + + VerificationEngine engine = new VerificationEngine(_log); + await engine.Execute(options); + +#pragma warning disable CA1031 // Do not catch general exception types + try + { + Directory.Delete(workingDir, recursive: true); + } + catch + { + /* don't care */ + } +#pragma warning restore CA1031 // Do not catch general exception types + } +} diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/.mcp/server.json b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/.mcp/server.json new file mode 100644 index 00000000000..353a76d06ac --- /dev/null +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/.mcp/server.json @@ -0,0 +1,25 @@ +{ + "description": "", + "name": "io.github./", + "packages": [ + { + "registry_name": "nuget", + "name": "", + "version": "0.1.0-beta", + "package_arguments": [], + "environment_variables": [ + { + "description": "The maximum number to return from the random number generator", + "name": "MAX_RANDOM_NUMBER" + } + ] + } + ], + "repository": { + "url": "https://github.com//", + "source": "github" + }, + "version_detail": { + "version": "0.1.0-beta" + } +} diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs new file mode 100644 index 00000000000..2390c797ce6 --- /dev/null +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using mcpserver.Tools; + +var builder = Host.CreateApplicationBuilder(args); + +// Uncomment the following lines if you want to enforce the MCP command-line arguments. +// You can use command-line arguments to support multiple modes or commands in your application. +// These would be specified in the "package_arguments" property in the .mcp/server.json to inform client tools. +/* +if (args.Length == 0 || args[0] != "mcp") +{ + Console.Error.WriteLine("Error: invalid command. Use the 'mcp' command-line argument to start the MCP server."); + return 1; +} +*/ + +builder.Logging.AddConsole(consoleLogOptions => +{ + // Configure all logs to go to stderr (stdout is used for the MCP protocol messages). + consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace; +}); + +// Add the MCP services: the transport to use (stdio) and the tools to register. +builder.Services + .AddMcpServer() + .WithStdioServerTransport() + .WithTools(); + +await builder.Build().RunAsync(); +return 0; diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md new file mode 100644 index 00000000000..56083765735 --- /dev/null +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md @@ -0,0 +1,88 @@ +# MCP Server + +This README was created using the C# MCP server template project. It demonstrates how you can easily create an MCP server using C# and then package it in a NuGet package. + +See [aka.ms/nuget/mcp/guide](https://aka.ms/nuget/mcp/guide) for the full guide. + +## Checklist before publishing to NuGet.org + +- Test the MCP server locally using the steps below. +- Update the package metadata in the .csproj file. +- Update `.mcp/server.json` to declare your MCP server's inputs. +- Pack the project using `dotnet pack`. + +The `bin/Release` directory will contain the package file (.nupkg), which can be [published to NuGet.org](https://learn.microsoft.com/nuget/nuget-org/publish-a-package). + +## Using the MCP Server in VS Code + +Once the MCP server package is published to NuGet.org, you can use the following VS Code user configuration to download and install the MCP server package. See [Use MCP servers in VS Code (Preview)](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more information about using MCP servers in VS Code. + +```json +{ + "mcp": { + "servers": { + "mcpserver": { + "type": "stdio", + "command": "dotnet", + "args": [ + "tool", + "exec", + "", + "--version", + "", + "--yes" + ], + "env": { + "MAX_RANDOM_NUMBER": 100 + } + } + } + } +} +``` + +Now you can ask Copilot Chat for a random number, for example, `Give me 3 random numbers`. It should prompt you to use the `get_random_number` tool on the `my-custom-mcp` MCP server and show you the results. + +## Developing locally in VS Code + +To test this MCP server from source code (locally) without using a built MCP server package, create a `.vscode/mcp.json` file (a VS Code workspace settings file) in your project directory and add the following configuration: + +```json +{ + "servers": { + "mcpserver": { + "type": "stdio", + "command": "dotnet", + "args": [ + "run" + ], + "env": { + "MAX_RANDOM_NUMBER": 100 + } + } + } +} +``` + +Alternatively, you can configure your VS Code user settings to use your local project: + +```json +{ + "mcp": { + "servers": { + "mcpserver": { + "type": "stdio", + "command": "dotnet", + "args": [ + "run", + "--project", + "" + ], + "env": { + "MAX_RANDOM_NUMBER": 100 + } + } + } + } +} +``` diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs new file mode 100644 index 00000000000..3036536a494 --- /dev/null +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs @@ -0,0 +1,49 @@ +using System.ComponentModel; +using ModelContextProtocol.Server; + +namespace mcpserver.Tools; + +/// +/// Sample MCP tools for demonstration purposes. +/// These tools can be invoked by MCP clients to perform various operations. +/// +public class RandomNumberTools +{ + private readonly int _maxNumber; + + public RandomNumberTools() + { + // Process configuration settings from the environment variables. + // These will be provided by the MCP client application, such as VS Code. + // Configuration settings could be provided via dependency injection and the IOptions pattern. + var maxNumberEnv = Environment.GetEnvironmentVariable("MAX_RANDOM_NUMBER"); + if (!int.TryParse(maxNumberEnv, out var maxNumber) || maxNumber <= 0) + { + throw new InvalidOperationException("Error: you must set the MAX_RANDOM_NUMBER environment variable to a positive integer."); + } + + _maxNumber = maxNumber; + } + + /// + /// Returns the maximum random number that can be generated by the tool. + /// + /// The maximum random number. + [McpServerTool(Name = "get_max_random_number")] + [Description("Returns the maximum random number that can be generated by the tool.")] + public int GetMaxRandomNumber() + { + return _maxNumber; + } + + /// + /// Returns a random number between 1 and the maximum number allowed by the tool (inclusive). + /// + /// A random number. + [McpServerTool(Name = "get_random_number")] + [Description("Returns a random number between 1 and the maximum number allowed by the tool.")] + public int GetRandomNumber() + { + return Random.Shared.Next(1, _maxNumber + 1); + } +} diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/mcpserver.csproj b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/mcpserver.csproj new file mode 100644 index 00000000000..a71ac148e6f --- /dev/null +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/mcpserver.csproj @@ -0,0 +1,32 @@ + + + + net10.0 + Exe + enable + enable + + + true + McpServer + + + README.md + SampleMcpServer + 0.1.0-beta + AI; MCP; server; stdio + An MCP server using the MCP C# SDK. + + + + + + + + + + + + + + From 129faff81cfa19f769b6f11919b6c662d70c2f8b Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 1 Jul 2025 14:53:39 -0400 Subject: [PATCH 16/21] Remove return 0 --- .../src/McpServer/McpServer-CSharp/Program.cs | 1 - .../Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs index 59597ac0c44..10c267252be 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Program.cs @@ -29,4 +29,3 @@ .WithTools(); await builder.Build().RunAsync(); -return 0; diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs index 2390c797ce6..38cdc7dae25 100644 --- a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs @@ -29,4 +29,3 @@ .WithTools(); await builder.Build().RunAsync(); -return 0; From 6be6521100d8330509f2d78d8efc927d491de4c1 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 1 Jul 2025 15:14:09 -0400 Subject: [PATCH 17/21] Added comment --- .../Microsoft.Extensions.AI.Templates.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj index 3ae465d0979..7784747028e 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj @@ -61,6 +61,8 @@ **\NuGet.config; **\Directory.Build.targets; **\Directory.Build.props;" /> + + -{ - // Configure all logs to go to stderr (stdout is used for the MCP protocol messages). - consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace; -}); +// Configure all logs to go to stderr (stdout is used for the MCP protocol messages). +builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace); // Add the MCP services: the transport to use (stdio) and the tools to register. builder.Services diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md index ce2bd0ef01b..e1e9897e1df 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md @@ -41,7 +41,7 @@ Once the MCP server package is published to NuGet.org, you can use the following } ``` -Now you can ask Copilot Chat for a random number, for example, `Give me 3 random numbers`. It should prompt you to use the `get_random_number` tool on the `my-custom-mcp` MCP server and show you the results. +Now you can ask Copilot Chat for a random number, for example, `Give me 3 random numbers`. It should prompt you to use the `get_random_number` tool on the `McpServer-CSharp` MCP server and show you the results. ## Developing locally in VS Code diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs index af81df4c97c..aec1a8e9d96 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs @@ -1,13 +1,11 @@ using System.ComponentModel; using ModelContextProtocol.Server; -namespace McpServer_CSharp.Tools; - /// /// Sample MCP tools for demonstration purposes. /// These tools can be invoked by MCP clients to perform various operations. /// -public class RandomNumberTools +internal class RandomNumberTools { private readonly int _maxNumber; @@ -25,17 +23,6 @@ public RandomNumberTools() _maxNumber = maxNumber; } - /// - /// Returns the maximum random number that can be generated by the tool. - /// - /// The maximum random number. - [McpServerTool(Name = "get_max_random_number")] - [Description("Returns the maximum random number that can be generated by the tool.")] - public int GetMaxRandomNumber() - { - return _maxNumber; - } - /// /// Returns a random number between 1 and the maximum number allowed by the tool (inclusive). /// diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs index 38cdc7dae25..73b72d35a46 100644 --- a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Program.cs @@ -1,26 +1,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using mcpserver.Tools; var builder = Host.CreateApplicationBuilder(args); -// Uncomment the following lines if you want to enforce the MCP command-line arguments. -// You can use command-line arguments to support multiple modes or commands in your application. -// These would be specified in the "package_arguments" property in the .mcp/server.json to inform client tools. -/* -if (args.Length == 0 || args[0] != "mcp") -{ - Console.Error.WriteLine("Error: invalid command. Use the 'mcp' command-line argument to start the MCP server."); - return 1; -} -*/ - -builder.Logging.AddConsole(consoleLogOptions => -{ - // Configure all logs to go to stderr (stdout is used for the MCP protocol messages). - consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace; -}); +// Configure all logs to go to stderr (stdout is used for the MCP protocol messages). +builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace); // Add the MCP services: the transport to use (stdio) and the tools to register. builder.Services diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md index 56083765735..6bcfe120253 100644 --- a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md @@ -41,7 +41,7 @@ Once the MCP server package is published to NuGet.org, you can use the following } ``` -Now you can ask Copilot Chat for a random number, for example, `Give me 3 random numbers`. It should prompt you to use the `get_random_number` tool on the `my-custom-mcp` MCP server and show you the results. +Now you can ask Copilot Chat for a random number, for example, `Give me 3 random numbers`. It should prompt you to use the `get_random_number` tool on the `mcpserver` MCP server and show you the results. ## Developing locally in VS Code diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs index 3036536a494..398c0dd84aa 100644 --- a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs @@ -1,13 +1,11 @@ using System.ComponentModel; using ModelContextProtocol.Server; -namespace mcpserver.Tools; - /// /// Sample MCP tools for demonstration purposes. /// These tools can be invoked by MCP clients to perform various operations. /// -public class RandomNumberTools +internal class RandomNumberTools { private readonly int _maxNumber; @@ -25,17 +23,6 @@ public RandomNumberTools() _maxNumber = maxNumber; } - /// - /// Returns the maximum random number that can be generated by the tool. - /// - /// The maximum random number. - [McpServerTool(Name = "get_max_random_number")] - [Description("Returns the maximum random number that can be generated by the tool.")] - public int GetMaxRandomNumber() - { - return _maxNumber; - } - /// /// Returns a random number between 1 and the maximum number allowed by the tool (inclusive). /// From eabb0373cb990c78b596a7055112d193af099eb4 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 1 Jul 2025 17:47:49 -0400 Subject: [PATCH 19/21] Address comments --- .../McpServer-CSharp/.mcp/server.json | 7 +----- .../src/McpServer/McpServer-CSharp/README.md | 18 ++++---------- .../Tools/RandomNumberTools.cs | 24 ++++--------------- .../mcpserver/.mcp/server.json | 7 +----- .../mcpserver/README.md | 18 ++++---------- .../mcpserver/Tools/RandomNumberTools.cs | 24 ++++--------------- 6 files changed, 22 insertions(+), 76 deletions(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.mcp/server.json b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.mcp/server.json index 0ec7270e177..d4b9d0edf5b 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.mcp/server.json +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.mcp/server.json @@ -7,12 +7,7 @@ "name": "", "version": "0.1.0-beta", "package_arguments": [], - "environment_variables": [ - { - "description": "The maximum number to return from the random number generator", - "name": "MAX_RANDOM_NUMBER" - } - ] + "environment_variables": [] } ], "repository": { diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md index e1e9897e1df..eb64abe2508 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md @@ -7,8 +7,9 @@ See [aka.ms/nuget/mcp/guide](https://aka.ms/nuget/mcp/guide) for the full guide. ## Checklist before publishing to NuGet.org - Test the MCP server locally using the steps below. -- Update the package metadata in the .csproj file. +- Update the package metadata in the .csproj file, in particular the ``. - Update `.mcp/server.json` to declare your MCP server's inputs. + - See [configuring inputs](https://aka.ms/nuget/mcp/guide/configuring-inputs) for more details. - Pack the project using `dotnet pack`. The `bin/Release` directory will contain the package file (.nupkg), which can be [published to NuGet.org](https://learn.microsoft.com/nuget/nuget-org/publish-a-package). @@ -31,10 +32,7 @@ Once the MCP server package is published to NuGet.org, you can use the following "--version", "", "--yes" - ], - "env": { - "MAX_RANDOM_NUMBER": 100 - } + ] } } } @@ -55,10 +53,7 @@ To test this MCP server from source code (locally) without using a built MCP ser "command": "dotnet", "args": [ "run" - ], - "env": { - "MAX_RANDOM_NUMBER": 100 - } + ] } } } @@ -77,10 +72,7 @@ Alternatively, you can configure your VS Code user settings to use your local pr "run", "--project", "" - ], - "env": { - "MAX_RANDOM_NUMBER": 100 - } + ] } } } diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs index aec1a8e9d96..0b9dd3b0e91 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs @@ -7,30 +7,16 @@ /// internal class RandomNumberTools { - private readonly int _maxNumber; - - public RandomNumberTools() - { - // Process configuration settings from the environment variables. - // These will be provided by the MCP client application, such as VS Code. - // Configuration settings could be provided via dependency injection and the IOptions pattern. - var maxNumberEnv = Environment.GetEnvironmentVariable("MAX_RANDOM_NUMBER"); - if (!int.TryParse(maxNumberEnv, out var maxNumber) || maxNumber <= 0) - { - throw new InvalidOperationException("Error: you must set the MAX_RANDOM_NUMBER environment variable to a positive integer."); - } - - _maxNumber = maxNumber; - } - /// /// Returns a random number between 1 and the maximum number allowed by the tool (inclusive). /// /// A random number. [McpServerTool(Name = "get_random_number")] - [Description("Returns a random number between 1 and the maximum number allowed by the tool.")] - public int GetRandomNumber() + [Description("Generates a random number between the specified minimum and maximum values.")] + public int GetRandomNumber( + [Description("Minimum value (inclusive)")] int min = 0, + [Description("Maximum value (exclusive)")] int max = 100) { - return Random.Shared.Next(1, _maxNumber + 1); + return Random.Shared.Next(min, max); } } diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/.mcp/server.json b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/.mcp/server.json index 353a76d06ac..ab997541e52 100644 --- a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/.mcp/server.json +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/.mcp/server.json @@ -7,12 +7,7 @@ "name": "", "version": "0.1.0-beta", "package_arguments": [], - "environment_variables": [ - { - "description": "The maximum number to return from the random number generator", - "name": "MAX_RANDOM_NUMBER" - } - ] + "environment_variables": [] } ], "repository": { diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md index 6bcfe120253..8e6185b27aa 100644 --- a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md @@ -7,8 +7,9 @@ See [aka.ms/nuget/mcp/guide](https://aka.ms/nuget/mcp/guide) for the full guide. ## Checklist before publishing to NuGet.org - Test the MCP server locally using the steps below. -- Update the package metadata in the .csproj file. +- Update the package metadata in the .csproj file, in particular the ``. - Update `.mcp/server.json` to declare your MCP server's inputs. + - See [configuring inputs](https://aka.ms/nuget/mcp/guide/configuring-inputs) for more details. - Pack the project using `dotnet pack`. The `bin/Release` directory will contain the package file (.nupkg), which can be [published to NuGet.org](https://learn.microsoft.com/nuget/nuget-org/publish-a-package). @@ -31,10 +32,7 @@ Once the MCP server package is published to NuGet.org, you can use the following "--version", "", "--yes" - ], - "env": { - "MAX_RANDOM_NUMBER": 100 - } + ] } } } @@ -55,10 +53,7 @@ To test this MCP server from source code (locally) without using a built MCP ser "command": "dotnet", "args": [ "run" - ], - "env": { - "MAX_RANDOM_NUMBER": 100 - } + ] } } } @@ -77,10 +72,7 @@ Alternatively, you can configure your VS Code user settings to use your local pr "run", "--project", "" - ], - "env": { - "MAX_RANDOM_NUMBER": 100 - } + ] } } } diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs index 398c0dd84aa..1a89f5593fe 100644 --- a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs @@ -7,30 +7,16 @@ /// internal class RandomNumberTools { - private readonly int _maxNumber; - - public RandomNumberTools() - { - // Process configuration settings from the environment variables. - // These will be provided by the MCP client application, such as VS Code. - // Configuration settings could be provided via dependency injection and the IOptions pattern. - var maxNumberEnv = Environment.GetEnvironmentVariable("MAX_RANDOM_NUMBER"); - if (!int.TryParse(maxNumberEnv, out var maxNumber) || maxNumber <= 0) - { - throw new InvalidOperationException("Error: you must set the MAX_RANDOM_NUMBER environment variable to a positive integer."); - } - - _maxNumber = maxNumber; - } - /// /// Returns a random number between 1 and the maximum number allowed by the tool (inclusive). /// /// A random number. [McpServerTool(Name = "get_random_number")] - [Description("Returns a random number between 1 and the maximum number allowed by the tool.")] - public int GetRandomNumber() + [Description("Generates a random number between the specified minimum and maximum values.")] + public int GetRandomNumber( + [Description("Minimum value (inclusive)")] int min = 0, + [Description("Maximum value (exclusive)")] int max = 100) { - return Random.Shared.Next(1, _maxNumber + 1); + return Random.Shared.Next(min, max); } } From e29a10117f7605194eece0de6c2a7538f9223628 Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 1 Jul 2025 17:53:51 -0400 Subject: [PATCH 20/21] =?UTF-8?q?Remove=20comment=20that=20is=20wrong=20?= =?UTF-8?q?=E2=98=A0=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs | 4 ---- .../mcpserver/Tools/RandomNumberTools.cs | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs index 0b9dd3b0e91..4542f8505a5 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/Tools/RandomNumberTools.cs @@ -7,10 +7,6 @@ /// internal class RandomNumberTools { - /// - /// Returns a random number between 1 and the maximum number allowed by the tool (inclusive). - /// - /// A random number. [McpServerTool(Name = "get_random_number")] [Description("Generates a random number between the specified minimum and maximum values.")] public int GetRandomNumber( diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs index 1a89f5593fe..72af767e320 100644 --- a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/Tools/RandomNumberTools.cs @@ -7,10 +7,6 @@ /// internal class RandomNumberTools { - /// - /// Returns a random number between 1 and the maximum number allowed by the tool (inclusive). - /// - /// A random number. [McpServerTool(Name = "get_random_number")] [Description("Generates a random number between the specified minimum and maximum values.")] public int GetRandomNumber( From 9b8a76794a5e05fad98f0877d2cd15b77bb6293c Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Tue, 1 Jul 2025 18:24:42 -0400 Subject: [PATCH 21/21] Address comment --- .../src/McpServer/McpServer-CSharp/README.md | 6 ++++-- .../Snapshots/mcpserver.Basic.verified/mcpserver/README.md | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md index eb64abe2508..dc6f5038b61 100644 --- a/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md +++ b/src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md @@ -52,7 +52,9 @@ To test this MCP server from source code (locally) without using a built MCP ser "type": "stdio", "command": "dotnet", "args": [ - "run" + "run", + "--project", + "" ] } } @@ -71,7 +73,7 @@ Alternatively, you can configure your VS Code user settings to use your local pr "args": [ "run", "--project", - "" + "" ] } } diff --git a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md index 8e6185b27aa..25704e5d135 100644 --- a/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md +++ b/test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/Snapshots/mcpserver.Basic.verified/mcpserver/README.md @@ -52,7 +52,9 @@ To test this MCP server from source code (locally) without using a built MCP ser "type": "stdio", "command": "dotnet", "args": [ - "run" + "run", + "--project", + "" ] } } @@ -71,7 +73,7 @@ Alternatively, you can configure your VS Code user settings to use your local pr "args": [ "run", "--project", - "" + "" ] } }