Skip to content

Commit 498c427

Browse files
joelverhagenstephentoub
authored andcommitted
Create a project template for an MCP server (#6547)
The conventions we're pushing for on NuGet are: 1. `PackAsTool` 2. `McpServer` package type (in addition to the default `DotnetTool`) 3. Embed server.json This provides an `mcpserver` template as part of `Microsoft.Extensions.AI.Templates`. This currently only covers a local MCP server, with stdio transport.
1 parent a1bf53c commit 498c427

File tree

17 files changed

+508
-0
lines changed

17 files changed

+508
-0
lines changed

src/ProjectTemplates/GeneratedContent.targets

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<_LocalChatTemplateVariant>aspire</_LocalChatTemplateVariant>
1111

1212
<_ChatWithCustomDataContentRoot>$(MSBuildThisFileDirectory)Microsoft.Extensions.AI.Templates\src\ChatWithCustomData\</_ChatWithCustomDataContentRoot>
13+
<_McpServerContentRoot>$(MSBuildThisFileDirectory)Microsoft.Extensions.AI.Templates\src\McpServer\</_McpServerContentRoot>
1314
</PropertyGroup>
1415

1516
<Target Name="ComputeGeneratedContentProperties">
@@ -34,9 +35,11 @@
3435
<TemplatePackageVersion_AzureIdentity>1.14.0</TemplatePackageVersion_AzureIdentity>
3536
<TemplatePackageVersion_AzureSearchDocuments>11.6.0</TemplatePackageVersion_AzureSearchDocuments>
3637
<TemplatePackageVersion_CommunityToolkitAspire>9.4.1-beta.291</TemplatePackageVersion_CommunityToolkitAspire>
38+
<TemplatePackageVersion_MicrosoftExtensionsHosting>10.0.0-preview.5.25277.114</TemplatePackageVersion_MicrosoftExtensionsHosting>
3739
<TemplatePackageVersion_MicrosoftExtensionsServiceDiscovery>9.3.0</TemplatePackageVersion_MicrosoftExtensionsServiceDiscovery>
3840
<TemplatePackageVersion_MicrosoftSemanticKernel>1.53.0</TemplatePackageVersion_MicrosoftSemanticKernel>
3941
<TemplatePackageVersion_MicrosoftSemanticKernel_Preview>1.53.0-preview</TemplatePackageVersion_MicrosoftSemanticKernel_Preview>
42+
<TemplatePackageVersion_ModelContextProtocol>0.3.0-preview.1</TemplatePackageVersion_ModelContextProtocol>
4043
<TemplatePackageVersion_OllamaSharp>5.1.18</TemplatePackageVersion_OllamaSharp>
4144
<TemplatePackageVersion_OpenTelemetry>1.12.0</TemplatePackageVersion_OpenTelemetry>
4245
<TemplatePackageVersion_PdfPig>0.1.10</TemplatePackageVersion_PdfPig>
@@ -64,9 +67,11 @@
6467
TemplatePackageVersion_AzureIdentity=$(TemplatePackageVersion_AzureIdentity);
6568
TemplatePackageVersion_AzureSearchDocuments=$(TemplatePackageVersion_AzureSearchDocuments);
6669
TemplatePackageVersion_CommunityToolkitAspire=$(TemplatePackageVersion_CommunityToolkitAspire);
70+
TemplatePackageVersion_MicrosoftExtensionsHosting=$(TemplatePackageVersion_MicrosoftExtensionsHosting);
6771
TemplatePackageVersion_MicrosoftExtensionsServiceDiscovery=$(TemplatePackageVersion_MicrosoftExtensionsServiceDiscovery);
6872
TemplatePackageVersion_MicrosoftSemanticKernel=$(TemplatePackageVersion_MicrosoftSemanticKernel);
6973
TemplatePackageVersion_MicrosoftSemanticKernel_Preview=$(TemplatePackageVersion_MicrosoftSemanticKernel_Preview);
74+
TemplatePackageVersion_ModelContextProtocol=$(TemplatePackageVersion_ModelContextProtocol);
7075
TemplatePackageVersion_OllamaSharp=$(TemplatePackageVersion_OllamaSharp);
7176
TemplatePackageVersion_OpenTelemetry=$(TemplatePackageVersion_OpenTelemetry);
7277
TemplatePackageVersion_PdfPig=$(TemplatePackageVersion_PdfPig);
@@ -100,6 +105,9 @@
100105
<GeneratedContent
101106
Include="$(_ChatWithCustomDataContentRoot)ChatWithCustomData-CSharp.ServiceDefaults\ChatWithCustomData-CSharp.ServiceDefaults.csproj.in"
102107
OutputPath="$(_ChatWithCustomDataContentRoot)ChatWithCustomData-CSharp.ServiceDefaults\ChatWithCustomData-CSharp.ServiceDefaults.csproj" />
108+
<GeneratedContent
109+
Include="$(_McpServerContentRoot)McpServer-CSharp\McpServer-CSharp.csproj.in"
110+
OutputPath="$(_McpServerContentRoot)McpServer-CSharp\McpServer-CSharp.csproj" />
103111

104112
<!-- The following content only gets generated when using just-built packages -->
105113
<_GeneratedContentEnablingJustBuiltPackages

src/ProjectTemplates/Microsoft.Extensions.AI.Templates/Microsoft.Extensions.AI.Templates.csproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@
6161
**\NuGet.config;
6262
**\Directory.Build.targets;
6363
**\Directory.Build.props;" />
64+
65+
<!-- Keep the exclude patterns below in sync with those in McpServerSnapshotTests.cs -->
66+
<Content
67+
Include="src\McpServer\**\*"
68+
Exclude="
69+
**\bin\**;
70+
**\obj\**;
71+
**\.vs\**;
72+
**\*.sln;
73+
**\*.in;" />
6474
<None Include="THIRD-PARTY-NOTICES.TXT" Pack="true" PackagePath="." />
6575
<Compile Remove="**\*" />
6676
</ItemGroup>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"description": "<your description here>",
3+
"name": "io.github.<your GitHub username here>/<your repo name>",
4+
"packages": [
5+
{
6+
"registry_name": "nuget",
7+
"name": "<your package ID here>",
8+
"version": "0.1.0-beta",
9+
"package_arguments": [],
10+
"environment_variables": []
11+
}
12+
],
13+
"repository": {
14+
"url": "https://github.com/<your GitHub username here>/<your repo name>",
15+
"source": "github"
16+
},
17+
"version_detail": {
18+
"version": "0.1.0-beta"
19+
}
20+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "https://json.schemastore.org/dotnetcli.host",
3+
"symbolInfo": {},
4+
"usageExamples": [
5+
""
6+
]
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "https://json.schemastore.org/ide.host",
3+
"order": 0,
4+
"icon": "ide/icon.ico",
5+
"symbolInfo": []
6+
}
37.2 KB
Binary file not shown.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"$schema": "http://json.schemastore.org/template",
3+
"author": "Microsoft",
4+
"classifications": [
5+
"Common",
6+
"AI",
7+
"MCP"
8+
],
9+
"identity": "Microsoft.Extensions.AI.Templates.McpServer.CSharp",
10+
"name": "Local MCP Server Console App",
11+
"description": "A project template for creating a Model Context Protocol (MCP) server using C# and the ModelContextProtocol package.",
12+
"shortName": "mcpserver",
13+
"defaultName": "McpServer",
14+
"sourceName": "McpServer-CSharp",
15+
"preferNameDirectory": true,
16+
"tags": {
17+
"language": "C#",
18+
"type": "project"
19+
},
20+
"symbols": {
21+
"hostIdentifier": {
22+
"type": "bind",
23+
"binding": "HostIdentifier"
24+
}
25+
},
26+
"primaryOutputs": [
27+
{
28+
"path": "./README.md"
29+
},
30+
{
31+
"path": "./McpServer-CSharp.csproj"
32+
}
33+
],
34+
"postActions": [
35+
{
36+
"condition": "(hostIdentifier != \"dotnetcli\" && hostIdentifier != \"dotnetcli-preview\")",
37+
"description": "Opens README file in the editor",
38+
"manualInstructions": [],
39+
"actionId": "84C0DA21-51C8-4541-9940-6CA19AF04EE6",
40+
"args": {
41+
"files": "0"
42+
},
43+
"continueOnError": true
44+
}
45+
]
46+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<OutputType>Exe</OutputType>
6+
<Nullable>enable</Nullable>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
9+
<!-- Set up the NuGet package to be an MCP server -->
10+
<PackAsTool>true</PackAsTool>
11+
<PackageType>McpServer</PackageType>
12+
13+
<!-- Set recommended package metadata -->
14+
<PackageReadmeFile>README.md</PackageReadmeFile>
15+
<PackageId>SampleMcpServer</PackageId>
16+
<PackageVersion>0.1.0-beta</PackageVersion>
17+
<PackageTags>AI; MCP; server; stdio</PackageTags>
18+
<Description>An MCP server using the MCP C# SDK.</Description>
19+
</PropertyGroup>
20+
21+
<!-- Include additional files for browsing the MCP server. -->
22+
<ItemGroup>
23+
<None Include=".mcp\server.json" Pack="true" PackagePath="/.mcp/" />
24+
<None Include="README.md" Pack="true" PackagePath="/" />
25+
</ItemGroup>
26+
27+
<ItemGroup>
28+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="${TemplatePackageVersion_MicrosoftExtensionsHosting}" />
29+
<PackageReference Include="ModelContextProtocol" Version="${TemplatePackageVersion_ModelContextProtocol}" />
30+
</ItemGroup>
31+
32+
</Project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Hosting;
3+
using Microsoft.Extensions.Logging;
4+
5+
var builder = Host.CreateApplicationBuilder(args);
6+
7+
// Configure all logs to go to stderr (stdout is used for the MCP protocol messages).
8+
builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace);
9+
10+
// Add the MCP services: the transport to use (stdio) and the tools to register.
11+
builder.Services
12+
.AddMcpServer()
13+
.WithStdioServerTransport()
14+
.WithTools<RandomNumberTools>();
15+
16+
await builder.Build().RunAsync();
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# MCP Server
2+
3+
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.
4+
5+
See [aka.ms/nuget/mcp/guide](https://aka.ms/nuget/mcp/guide) for the full guide.
6+
7+
## Checklist before publishing to NuGet.org
8+
9+
- Test the MCP server locally using the steps below.
10+
- Update the package metadata in the .csproj file, in particular the `<PackageId>`.
11+
- Update `.mcp/server.json` to declare your MCP server's inputs.
12+
- See [configuring inputs](https://aka.ms/nuget/mcp/guide/configuring-inputs) for more details.
13+
- Pack the project using `dotnet pack`.
14+
15+
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).
16+
17+
## Using the MCP Server in VS Code
18+
19+
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.
20+
21+
```json
22+
{
23+
"mcp": {
24+
"servers": {
25+
"McpServer-CSharp": {
26+
"type": "stdio",
27+
"command": "dotnet",
28+
"args": [
29+
"tool",
30+
"exec",
31+
"<your package ID here>",
32+
"--version",
33+
"<your package version here>",
34+
"--yes"
35+
]
36+
}
37+
}
38+
}
39+
}
40+
```
41+
42+
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.
43+
44+
## Developing locally in VS Code
45+
46+
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:
47+
48+
```json
49+
{
50+
"servers": {
51+
"McpServer-CSharp": {
52+
"type": "stdio",
53+
"command": "dotnet",
54+
"args": [
55+
"run",
56+
"--project",
57+
"<RELATIVE PATH TO PROJECT DIRECTORY>"
58+
]
59+
}
60+
}
61+
}
62+
```
63+
64+
Alternatively, you can configure your VS Code user settings to use your local project:
65+
66+
```json
67+
{
68+
"mcp": {
69+
"servers": {
70+
"McpServer-CSharp": {
71+
"type": "stdio",
72+
"command": "dotnet",
73+
"args": [
74+
"run",
75+
"--project",
76+
"<FULL PATH TO PROJECT DIRECTORY>"
77+
]
78+
}
79+
}
80+
}
81+
}
82+
```

0 commit comments

Comments
 (0)