Skip to content

Conversation

@joelverhagen
Copy link
Member

@joelverhagen joelverhagen commented Jun 2, 2025

Describes NuGet/NuGetGallery#10461.

A lot of the work here is already done (MCP SDK for C#/.NET, designs for single-shot execution, existing MCP registry efforts). This document is meant to describe the big picture of what's missing and zoom in on the NuGet-specific pieces.

Rendered Proposal

@JonDouglas JonDouglas self-requested a review June 2, 2025 16:53
- What schema should be used for a machine-readable MCP server startup instruction?
- The package author could include an `mcp.json` in the NuGet package [matching the MCP registry OpenAPI spec](https://github.com/modelcontextprotocol/registry/blob/a4cefcf05f81466ad65e7c3971e76d0f6d60783e/docs/openapi.yaml#L183-L201).
- How are client runtime requirements expressed?
- For example, if an MCP server needs a certain .NET version, how is this communicated to the end user before failure occurs?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super interesting question here. Didn't think about this as I was under the impression this would be a .NET SDK version/compatibility issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll need to keep an eye on how this is solved in other ecosystems. I will do some research and ask in the MCP/registry repo if needed.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this problem with tools already - there are a few ways to tackle it

Available today:

  • the tool author can set rollforward on their tool at build time to allow it to run on newer runtimes than the one being targeted
  • tool consumers can use the roll forward flags on the tool run commands to explicitly tell a tool to run on a runtime that it doesn't explicitly support

Available soon:

  • when we support RID-specific tools, the RID-specific tools can compile self-contained. This means they won't need a separate runtime. This solves the problem at the cost of needing to bump package versions to get runtime updates (which one-shot execution also solves).
  • when a RID-agnostic MCP server is used, the runtime already has knobs for controlling 'roll forward'. We expose those knobs on tool run and related commands (see above), and would likely need to do the same on one-shot invocation as well. This would allow tool consumers to opt into running a tool on newer runtimes than the one it was built for

Available later:

  • the CLI bootstrapper could auto- or interactively-acquire runtimes that tools require to run

JonDouglas
JonDouglas previously approved these changes Jun 2, 2025
Copy link
Contributor

@JonDouglas JonDouglas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't have wrote this better to be honest. This is gold!

@joelverhagen joelverhagen marked this pull request as ready for review June 2, 2025 18:24
@joelverhagen joelverhagen requested a review from a team as a code owner June 2, 2025 18:24
jeffkl
jeffkl previously approved these changes Jun 3, 2025
Copy link
Contributor

@jeffkl jeffkl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, looking forward to seeing these changes implemented!

baronfel
baronfel previously approved these changes Jun 3, 2025
Copy link

@baronfel baronfel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few notes to consider, but overall I love the end to end.


We have two options to encode the startup instructions into the package:

1. Instruct the package author to include the desired consumer MCP JSON in the readme, and allow NuGet.org to scrape the JSON from the README markdown (a code block matching a certain pattern).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would nuget.org need to scrape the JSON? It already presents the README, which the user sees and can copy from and paste into their mcp.json file.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would make the command palette on nuget.org MCP aware so it's clearer how to "install" the thing. The command palette for .NET tools would just be to install it, not run it (let alone run it with needed args/env).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We spoke offline a bit about this but wanted to share in this forum:
Long term VS Code/VS IDE/other MCP clients may have UI to connect to the MCP registry, making the MCP consumption JSON less useful. But I think the consumption JSON is still a good thing to have on NuGet.org because there is a ecosystem of tools that can use MCP and maybe advanced scenarios where people still edit their client mcp.json manually. I think the copy-paste of MCP consumption JSON is useful long term, just decreasing over time is MCP registry adoption goes up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine we won't make the consumption json mandatory for the publishers, so we won't be able to show it for all MCP packages. In cases where it's missing, is there a default we can show? If so, what would it look like?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a default would cause more confusion than do good if it wasn't guaranteed to work. To guarantee it would work, you'd need to know the basic command line to use as well as any switches and env vars that are required (which will be common for MCP servers that access network resources).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cases where it's missing, is there a default we can show?

I think we would show a user message not a JSON configuration or something meant to be machine readable. Something like "This NuGet package contains an MCP server. Check the package README and project documentation for details on how to launch the MCP server."

The command palette tabs for global and local tools would still be there.

We could show a warning to the author (not the end user) indicating that the server.json is missing, much like we do when a package is missing a README.md.


The resulting MCP server .nupkg will have two package types: `DotnetTool` and `McpServer`. For more information on package types, [see NuGet documentation](https://learn.microsoft.com/en-us/nuget/reference/nuspec#packagetypes).

#### Startup instructions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also consider metadata in the .nuspec like how you specify git repository information. We could then render an mcp.json on nuget.org based on that metadata on behalf of the user. We'd technically already know the package ID and if its an MCP server. Maybe that would be best since perhaps the "one shot" command could change over time and we could update how its rendered on nuget.org rather than it being statically declared in each individually pushed package?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can compute a basic consumption JSON just from the ID and version. And then single-shot command changing over time can be done there. But this misses the args/env settings you can set in the mcp.json (now server.json as mentioned above). For example, @ErikEJ's MCP .NET tool needs an -mcp argument to launch:
https://github.com/ErikEJ/SqlServer.Rules/blob/95f72d575734f59ce2be3e1117e8a8d970d472fd/tools/SqlAnalyzerCli/readme.md#github-copilot-mcp-server-preview

Something in the package needs to define that.

I prefer the mcp/server.json approach over .nuspec because it doesn't invent a new scheme and instead inherits the schema used by the MCP registry project which the author will likely need to construct anyway to publish to the MCP registry.

Publish tool for MCP registry: https://github.com/modelcontextprotocol/registry/tree/main/tools/publisher (seems prototypical, the space is evolving)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While in a way it feels helpful to try and construct the JSON on behalf of the package author, I think as you've illustrated there's just too many things to consider accounting for. The format is really not that hard, and if the (eventual) template contains the mcp.json wired into the package build properly, it's pretty simple to change as opposed to having to create from scratch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right. We should be skeptical of generating JSON for the consumer, on behalf of the package author unless there is a clear hint inside the package. It's hard to say if most MCP servers will be at all useful with the default JSON we generate.

2. Work with Microsoft and community MCP server implementers to package as NuGet.
- As of June 2025, there are 28 .NET tools on NuGet.org that use the MCP SDK. Newer versions should have the `McpServer` package type.
- Some Microsoft MCP servers are implemented in .NET but distributed via npm, for example [@azure/mcp](https://www.npmjs.com/package/@azure/mcp).
3. Wait for `dnx` to land in a .NET 10 preview.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given dnx will be a critical part to this, VS Code and VS should consider what SDK is installed and what global.json may be present in the open folder/solution before using it. VS Code itself doesn't guarantee any SDK version is present. VS can guarantee a given .NET SDK is present if the .NET workload is installed, but that SDK may not be available within the open solution if global.json specifies a different (possibly older) SDK version.
If dnx isn't available, what should these clients do?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same problem exists for npm, Python, or docker based MCP servers. I think this is dependent on the client to check and smooth the experience (instead of just hard failing). Options are prompting to install a proper SDK version, or shipping one in box, I think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are more ways for dnx to fail than those others, because global.json can get in the way. But yes, let's certainly explore how we can predict failure and guide users to resolving it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I proposed modelcontextprotocol/registry#125. This would upfront some of the context needed to improve the acquisition experience. But your point about global.json is great. The MCP client will need to handle other error modes also. I don't think we need a perfect story here to say "this is how you should shape your MCP NuGet package" (the immediate goal of this proposal) and leave some of there error handled for client-specific implementations. Ideally VS Code and VS IDE can share error handling strategies.


## Future Possibilities

We will wait to publish an MCP server template until the .NET MCP SDK has announced a stable API surface area. It is currently in prerelease. In addition we will continue to keep eyes on the development of the [MCP specification](https://modelcontextprotocol.io/development/updates) and [MCP Registry](https://github.com/modelcontextprotocol/registry).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MCP may be turbulent for quite some time. But MCP support in clients isn't slowing down. Why wait to expose this for a stable .NET MCP SDK? Changes to the MCP SDK won't break servers, since they ship with all their dependencies of a version that they require.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would need agreement with the .NET MCP SDK team. If we fix to a specific version (not floating in the template) and release new template versions frequently, it seems like it could work.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do really think the template should ship either alongside the MCP SDK (ideally), or on its own, but not in the SDK. The more we can decouple templates from the SDK, the better and more agile we become in the future. While obviously out of scope here, and a bit of a tangent, this is a concept MAUI has struggled with too. Would be great if there was a more dynamic way to manage template versions and notify the user of available updates to them within tools like VS instead of having to insert them into VS updates.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I missed that this was just about the project template. Sure, you can wait on that. I thought the spec was saying that you were waiting on this whole document until that milestone was reached.


We will wait to publish an MCP server template until the .NET MCP SDK has announced a stable API surface area. It is currently in prerelease. In addition we will continue to keep eyes on the development of the [MCP specification](https://modelcontextprotocol.io/development/updates) and [MCP Registry](https://github.com/modelcontextprotocol/registry).

As MCP servers are run in more and more places, we can consider enhacing the MSBuild project file to enable an MCP server dependency. This could allow the MCP server to be available to the editor (instead of defined in client `mcp.json` configuration) or on a CI for build-time tasks. For example, an MCP server could be used inside an analyzer to produce or fix build warnings. This could work much like the existing [build integration that NuGet has to ship MSBuild props and targets](https://learn.microsoft.com/en-us/nuget/concepts/msbuild-props-and-targets). Thanks Jeff Kluge ([@jeffkl](https://github.com/jeffkl)) for the idea!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting idea, but I wonder ... what process would be the 'client' of such an MCP server? Would the compiler/analyzer act as the MCP client itself? Or would the editor/IDE be expected to read these project files and add the server to the user's list?
Perhaps the answer would depend on whether you expect user prompts to use this server or only the compiler/analyzer. But if only the compiler/analyzer, how would it use the MCP server without a connection to an LLM? And if we want to expose this through the IDE as another MCP server for the user to use in their chat window, ... that adds another dimension to how we discover MCP servers that is worth discussing with the impacted teams.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without considering the 'how'....

One scenario for this I can imagine is taking the MAUI MCP server I built with tools for querying developer environment state (installed xcode version, android sdk components, jdk, etc) and tools for installing/configuring those things, and shipping it as a dependency of the MAUI SDK itself (just the fact that the nuget package reference makes it into the project is the important part of course).

VS/VSWin could then advertise the MCP server availability for Copilot chat to consume, at which point the MCP Server's prompts, tools, resources, etc. would be available to a .NET MAUI developer to help diagnose and fix environment configuration issues within the chat (and any other agents).

This could help with having a sort of 'per project/solution' set of MCP servers referenced just as a happy side effect of which nuget packages you happen to reference.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the compiler/analyzer act as the MCP client itself?

In a CI context -- yes, it could work that way, or some other automated part of the build pipeline. This is a very forward looking "future opportunity".

Or would the editor/IDE be expected to read these project files and add the server to the user's list?

This is another option, but perhaps a bit more controversial. This would be an alternative to defining MCP servers in a client mcp.json and instead define them in an "MSBuild place". Maybe a .NET only shop could find use in this but given MCP servers can be non-.NET and a user might want to use a mix of MCP servers (not just .NET tools, they may not even know or care how they are implemented), shifting this into a project definition makes it so there are not two places to depend on an MCP server for editing, and makes the MCP dependency "project specific" whereas it's more of a user-wide setting, let alone solution-wide.

This idea is here more to help us think creatively about where an MCP server (packaged in NuGet or otherwise) might be used in the workflow. A lot of the conversation right now is around IDE + MCP integration, but in the future the LLM + MCP will likely run in a lot more places, including non-interactive ones.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a CI context -- yes, it could work that way

I don't think CI builds should ever call an LLM. LLMs are slow, expensive, and non-deterministic. None of those things belong in a CI. All of them together? I really think we should focus on other scenarios.

VS/VSWin could then advertise the MCP server availability for Copilot chat to consume, at which point the MCP Server's prompts, tools, resources, etc. would be available to a .NET MAUI developer

That's interesting. I'll start a conversation with my VS copilot team to get folks thinking about this.

perhaps a bit more controversial

Definitely this. But maybe it'll fly. We'll see.

@joelverhagen
Copy link
Member Author

This is close enough to merge. The areas we will be focusing on for implementation are:

  • Required: Track the .NET preview release that brings single-shot execution
  • Required: Enhance NuGetGallery with MCP awareness
  • Required: Write a tutorial, blog
  • Add support for NuGet on MCP registry, like npm/pypi
  • Make a project template
  • Work with VS Code for integration like npm/pypi
  • Work with MSFT and community authors to use the new NuGet package type

@joelverhagen joelverhagen merged commit 9df2332 into dev Jun 12, 2025
1 check passed
@joelverhagen joelverhagen deleted the dev-joelverhagen-mcp branch June 12, 2025 15:18
@baronfel
Copy link

Required: Track the .NET preview release that brings single-shot execution

We just merged the PR for this, it'll be in 10.0.100-preview.6! As will RID-specific/AOT tools.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants