Skip to content

Commit 919dd6a

Browse files
authored
Merge pull request #3454 from NuGet/main
Merge to Live, July 2025
2 parents e38a62b + 5d42542 commit 919dd6a

File tree

18 files changed

+264
-26
lines changed

18 files changed

+264
-26
lines changed

docs/TOC.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
## [Best practices for a secure software supply chain](concepts/Security-Best-Practices.md)
7878
## [MSBuild .props and .targets](concepts/MSBuild-props-and-targets.md)
7979
## [Troubleshooting Installed Packages](concepts/troubleshooting-installed-packages.md)
80+
## [MCP servers in NuGet packages](concepts/nuget-mcp.md)
8081
# Reference
8182
## [.nuspec](reference/nuspec.md)
8283
## [nuget.config file](reference/nuget-config-file.md)
@@ -153,6 +154,7 @@
153154
### [NU1011](reference/errors-and-warnings/NU1011.md)
154155
### [NU1012](reference/errors-and-warnings/NU1012.md)
155156
### [NU1014](reference/errors-and-warnings/NU1014.md)
157+
### [NU1015](reference/errors-and-warnings/NU1015.md)
156158
### [NU1100](reference/errors-and-warnings/NU1100.md)
157159
### [NU1101](reference/errors-and-warnings/NU1101.md)
158160
### [NU1102](reference/errors-and-warnings/NU1102.md)

docs/api/implementation-guide.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,15 @@ Nuget.org provides vulnerability data for all GitHub reviewed advisories from th
106106

107107
If your package repository is hosting first-party packages, and you would like to provide vulnerability information to customers using your own feed, but don't yet have any disclosed package vulnerabilities, you should provide a [vulnerability index](./vulnerability-info.md#vulnerability-index) with one or more [vulnerability pages](./vulnerability-info.md#vulnerability-page) whose contents are an empty JSON array (`[]`).
108108

