-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Initial MCP Server implementation #5610
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
Conversation
…her than full path
<data name="ConfigurationEnableMessage" xml:space="preserve"> | ||
<value>Enable configuration components. Requires store access.</value> | ||
<data name="ExtendedFeaturesEnableMessage" xml:space="preserve"> | ||
<value>Enable extended features. Requires store access.</value> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit concerned about there being confusion between "experimental" and "extended" features, but I can't think of a better naming.
@@ -119,4 +119,7 @@ namespace AppInstaller::Filesystem | |||
// Gets the PathDetails used for the given path. | |||
// This is exposed primarily to allow for testing, GetPathTo should be preferred. | |||
PathDetails GetPathDetailsFor(PathName path, bool forDisplay = false); | |||
|
|||
// Gets the path to the executable for the given process id. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are HANDLE
s just the process ID? 🤯
(Or is the comment wrong?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment is wrong because I wrote it with process id at first and then changed it when I realized what I actually had to pass in. Might hold on changing it in case there aren't any other substantial issues.
} | ||
|
||
// First attempt a more exact match | ||
var findResult = FindForIdentifier(packageCatalog, identifier, expandedFields: false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The search is using "FindForQuery", so it may return different results. I'm pretty sure people don't like that in the CLI, but I don't know how this ends up being called by the hosts to know if that would be an issue here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The search tool does:
- ID equals OR Name equals OR Moniker equals (what the CLI uses for "targeted" commands)
- ID contains OR Name contains OR Moniker equals (a looser version to provide some flexibility to the agent)
only using option 2 if nothing is found with option 1.
The install tool does:
- Id equals
- Id equals OR Name equals OR Moniker equals
similarly falling back to 2 only if nothing is found with 1.
The expectation is that the agent will always use the search tool to gather information, then use the result from that to inform the identifier field to use in the install tool.
|
||
public string? InstalledLocation { get; set; } | ||
|
||
public bool? IsUpdateAvailable { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't include the available version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
COM doesn't provide it easily.
public string? Message { get; set; } | ||
|
||
public bool? RebootRequired { get; set; } | ||
|
||
public int? ErrorCode { get; set; } | ||
|
||
public uint? InstallerErrorCode { get; set; } | ||
|
||
public FindPackageResult? InstalledPackageInformation { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does MCP allow us to say what any of these mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, as far as I know, there is no ability to provide output schemas, despite the MCP documentation suggesting that you should have them.
{ | ||
public string? Message { get; set; } | ||
|
||
public bool? RebootRequired { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have information about this in the manifest, right? Could we report it ahead of time using the progress message? I think it would be nice to not get that information at the very end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I can get it in the search results, then I will add it there so that the agent can better reason over that fact.
Is this not going to be behind an experimental feature? |
Also, I'd really appreciate it if you could update doc/ReleaseNotes.md with this feature |
I don't see it as needed given that the user needs to opt-in to using it by manually configuring their client. The implementation of that block would either require reading the config in the MCP server or hacking in a check based on the caller info. @denelon for opinion. |
@@ -176,6 +176,16 @@ | |||
<decimal value="0" /> | |||
</disabledValue> | |||
</policy> | |||
<policy name="EnableWindowsPackageManagerMcpServer" class="Machine" displayName="$(string.EnableWindowsPackageManagerMcpServer)" explainText="$(string.EnableWindowsPackageManagerMcpServerExplanation)" key="Software\Policies\Microsoft\Windows\AppInstaller" valueName="EnableWindowsPackageManagerMcpServer"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case we have not done it, we'll need to talk to policy team about updating the policy template later.
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" /> | ||
<PackageVersion Include="Microsoft.Msix.Utils" Version="2.1.1" /> | ||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> | ||
<PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.4.6" /> | ||
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" /> | ||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.6" /> | ||
<PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.26100.1742" /> | ||
<PackageVersion Include="ModelContextProtocol" Version="0.2.0-preview.3" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: do we want to add a todo to update to stable version later?
@@ -3,6 +3,7 @@ | |||
#include "pch.h" | |||
#include "ConfigureExportCommand.h" | |||
#include "Workflows/ConfigurationFlow.h" | |||
#include "Workflows/MSStoreInstallerHandler.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I'm wondering, why did we not put this command under Commands folder ...
if (SUCCEEDED(wil::QueryFullProcessImageNameW(processHandle.get(), 0, imageName)) && | ||
(imageName.get() != nullptr)) | ||
std::filesystem::path executablePath = AppInstaller::Filesystem::GetExecutablePathForProcess(processHandle.get()); | ||
if (executablePath.has_filename()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the executable path does not have file name, should we return the full path instead of empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so; that sounds like an error and logging would be appropriate.
using Microsoft.Management.Deployment; | ||
using ModelContextProtocol.Protocol; | ||
using ModelContextProtocol.Server; | ||
using System.ComponentModel; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I guess Stylecop is not turned on for this project? It should complain that System. should be on the top.
.WithStdioServerTransport() | ||
.WithTools<WingetPackageTools>(); | ||
|
||
builder.Build().Run(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this handle shutdown events automagically by the framework?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I doubt it, but I had thought of that. Maybe my change to improve our quiesce story should include an event to make it easier for clients to act appropriately without needing to re-implement the entire "window to listen for shutdown messages".
Discussion on further MCP work: #5609
Change
This change adds an MCP server that provides two tools:
winget
CLI)The package response looks like:
Also includes:
mcp
command that helps with manually configuring an MCP clientVersion
property on thePackageManager
COM object that returns the version string as1.2.3
or1.2.3-preview
.Validation
I have ensured that the various portions of this change function within the context of the dev package.
Microsoft Reviewers: Open in CodeFlow