Skip to content

Commit c498de0

Browse files
authored
Update MCP server template (#6772)
1 parent 282b96c commit c498de0

File tree

23 files changed

+744
-9
lines changed

23 files changed

+744
-9
lines changed
Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
11
{
22
"$schema": "https://json.schemastore.org/dotnetcli.host",
3-
"symbolInfo": {},
3+
"symbolInfo": {
4+
"TargetFrameworkOverride": {
5+
"isHidden": "true",
6+
"longName": "target-framework-override",
7+
"shortName": ""
8+
},
9+
"Framework": {
10+
"longName": "framework"
11+
},
12+
"NativeAot": {
13+
"longName": "aot",
14+
"shortName": ""
15+
},
16+
"SelfContained": {
17+
"longName": "self-contained",
18+
"shortName": ""
19+
}
20+
},
421
"usageExamples": [
522
""
623
]
7-
}
24+
}

src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/ide.host.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,14 @@
22
"$schema": "https://json.schemastore.org/ide.host",
33
"order": 0,
44
"icon": "ide/icon.ico",
5-
"symbolInfo": []
5+
"symbolInfo": [
6+
{
7+
"id": "NativeAot",
8+
"isVisible": true
9+
},
10+
{
11+
"id": "SelfContained",
12+
"isVisible": true
13+
}
14+
]
615
}

src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/.template.config/template.json

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,53 @@
1818
"type": "project"
1919
},
2020
"symbols": {
21+
"TargetFrameworkOverride": {
22+
"type": "parameter",
23+
"description": "Overrides the target framework",
24+
"displayName": "Target framework override",
25+
"replaces": "TargetFrameworkOverride",
26+
"datatype": "string",
27+
"defaultValue": ""
28+
},
29+
"Framework": {
30+
"type": "parameter",
31+
"description": "The target framework for the project.",
32+
"displayName": "Framework",
33+
"datatype": "choice",
34+
"choices": [
35+
{
36+
"choice": "net10.0",
37+
"description": ".NET 10"
38+
},
39+
{
40+
"choice": "net9.0",
41+
"description": ".NET 9"
42+
},
43+
{
44+
"choice": "net8.0",
45+
"description": ".NET 8"
46+
}
47+
],
48+
"replaces": "net9.0",
49+
"defaultValue": "net9.0"
50+
},
2151
"hostIdentifier": {
2252
"type": "bind",
2353
"binding": "HostIdentifier"
54+
},
55+
"NativeAot": {
56+
"type": "parameter",
57+
"datatype": "bool",
58+
"defaultValue": "false",
59+
"displayName": "Enable _native AOT publish",
60+
"description": "Whether to enable the MCP server for publishing as a native AOT application."
61+
},
62+
"SelfContained": {
63+
"type": "parameter",
64+
"datatype": "bool",
65+
"defaultValue": "true",
66+
"displayName": "Enable _self-contained publish",
67+
"description": "Whether to enable the MCP server for publishing as a self-contained application."
2468
}
2569
},
2670
"primaryOutputs": [
@@ -42,5 +86,23 @@
4286
},
4387
"continueOnError": true
4488
}
45-
]
89+
],
90+
"SpecialCustomOperations": {
91+
"**/*.md": {
92+
"operations": [
93+
{
94+
"type": "conditional",
95+
"configuration": {
96+
"if": [ "#### ---#if" ],
97+
"else": [ "#### ---#else" ],
98+
"elseif": [ "#### ---#elseif", "#### ---#elif" ],
99+
"endif": [ "#### ---#endif" ],
100+
"trim": "true",
101+
"wholeLine": "true",
102+
"evaluator": "C++"
103+
}
104+
}
105+
]
106+
}
107+
}
46108
}

src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/McpServer-CSharp.csproj.in

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">net9.0</TargetFramework>
5+
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
6+
<!--#if (SelfContained || NativeAot)-->
7+
<RuntimeIdentifiers>win-x64;win-arm64;osx-arm64;linux-x64;linux-arm64;linux-musl-x64</RuntimeIdentifiers>
8+
<!--#else-->
59
<RollForward>Major</RollForward>
10+
<!--#endif -->
611
<OutputType>Exe</OutputType>
712
<Nullable>enable</Nullable>
813
<ImplicitUsings>enable</ImplicitUsings>
914

1015
<!-- Set up the NuGet package to be an MCP server -->
1116
<PackAsTool>true</PackAsTool>
1217
<PackageType>McpServer</PackageType>
18+
<!--#if (SelfContained || NativeAot)-->
19+
20+
<!-- Set up the MCP server to be a self-contained application that does not rely on a shared framework -->
21+
<SelfContained>true</SelfContained>
22+
<PublishSelfContained>true</PublishSelfContained>
23+
24+
<!-- Set up the MCP server to be a single file executable -->
25+
<PublishSingleFile>true</PublishSingleFile>
26+
<!--#endif -->
27+
<!--#if (NativeAot)-->
28+
29+
<!-- Configure the MCP server to be a native AOT application with invariant globalization -->
30+
<PublishAot>true</PublishAot>
31+
<InvariantGlobalization>true</InvariantGlobalization>
32+
<!--#endif -->
1333

1434
<!-- Set recommended package metadata -->
1535
<PackageReadmeFile>README.md</PackageReadmeFile>

src/ProjectTemplates/Microsoft.Extensions.AI.Templates/src/McpServer/McpServer-CSharp/README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
# MCP Server
22

3-
This README was created using the C# MCP server project template. It demonstrates how you can easily create an MCP server using C# and publish it as a NuGet package.
3+
This README was created using the C# MCP server project template.
4+
It demonstrates how you can easily create an MCP server using C# and publish it as a NuGet package.
5+
6+
#### ---#if (SelfContained)
7+
The MCP server is built as a self-contained application and does not require the .NET runtime to be installed on the target machine.
8+
However, since it is self-contained, it must be built for each target platform separately.
9+
By default, the template is configured to build for:
10+
* `win-x64`
11+
* `win-arm64`
12+
* `osx-arm64`
13+
* `linux-x64`
14+
* `linux-arm64`
15+
* `linux-musl-x64`
16+
17+
If your users require more platforms to be supported, update the list of runtime identifiers in the project's `<RuntimeIdentifiers />` element.
18+
#### ---#else
19+
The MCP server is built as a framework-dependent application and requires the .NET runtime to be installed on the target machine.
20+
The application is configured to roll-forward to the next highest major version of the runtime if one is available on the target machine.
21+
If an applicable .NET runtime is not available, the MCP server will not start.
22+
Consider building the MCP server as a self-contained application if you want to avoid this dependency.
23+
#### ---#endif
424

525
See [aka.ms/nuget/mcp/guide](https://aka.ms/nuget/mcp/guide) for the full guide.
626

test/ProjectTemplates/Microsoft.Extensions.AI.Templates.IntegrationTests/McpServerSnapshotTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@ public async Task BasicTest()
4141
await TestTemplateCoreAsync(scenarioName: "Basic");
4242
}
4343