109-
If your package repository is intended to be used by apps as the default repository (instead of nuget.org), you can use nuget.org's vulnerability data.
110-
One option is to use nuget.org's vulnerability index URL in your service index.
111-
Another option is to periodically check nuget.org's `VulnerabilityInfo` index, and download any changed pages to mirror locally.
109+
#### Reusing nuget.org's vulnerability data
110+
111+
NuGet does not require that resources in the [service index](./service-index.md), or [the vulnerability index](./vulnerability-info.md#vulnerability-index), must be on the same server as the service index itself.
112+
However, there are several reasons why some companies choose to block nuget.org at the firewall, or have on-prem feeds on a disconnected network.
113+
To avoid connectivity issues, we recommend serving vulnerability data from your own web app, so that NuGet clients only make HTTP connections to the host the feed is installed on.
114+
115+
✔️ DO cache or proxy the vulnerability pages in your own web app
116+
117+
❌ DO NOT advertise api.nuget.org in your service index or vulnerability index without a configuration to turn this off.
112118

113119
## `packageTypes` search query
114120

docs/api/service-index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ There is no requirement that each resource has a unique `@id` or `@type`. It is
6565
determine which resource to prefer over another. One possible implementation is that resources of the same or
6666
compatible `@type` can be used in a round-robin fashion in case of connection failure or server error.
6767

68+
A resource can use a different host or domain than the service index, but this may cause issues in environments with strict network rules.
69+
In particular, if your service index adds resources that point directly to nuget.org (rather than proxying or caching through your own feed), your feed will not work where access to nuget.org is blocked.
70+
If your feed is going to delegate particular resources to nuget.org, we recommend adding a configuration so that when your feed is deployed, the direct nuget.org reference can be removed from the service index.
71+
6872
### Sample request
6973

7074
```

docs/api/vulnerability-info.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ The data file schema does not allow for modification or redaction of known vulne
3333
Therefore if a server's vulnerability data source (for example the [GitHub Advisories Database](https://github.com/advisories)) modifies an existing advisory, the NuGet server must modify the page that the vulnerability information was previously reported.
3434
One way to achieve this with the suggested partition scheme is to treat all vulnerability modifications and deletions as a trigger to regenerate the complete `base.json` file, and empty `updates.json`.
3535

36+
If you intend to use nuget.org's vulnerability data in your own NuGet server implementation, you should take into consideration developers who do not have direct access to nuget.org.
37+
[See our implementation guide for more details](./implementation-guide.md#reusing-nugetorgs-vulnerability-data).
38+
3639
## Versioning
3740

3841
The following `@type` values are used:

docs/concepts/Package-Versioning.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ That said, package developers generally follow recognized naming conventions:
5858
When ordering versions by precedence, NuGet follows the SemVer standard and chooses a version without a suffix first, then applies precedence to pre-release versions in reverse alphabetical order and treats dot notation numbers with numerical order.
5959

6060
> [!Note]
61-
> Prerelease numbers with dot notation, as in *1.0.1-build.23*, are considered are part of the [SemVer 2.0.0](https://semver.org/spec/v2.0.0.html) standard, and as such are [only supported with NuGet 4.3.0+](#semantic-versioning-200).
61+
> Prerelease numbers with dot notation, as in *1.0.1-build.23*, are considered part of the [SemVer 2.0.0](https://semver.org/spec/v2.0.0.html) standard, and as such are [only supported with NuGet 4.3.0+](#semantic-versioning-200).
6262
6363
### [SemVer 2.0 sorting](#tab/semver20sort)
6464

docs/concepts/nuget-mcp.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
---
2+
title: MCP servers in NuGet packages
3+
description: How can MCP servers be distributed using NuGet?
4+
author: joelverhagen
5+
ms.author: jver
6+
ms.topic: conceptual
7+
ms.date: 07/23/2025
8+
---
9+
10+
# MCP servers in NuGet packages
11+
12+
NuGet provides a convenient way to package and distribute MCP servers written in .NET. The C# MCP SDK and .NET offer a robust platform for building MCP servers, and NuGet is ideal for delivering your MCP server to end users as a local tool. Self-contained, platform-specific packages reduce runtime compatibility issues, and AOT compilation can further improve the end-user experience.
13+
14+
For more information about the Model Context Protocol (MCP) in general, see the [introduction on the MCP website](https://modelcontextprotocol.io/introduction). To create your own MCP server and package it using NuGet, see the [quickstart guide](/dotnet/ai/quickstarts/build-mcp-server).
15+
16+
## Applicable scenarios
17+
18+
Shipping your MCP server via NuGet does not apply to all situations. The term "MCP client" is used in this document and refers to an application that orchestrates the interaction between an AI agent or LLM and calls made to an MCP server. Some example MCP clients are [Visual Studio Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers), [Visual Studio](/visualstudio/ide/mcp-servers), [GitHub Copilot coding agent](https://docs.github.com/copilot/concepts/coding-agent/about-copilot-coding-agent), Claude Code, or Cursor.
19+
20+
Consider the following criteria to determine whether shipping your MCP server as a NuGet package makes sense:
21+
22+
- ✅ You want your MCP server to run **locally** on the user's system (i.e., in the same context as the MCP client).
23+
- Local MCP servers, such as those shipped in NuGet packages, run in the same context as the MCP client and communicate with the MCP client via standard IO (stdio) transport. The MCP client is responsible for launching the local MCP server process.
24+
- ✅ The .NET SDK is available to the MCP client.
25+
- NuGet MCP servers are [.NET tool packages](/dotnet/core/tools/global-tools), which are installed and executed using `dnx` from the .NET SDK.
26+
- ✅ You have a NuGet package feed to host your MCP server package.
27+
- NuGet.org can be used to publish MCP server packages and provides a tailored MCP browsing and consumption experience. However, any NuGet package feed, such as Azure Artifacts, can be used for hosting MCP servers if you wish to keep your MCP server package private.
28+
29+
## Benefits of using .NET and NuGet for MCP servers
30+
31+
There are several benefits to using NuGet for hosting your MCP server:
32+
33+
- **Official SDK** - the [MCP C# SDK](https://github.com/modelcontextprotocol/csharp-sdk) provides a familiar interface for implementing your MCP server in C# and makes it easy to expose tools to MCP clients.
34+
- **Flexible runtime options** - the .NET SDK provides several options for how your MCP server is compiled and packaged. See the [runtime requirements](#runtime-requirements) section for details.
35+
- **Discoverability and distribution** - NuGet.org provides a way to showcase your MCP server, allowing potential users to find your MCP server and easily use it from inside VS Code or Visual Studio. NuGet.org encourages the use of an embedded [`.mcp/server.json`](https://github.com/modelcontextprotocol/registry/blob/main/docs/server-json/README.md) to declare inputs and an `McpServer` package type to allow MCP servers to be differentiated from other tool or dependency packages.
36+
- **Familiar authoring workflows** - if you already use NuGet for creating dependency packages, creating and publishing an MCP server will be a very similar experience.
37+
38+
## Package download and execution
39+
40+
To fetch a local MCP server, the code for the server must be located and downloaded using a mechanism (protocol) specific to the package ecosystem. This is generally done with a "single-shot" command which takes the package name and arguments to download and then execute the package as a command-line application.
41+
42+
For NuGet-based MCP servers, we recommend using `dnx` (a new command shipped in .NET 10 Preview 6) to acquire and execute the package. `dnx` currently ships with the .NET SDK, but there [is discussion to include `dnx` in the .NET runtime](https://github.com/dotnet/sdk/issues/49796).
43+
44+
A command to start an MCP server would look something like this:
45+
46+
```bash
47+
dnx NuGet.Mcp.Server@0.1.2-preview --yes
48+
```
49+
50+
Environment variables and command-line arguments can both be used to configure your MCP server in custom ways. These inputs allow you to customize the behavior of your MCP server to suit specific needs.
51+
52+
This will download the `NuGet.Mcp.Server` package of version `0.1.2-preview` from your configured package sources (NuGet.org by default), and launch the contained CLI tool. For an MCP server, you may see log messages appear in stderr, but the process will appear to hang. This is expected, since the process is waiting for MCP protocol messages over stdin from your MCP client.
53+
54+
Typically, your MCP client will invoke this command via tool-specific MCP configuration, such as the `mcp.json` file used by Visual Studio Code and Visual Studio.
55+
56+
All of these tools support installing alternate sources. For example, `dnx` supports installing from Azure DevOps using the `--source` parameter, allowing consumption of private MCP servers, as long as the needed credential or credential providers are configured.
57+
58+
## Runtime requirements
59+
60+
Once the package is downloaded, a runtime is needed to execute the code inside the package. .NET tool packages (and by extension NuGet-based MCP servers) support a variety of options for how the tool is compiled and packaged. These options allow you, the MCP server author, to decide which runtime requirements should be placed on the users of your MCP server.
61+
62+
There are three main options for how to package your MCP server:
63+
64+
1. **Framework-dependent**: Requires that the MCP client has access to a compatible .NET runtime. If `dnx` is being used to download and execute the package, a runtime will be available.
65+
2. **Self-contained**: Bundles the runtime with the package. [Using trimming](/dotnet/core/deploying/trimming/trimming-options) can reduce the size of the package. Self-contained .NET tools use `<PublishSelfContained>true</PublishSelfContained>`.
66+
3. **Ahead-of-time (AOT) compiled**: A self-contained package with AOT compilation enabled. See [native AOT deployment](/dotnet/core/deploying/native-aot/) for more information. AOT .NET tools use `<PublishAot>true</PublishAot>`.
67+
68+
For MCP servers, we recommend using option #2 (self-contained package without AOT) because it eliminates the need for any specific .NET runtime version present in the user's environment. If you can guarantee a compatible runtime version on the intended execution environment, option #1 is reasonable. Option #3 (using AOT) is also a good option, but it forces you or your dependencies to make your code compatible with AOT compilation. The C# MCP SDK is AOT compatible, but other dependencies you intend to use may not yet be AOT compatible.
69+
70+
Consider using the `mcpserver` template in the [Microsoft.Extensions.AI.Templates](https://www.nuget.org/packages/Microsoft.Extensions.AI.Templates) template package to use the latest recommended defaults.
71+
72+
## Comparison to other ecosystems
73+
74+
Other ecosystems have similar requirements and workflows for packaging and running MCP servers:
75+
76+
- **NuGet/.NET**: Uses the `dnx` command to download and execute .NET tool packages. Requires the .NET SDK for `dnx`. Additional runtime dependencies can be bundled into the package.
77+
- **npm**: Uses the `npx` command to download and execute npm packages, and install dependencies transitively. Requires Node.js.
78+
- **Python**: Uses the `uvx` command to download and execute Python packages, and install dependencies transitively. Requires a Python runtime.
79+
- **Docker**: Uses the `docker run` command to download and execute images. Requires the Docker Engine. Docker images can target multiple CPU architectures and provide excellent isolation, but are generally larger than NuGet, npm, or Python packages.
80+
81+
In all of these cases, the MCP client needs to have the necessary ecosystem-specific tool (e.g., `dnx`, `npx`) to download and execute the package-based MCP server.

docs/consume-packages/Package-References-in-Project-Files.md

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Note that because `build` is not included with `PrivateAssets`, targets and prop
143143
144144
## Adding a PackageReference condition
145145

146-
You can use a condition to control whether a package is included, where conditions can use any MSBuild variable or a variable defined in the targets or props file. However, at presently, only the `TargetFramework` variable is supported.
146+
You can use a condition to control whether a package is included, where conditions can use any MSBuild variable or a variable defined in the targets or props file. However, at present, only the `TargetFramework` variable is supported.
147147

148148
For example, say you're targeting `netstandard1.4` as well as `net452` but have a dependency that is applicable only for `net452`. In this case you don't want a `netstandard1.4` project that's consuming your package to add that unnecessary dependency. To prevent this, you specify a condition on the `PackageReference` as follows:
149149

@@ -464,10 +464,12 @@ You can leave off `$(AssetTargetFallback)` if you wish to overwrite, instead of
464464
## PrunePackageReference
465465

466466
The .NET Runtime is constantly evolving, with performance improvements and new APIs each release.
467-
There is a lot of functionality that's available within the runtime, but also as packages, such as [System.Text.Json](https://www.nuget.org/packages/System.Text.Json). This can often lead to a `System.Text.Json 8.0.0` in a project targeting `.NET 9` or `.NET 8`. This dependency is unnecessary and the build conflict resolution would not use the assembly coming from the package since it's already available in the .NET Runtime.
468-
Starting in in [NuGet version 6.13](..\release-notes\NuGet-6.13.md) and .NET SDK 9.0.200, `PrunePackageReference` enables the pruning of these packages at restore time for .NET SDK based projects.
467+
New features added to .NET sometimes are also provided as packages, so that developers using older target frameworks can use the library, such as [System.Text.Json](https://www.nuget.org/packages/System.Text.Json).
468+
This can often lead to a `System.Text.Json 8.0.0` in a project targeting `.NET 9` or `.NET 8`. This dependency is unnecessary and the build conflict resolution would not use the assembly coming from the package since it's already available in the .NET Runtime.
469+
Starting in [NuGet version 6.13](..\release-notes\NuGet-6.13.md) and .NET SDK 9.0.200, `PrunePackageReference` enables the pruning of these packages at restore time for .NET SDK based projects.
470+
The first iteration of pruning affected transitive packages only, but starting with .NET SDK 10, package pruning affects direct packages as well.
469471

470-
Package pruning is available as an opt-in feature with the .NET 9 SDK, and will be enabled by default for all `.NET` frameworks and `>= .NET Standard 2.0` starting with .NET 10 SDK.
472+
Package pruning is available as an opt-in feature with the .NET 9 SDK, and is enabled by default for `>= .NET 8.0` frameworks and `>= .NET Standard 2.0` starting with .NET 10 SDK.
471473

472474
Package pruning is only available with the default dependency resolver as [released in 6.12](#nuget-dependency-resolver).
473475

@@ -489,16 +491,53 @@ The .NET SDK predefines the list of packages to be pruned for you.
489491

490492
### How PrunePackageReference works
491493

492-
When a package is specified to be pruned during restore, it is removed from the dependency graph. This package is not downloaded and does not appear in any of the outputs of NuGet. When a package is pruned, there is a detailed verbosity message indicating that the package has been removed for the given target framework.
494+
When a package is specified to be pruned during restore, it is removed from the dependency graph.
495+
When a package is pruned, there is a message, visible at detailed verbosity, indicating that the package has been removed for the given target framework.
493496

494-
Pruning is only supported for transitive packages, meaning packages that are referenced by other packages or projects. The following table illustrates various package pruning behaviors.
497+
For transitive packages, meaning dependencies of other packages or projects, the packages are not downloaded and do not appear in any of the outputs of NuGet.
498+
499+
For direct packages, `PrivateAssets='all'` and `IncludeAssets='none'` are implicitly applied.
500+
501+
- `IncludeAssets='none'` ensures that the assemblies from this package are not used during the build. Before pruning existed, the conflict resolution during the build ensured that platform assemblies were preferred over those coming from the packages.
502+
- `PrivateAssets='all'` ensures that the packages aren't included in packages or through project references.
503+
504+
Example:
505+
506+
A project like below:
507+
508+
```xml
509+
<PropertyGroup>
510+
<TargetFrameworks>net9.0;netstandard2.0</TargetFrameworks>
511+
</PropertyGroup>
512+
513+
<ItemGroup>
514+
<PackageReference Include="System.Text.Json" Version="9.0.4" />
515+
</ItemGroup>
516+
```
517+
518+
will have a nuspec with the following dependencies:
519+
520+
```xml
521+
<dependencies>
522+
<group targetFramework=".NETFramework4.7.2">
523+
<dependency id="System.Text.Json" version="9.0.4" />
524+
</group>
525+
<group targetFramework="net9.0">
526+
</group>
527+
</dependencies>
528+
```
529+
530+
When a direct PackageReference can be completely removed from your project, and one of the project frameworks are .NET 10 or newer, [NU1510](../reference/errors-and-warnings/NU1510.md) will be raised asking you to remove the package.
531+
Following this suggestion will reduce the complexity of your project graph.
532+
533+
The following table summarizes all the package pruning behaviors.
495534

496535
| Dependency disposition | Behavior |
497536
|-----------------|----------|
498537
| Matches the ID of a transitive package coming through another package | Prune |
499538
| Matches the ID of a transitive package coming through another project | Prune |
500-
| Matches the ID of a direct `PackageReference` | Raise the [NU1510](../reference/errors-and-warnings/NU1510.md) warning and do not prune |
501-
| Matches the ID of a `ProjectReference` | Raise the [NU1511](../reference/errors-and-warnings/NU1511.md) warning and do not prune |
539+
| Matches the ID of a direct `PackageReference` | Apply `PrivateAssets='all'` and `IncludeAssets='none'` and raise the [NU1510](../reference/errors-and-warnings/NU1510.md) warning when the package can be removed from all frameworks and the project targets .NET 10. |
540+
| Matches the ID of a `ProjectReference` | Do not prune and raise the [NU1511](../reference/errors-and-warnings/NU1511.md) warning when the project targets .NET 10 |
502541

503542
### PrunePackageReference applications
504543

0 commit comments

Comments
 (0)