-
Notifications
You must be signed in to change notification settings - Fork 6k
Add quickstart for minimal MCP Server using .NET and publish to NuGet #47007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
JonDouglas
wants to merge
52
commits into
main
Choose a base branch
from
mcp-nuget
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
52 commits
Select commit
Hold shift + click to select a range
0898b44
Initial draft
JonDouglas 0929dc4
Fix image path
JonDouglas cfd6d95
Add to ToC
JonDouglas 2e218fc
Fix NuGet link
JonDouglas 881fdb4
Fix download link
JonDouglas 67feebd
Add VS docs
JonDouglas 15e1148
Update docs/ai/quickstarts/build-mcp-server-publish-nuget.md
JonDouglas 3b81b04
Update docs/ai/quickstarts/build-mcp-server-publish-nuget.md
JonDouglas 7da8068
Update docs/ai/quickstarts/build-mcp-server-publish-nuget.md
JonDouglas 73dcd40
Update docs/ai/quickstarts/build-mcp-server-publish-nuget.md
JonDouglas 36301eb
Update docs/ai/quickstarts/build-mcp-server-publish-nuget.md
JonDouglas d57afb3
Update docs/ai/quickstarts/build-mcp-server-publish-nuget.md
JonDouglas 2eebf55
Add to list
JonDouglas 9b44d02
Update pkg args
JonDouglas 3b6816e
Fix indention, use auto-number, fix tool name based on latest templat…
joelverhagen 9238f77
Undo
joelverhagen 4a6351d
Update docs/ai/quickstarts/build-mcp-server-publish-nuget.md
joelverhagen f2e4f02
Update image with new tool name
joelverhagen c04f9e7
Merge into existing quickstart
joelverhagen a525a33
Improve
joelverhagen eb6d4e4
Fix lint issues
joelverhagen 790d356
Fix up
joelverhagen a9e8b7d
Improve sample, suggest int environment as an option
joelverhagen c402d78
Fix
joelverhagen b145877
Fix method name
joelverhagen 12f65e1
Word
joelverhagen 5f63725
Fix lint
joelverhagen 7950495
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen 72323e6
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen 2903727
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen 5aaf9ca
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen 62b7731
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen 444dda7
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen 6aa8a64
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen 98b668b
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen af7bb13
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen e7eef78
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen 384ca48
Indent and polish
joelverhagen 5ece1ef
Move to snippet
joelverhagen 3720db2
Use full snippet
joelverhagen 1c7b5ee
Lint
joelverhagen 6e6d072
Fix rename references
joelverhagen 13d8f52
Update docs/ai/quickstarts/build-mcp-server.md
joelverhagen d6ece12
Fix URLs
joelverhagen cff7ef1
Add NuGet.org screenshots
joelverhagen 5d71c33
Bigger screenshots
joelverhagen a2e9b7d
Use real template version
joelverhagen 295ea8d
Add a trouble shooting section for tool not being called
joelverhagen e8ead19
Update embedded sample
joelverhagen b4c2ca8
Update screenshot
joelverhagen 140b995
Fix up inputs
joelverhagen e74233c
Polish
joelverhagen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,54 @@ | ||
--- | ||
title: Quickstart - Create a minimal MCP Server using .NET | ||
description: Learn to create and connect to a minimal MCP server using .NET | ||
ms.date: 05/21/2025 | ||
title: Quickstart - Create a minimal MCP server and publish to NuGet | ||
description: Learn to create and connect to a minimal MCP server using C# and publish it to NuGet. | ||
ms.date: 07/02/2025 | ||
ms.topic: quickstart | ||
ms.custom: devx-track-dotnet, devx-track-dotnet-ai | ||
author: alexwolfmsft | ||
ms.author: alexwolf | ||
--- | ||
|
||
# Create and connect to a minimal MCP server using .NET | ||
# Create a minimal MCP server using C# and publish to NuGet | ||
|
||
In this quickstart, you create a minimal Model Context Protocol (MCP) server using the [C# SDK for MCP](https://github.com/modelcontextprotocol/csharp-sdk) and connect to it using GitHub Copilot. MCP servers are services that expose capabilities to clients through the Model Context Protocol (MCP). | ||
In this quickstart, you create a minimal Model Context Protocol (MCP) server using the [C# SDK for MCP](https://github.com/modelcontextprotocol/csharp-sdk), connect to it using GitHub Copilot, and publish it to NuGet. MCP servers are services that expose capabilities to clients through the Model Context Protocol (MCP). | ||
|
||
> [!NOTE] | ||
> The `Microsoft.Extensions.AI.Templates` experience is currently in preview. The template uses the [ModelContextProtocol](https://www.nuget.org/packages/ModelContextProtocol/) library and the [MCP registry `server.json` schema](https://github.com/modelcontextprotocol/registry/blob/main/docs/server-json/README.md), which are both in preview. | ||
|
||
## Prerequisites | ||
|
||
- [.NET 8.0 SDK or higher](https://dotnet.microsoft.com/download) | ||
- [.NET 10.0 SDK](https://dotnet.microsoft.com/download/dotnet) (preview 6 or higher) | ||
- [Visual Studio Code](https://code.visualstudio.com/) | ||
- [GitHub Copilot extension](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) for Visual Studio Code | ||
- [NuGet.org account](https://www.nuget.org/users/account/LogOn) | ||
|
||
## Create the project | ||
|
||
1. In a terminal window, navigate to the directory where you want to create your app, and create a new console app with the `dotnet new` command: | ||
1. In a terminal window, install the MCP Server template: | ||
|
||
```bash | ||
dotnet new console -n MinimalMcpServer | ||
dotnet new install Microsoft.Extensions.AI.Templates::9.7.0-preview.2.25356.2 | ||
``` | ||
|
||
1. Navigate to the `MinimalMcpServer` directory: | ||
1. Create a new MCP server app with the `dotnet new mcpserver` command: | ||
|
||
```bash | ||
cd MinimalMcpServer | ||
dotnet new mcpserver -n SampleMcpServer | ||
``` | ||
|
||
1. Add the following NuGet packages to your app: | ||
1. Navigate to the `SampleMcpServer` directory: | ||
|
||
```bash | ||
dotnet add package ModelContextProtocol --prerelease | ||
dotnet add package Microsoft.Extensions.Hosting | ||
cd SampleMcpServer | ||
``` | ||
|
||
- The [ModelContextProtocol](https://www.nuget.org/packages/ModelContextProtocol) package is the official C# SDK for working with the Model Context Protocol. | ||
- The [Microsoft.Extensions.Hosting](https://www.nuget.org/packages/Microsoft.Extensions.Hosting) package provides the generic .NET `HostBuilder` and services for logging and dependency injection. | ||
|
||
## Add the app code | ||
|
||
Replace the contents of `Program.cs` with the following code to implement a minimal MCP server that exposes simple echo tools. The AI model invokes these tools as necessary to generate responses to user prompts. | ||
1. Build the project: | ||
|
||
:::code language="csharp" source="snippets/mcp-server/program.cs" ::: | ||
|
||
The preceding code: | ||
```bash | ||
dotnet build | ||
``` | ||
|
||
- Creates a generic host builder for dependency injection, logging, and configuration. | ||
- Configures logging for better integration with MCP clients. | ||
- Registers the MCP server, configures it to use stdio transport, and scans the assembly for tool definitions. | ||
- Builds and runs the host, which starts the MCP server. | ||
- Defines a static class to hold two MCP tools that echo values back to the client. | ||
1. Update the `<PackageId>` in the `.csproj` file to be unique on NuGet.org, for example `<NuGet.org username>.SampleMcpServer`. | ||
|
||
## Configure the MCP server in Visual Studio Code | ||
|
||
|
@@ -66,15 +60,14 @@ Configure GitHub Copilot for Visual Studio Code to use your custom MCP server: | |
|
||
```json | ||
{ | ||
"inputs": [], | ||
"servers": { | ||
"MinimalMcpServer": { | ||
"SampleMcpServer": { | ||
"type": "stdio", | ||
"command": "dotnet", | ||
"args": [ | ||
"run", | ||
"--project", | ||
"${workspaceFolder}/MinimalMcpServer.csproj" | ||
"<RELATIVE PATH TO PROJECT DIRECTORY>" | ||
] | ||
} | ||
} | ||
|
@@ -85,30 +78,185 @@ Configure GitHub Copilot for Visual Studio Code to use your custom MCP server: | |
|
||
## Test the MCP server | ||
|
||
1. Open GitHub Copilot in Visual Studio Code and switch to agent mode. | ||
1. Select the **Select tools** icon to verify your **MinimalMcpServer** is available with both tools listed. | ||
The MCP server template includes a tool called `get_random_number` you can use for testing and as a starting point for development. | ||
|
||
1. Open GitHub Copilot in Visual Studio Code and switch to chat mode. | ||
joelverhagen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
1. Select the **Select tools** icon to verify your **SampleMcpServer** is available with the sample tool listed. | ||
joelverhagen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
:::image type="content" source="../media/mcp/available-tools.png" alt-text="A screenshot showing the available MCP tools."::: | ||
:::image type="content" source="../media/mcp/available-tools-nuget.png" alt-text="A screenshot showing the available MCP tools."::: | ||
|
||
1. Enter a prompt to run the **ReverseEcho** tool: | ||
1. Enter a prompt to run the **get_random_number** tool: | ||
|
||
```console | ||
Reverse the following: "Hello, minimal MCP server!" | ||
Give me a random number between 1 and 100. | ||
``` | ||
|
||
1. GitHub Copilot requests permission to run the **ReverseEcho** tool for your prompt. Select **Continue** or use the arrow to select a more specific behavior: | ||
1. GitHub Copilot requests permission to run the **get_random_number** tool for your prompt. Select **Continue** or use the arrow to select a more specific behavior: | ||
|
||
- **Current session** always runs the operation in the current GitHub Copilot Agent Mode session. | ||
- **Current workspace** always runs the command for the current Visual Studio Code workspace. | ||
- **Always allow** sets the operation to always run for any GitHub Copilot Agent Mode session or any Visual Studio Code workspace. | ||
|
||
1. Verify that the server responds with the echoed message: | ||
1. Verify that the server responds with a random number: | ||
|
||
```output | ||
Your random number is 42. | ||
``` | ||
|
||
## Add inputs and configuration options | ||
|
||
In this example, you enhance the MCP server to use a configuration value set in an environment variable. This could be configuration needed for the functioning of your MCP server, such as an API key, an endpoint to connect to, or a local directory path. | ||
|
||
1. Add another tool method after the `GetRandomNumber` method in `Tools/RandomNumberTools.cs`. Update the tool code to use an environment variable. | ||
|
||
:::code language="csharp" source="snippets/mcp-server/Tools/RandomNumberTools.cs" range="19-36"::: | ||
|
||
1. Update the `.vscode/mcp.json` to set the `WEATHER_CHOICES` environment variable for testing. | ||
|
||
```json | ||
{ | ||
"servers": { | ||
"SampleMcpServer": { | ||
"type": "stdio", | ||
"command": "dotnet", | ||
"args": [ | ||
"run", | ||
"--project", | ||
"<RELATIVE PATH TO PROJECT DIRECTORY>" | ||
], | ||
"env": { | ||
"WEATHER_CHOICES": "sunny,humid,freezing" | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
1. Try another prompt with Copilot in VS Code, such as: | ||
|
||
```console | ||
What is the weather in Redmond, Washington? | ||
``` | ||
|
||
VS Code should return a random weather description. | ||
|
||
1. Update the `.mcp/server.json` to declare your environment variable input. The `server.json` file schema is defined by the [MCP Registry project](https://github.com/modelcontextprotocol/registry/blob/main/docs/server-json/README.md) and is used by NuGet.org to generate VS Code MCP configuration. | ||
|
||
* Use the `environment_variables` property to declare environment variables used by your app that will be set by the client using the MCP server (for example, VS Code). | ||
|
||
* Use the `package_arguments` property to define CLI arguments that will be passed to your app. For more examples, see the [MCP Registry project](https://github.com/modelcontextprotocol/registry/blob/main/docs/server-json/examples.md). | ||
|
||
:::code language="json" source="snippets/mcp-server/.mcp/server.json"::: | ||
|
||
The only information used by NuGet.org in the `server.json` is the first `packages` array item with the `registry_name` value matching `nuget`. The other top-level properties aside from the `packages` property are currently unused and are intended for the upcoming central MCP Registry. You can leave the placeholder values until the MCP Registry is live and ready to accept MCP server entries. | ||
|
||
You can [test your MCP server again](#test-the-mcp-server) before moving forward. | ||
|
||
## Pack and publish to NuGet | ||
|
||
1. Pack the project: | ||
|
||
```bash | ||
dotnet pack -c Release | ||
``` | ||
|
||
1. Publish the package to NuGet: | ||
|
||
```bash | ||
dotnet nuget push bin/Release/*.nupkg --api-key <your-api-key> --source https://api.nuget.org/v3/index.json | ||
``` | ||
|
||
If you want to test the publishing flow before publishing to NuGet.org, you can register an account on the NuGet Gallery integration environment: [https://int.nugettest.org](https://int.nugettest.org). The `push` command would be modified to: | ||
|
||
```bash | ||
dotnet nuget push bin/Release/*.nupkg --api-key <your-api-key> --source https://apiint.nugettest.org/v3/index.json | ||
``` | ||
|
||
For more information, see [Publish a package](/nuget/nuget-org/publish-a-package). | ||
|
||
## Discover MCP servers on NuGet.org | ||
|
||
1. Search for your MCP server package on [NuGet.org](https://www.nuget.org/packages?packagetype=mcpserver) (or [int.nugettest.org](https://int.nugettest.org/packages?packagetype=mcpserver) if you published to the integration environment) and select it from the list. | ||
|
||
:::image type="content" source="../media/mcp/nuget-mcp-search.png" alt-text="A screenshot showing a search for MCP servers on NuGet.org."::: | ||
|
||
1. View the package details and copy the JSON from the "MCP Server" tab. | ||
|
||
:::image type="content" source="../media/mcp/nuget-mcp-display.png" alt-text="A screenshot showing a specific MCP server displayed on NuGet.org."::: | ||
|
||
1. In your `mcp.json` file in the `.vscode` folder, add the copied JSON, which looks like this: | ||
|
||
```json | ||
{ | ||
"inputs": [ | ||
{ | ||
"type": "promptString", | ||
"id": "weather_choices", | ||
"description": "Comma separated list of weather descriptions to randomly select.", | ||
"password": false | ||
} | ||
], | ||
"servers": { | ||
"Contoso.SampleMcpServer": { | ||
"type": "stdio", | ||
"command": "dnx", | ||
"args": ["[email protected]", "--yes"], | ||
"env": { | ||
"WEATHER_CHOICES": "${input:weather_choices}" | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
If you published to the NuGet Gallery integration environment, you need to add `"--add-source", "https://apiint.nugettest.org/v3/index.json"` at the end of the `"args"` array. | ||
|
||
1. Save the file. | ||
|
||
1. In GitHub Copilot, select the **Select tools** icon to verify your **SampleMcpServer** is available with the tools listed. | ||
|
||
1. Enter a prompt to run the new **get_city_weather** tool: | ||
|
||
```console | ||
What is the weather in Redmond? | ||
``` | ||
|
||
1. If you added inputs to your MCP server (for example, `WEATHER_CHOICES`), you will be prompted to provide values. | ||
|
||
1. Verify that the server responds with the random weather: | ||
|
||
```output | ||
!revres PCM laminim ,olleH | ||
The weather in Redmond is balmy. | ||
``` | ||
|
||
## Common issues | ||
|
||
### The command "dnx" needed to run SampleMcpServer was not found. | ||
|
||
If VS Code shows this error when starting the MCP server, you need to install a compatible version of the .NET SDK. | ||
|
||
:::image type="content" source="../media/mcp/missing-dnx.png" alt-text="A screenshot showing the missing dnx command in VS Code."::: | ||
|
||
The `dnx` command is shipped as part of the .NET SDK, starting with version 10 preview 6. [Install the .NET 10 SDK](https://dotnet.microsoft.com/download/dotnet) to resolve this issue. | ||
|
||
### GitHub Copilot does not use your tool (an answer is provided without invoking your tool). | ||
|
||
Generally speaking, an AI agent like GitHub Copilot is informed that it has some tools available by the client application, such as VS Code. Some tools, such as the sample random number tool, might not be leveraged by the AI agent because it has similar functionality built in. | ||
|
||
If your tool is not being used, check the following: | ||
|
||
1. Verify that your tool appears in the list of tools that VS Code has enabled. See the screenshot in [Test the MCP server](#test-the-mcp-server) for how to check this. | ||
1. Explicitly reference the name of the tool in your prompt. In VS Code, you can reference your tool by name. For example, `Using #get_random_weather, what is the weather in Redmond?`. | ||
1. Verify your MCP server is able to start. You can check this by clicking the "Start" button visible above your MCP server configuration in the VS Code user or workspace settings. | ||
|
||
:::image type="content" source="../media/mcp/started-mcp-server.png" alt-text="A screenshot showing an MCP server in VS Code configuration that is started."::: | ||
|
||
## Related content | ||
|
||
[Build a minimal MCP client](build-mcp-client.md) | ||
[Get started with .NET AI and the Model Context Protocol](../get-started-mcp.md) | ||
- [Get started with .NET AI and the Model Context Protocol](../get-started-mcp.md) | ||
- [Model Context Protocol .NET samples](https://github.com/microsoft/mcp-dotnet-samples) | ||
- [Build a minimal MCP client](build-mcp-client.md) | ||
- [Publish a package](/nuget/nuget-org/publish-a-package) | ||
- [Find and evaluate NuGet packages for your project](/nuget/consume-packages/finding-and-choosing-packages) | ||
- [What's new in .NET 10](../../core/whats-new/dotnet-10/overview.md) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"description": "<your description here>", | ||
"name": "io.github.<your GitHub username here>/<your repo name>", | ||
"packages": [ | ||
{ | ||
"registry_name": "nuget", | ||
"name": "<your package ID here>", | ||
"version": "<your package version here>", | ||
"package_arguments": [], | ||
"environment_variables": [ | ||
{ | ||
"name": "WEATHER_CHOICES", | ||
"value": "{weather_choices}", | ||
"variables": { | ||
"weather_choices": { | ||
"description": "Comma separated list of weather descriptions to randomly select.", | ||
"is_required": true, | ||
"is_secret": false | ||
} | ||
} | ||
} | ||
] | ||
} | ||
], | ||
"repository": { | ||
"url": "https://github.com/<your GitHub username here>/<your repo name>", | ||
"source": "github" | ||
}, | ||
"version_detail": { | ||
"version": "<your package version here>" | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"servers": { | ||
"SampleMcpServer": { | ||
"type": "stdio", | ||
"command": "dotnet", | ||
"args": [ | ||
"run", | ||
"--project", | ||
"." | ||
], | ||
"env": { | ||
"WEATHER_CHOICES": "sunny,humid,freezing" | ||
} | ||
} | ||
} | ||
} |
15 changes: 0 additions & 15 deletions
15
docs/ai/quickstarts/snippets/mcp-server/MinimalMcpServer.csproj
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.