44+
[Fact]
45+
public async Task SelfContainedFalse()
46+
{
47+
await TestTemplateCoreAsync(scenarioName: "SelfContainedFalse", templateArgs: ["--self-contained", bool.FalseString]);
48+
}
49+
50+
[Fact]
51+
public async Task AotTrue()
52+
{
53+
await TestTemplateCoreAsync(scenarioName: "AotTrue", templateArgs: ["--aot", bool.TrueString]);
54+
}
55+
56+
[Fact]
57+
public async Task Net10()
58+
{
59+
await TestTemplateCoreAsync(scenarioName: "net10", templateArgs: ["--framework", "net10.0"]);
60+
}
61+
4462
private async Task TestTemplateCoreAsync(string scenarioName, IEnumerable<string>? templateArgs = null)
4563
{
4664
string workingDir = TestUtils.CreateTemporaryFolder();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "https://modelcontextprotocol.io/schemas/draft/2025-07-09/server.json",
3+
"description": "<your description here>",
4+
"name": "io.github.<your GitHub username here>/<your repo name>",
5+
"packages": [
6+
{
7+
"registry_name": "nuget",
8+
"name": "<your package ID here>",
9+
"version": "0.1.0-beta",
10+
"package_arguments": [],
11+
"environment_variables": []
12+
}
13+
],
14+
"repository": {
15+
"url": "https://github.com/<your GitHub username here>/<your repo name>",
16+
"source": "github"
17+
},
18+
"version_detail": {
19+
"version": "0.1.0-beta"
20+
}
21+
}
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: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# MCP Server
2+
3+
This README was created using the C# MCP server project template.
4+
It demonstrates how you can easily create an MCP server using C# and publish it as a NuGet package.
5+
6+
The MCP server is built as a self-contained application and does not require the .NET runtime to be installed on the target machine.
7+
However, since it is self-contained, it must be built for each target platform separately.
8+
By default, the template is configured to build for:
9+
* `win-x64`
10+
* `win-arm64`
11+
* `osx-arm64`
12+
* `linux-x64`
13+
* `linux-arm64`
14+
* `linux-musl-x64`
15+
16+
If your users require more platforms to be supported, update the list of runtime identifiers in the project's `<RuntimeIdentifiers />` element.
17+
18+
See [aka.ms/nuget/mcp/guide](https://aka.ms/nuget/mcp/guide) for the full guide.
19+
20+
Please note that this template is currently in an early preview stage. If you have feedback, please take a [brief survey](http://aka.ms/dotnet-mcp-template-survey).
21+
22+
## Checklist before publishing to NuGet.org
23+
24+
- Test the MCP server locally using the steps below.
25+
- Update the package metadata in the .csproj file, in particular the `<PackageId>`.
26+
- Update `.mcp/server.json` to declare your MCP server's inputs.
27+
- See [configuring inputs](https://aka.ms/nuget/mcp/guide/configuring-inputs) for more details.
28+
- Pack the project using `dotnet pack`.
29+
30+
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+
32+
## Developing locally
33+
34+
To test this MCP server from source code (locally) without using a built MCP server package, you can configure your IDE to run the project directly using `dotnet run`.
35+
36+
```json
37+
{
38+
"servers": {
39+
"mcpserver": {
40+
"type": "stdio",
41+
"command": "dotnet",
42+
"args": [
43+
"run",
44+
"--project",
45+
"<PATH TO PROJECT DIRECTORY>"
46+
]
47+
}
48+
}
49+
}
50+
```
51+
52+
## Testing the MCP Server
53+
54+
Once configured, 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.
55+
56+
## Publishing to NuGet.org
57+
58+
1. Run `dotnet pack -c Release` to create the NuGet package
59+
2. Publish to NuGet.org with `dotnet nuget push bin/Release/*.nupkg --api-key <your-api-key> --source https://api.nuget.org/v3/index.json`
60+
61+
## Using the MCP Server from NuGet.org
62+
63+
Once the MCP server package is published to NuGet.org, you can configure it in your preferred IDE. Both VS Code and Visual Studio use the `dnx` command to download and install the MCP server package from NuGet.org.
64+
65+
- **VS Code**: Create a `<WORKSPACE DIRECTORY>/.vscode/mcp.json` file
66+
- **Visual Studio**: Create a `<SOLUTION DIRECTORY>\.mcp.json` file
67+
68+
For both VS Code and Visual Studio, the configuration file uses the following server definition:
69+
70+
```json
71+
{
72+
"servers": {
73+
"mcpserver": {
74+
"type": "stdio",
75+
"command": "dnx",
76+
"args": [
77+
"<your package ID here>",
78+
"--version",
79+
"<your package version here>",
80+
"--yes"
81+
]
82+
}
83+
}
84+
}
85+
```
86+
87+
## More information
88+
89+
.NET MCP servers use the [ModelContextProtocol](https://www.nuget.org/packages/ModelContextProtocol) C# SDK. For more information about MCP:
90+
91+
- [Official Documentation](https://modelcontextprotocol.io/)
92+
- [Protocol Specification](https://spec.modelcontextprotocol.io/)
93+
- [GitHub Organization](https://github.com/modelcontextprotocol)
94+
95+
Refer to the VS Code or Visual Studio documentation for more information on configuring and using MCP servers:
96+
97+
- [Use MCP servers in VS Code (Preview)](https://code.visualstudio.com/docs/copilot/chat/mcp-servers)
98+
- [Use MCP servers in Visual Studio (Preview)](https://learn.microsoft.com/visualstudio/ide/mcp-servers)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.ComponentModel;
2+
using ModelContextProtocol.Server;
3+
4+
/// <summary>
5+
/// Sample MCP tools for demonstration purposes.
6+
/// These tools can be invoked by MCP clients to perform various operations.
7+
/// </summary>
8+
internal class RandomNumberTools
9+
{
10+
[McpServerTool]
11+
[Description("Generates a random number between the specified minimum and maximum values.")]
12+
public int GetRandomNumber(
13+
[Description("Minimum value (inclusive)")] int min = 0,
14+
[Description("Maximum value (exclusive)")] int max = 100)
15+
{
16+
return Random.Shared.Next(min, max);
17+
}
18+
}

0 commit comments

Comments
 (0)