Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b462b35
add mcp
lei9444 Oct 20, 2025
84d86b7
Merge branch 'main' of https://github.com/microsoft/PowerToys into le…
lei9444 Oct 20, 2025
250f51f
add identity
lei9444 Oct 20, 2025
6d2b982
update sparse app
lei9444 Oct 20, 2025
0243f57
update
lei9444 Oct 20, 2025
f32b253
update
lei9444 Oct 22, 2025
254927f
remove unused
lei9444 Oct 22, 2025
8c3649b
add new tool
lei9444 Oct 22, 2025
603dfca
update description
lei9444 Oct 22, 2025
a2b4318
add mcp
lei9444 Oct 22, 2025
9bbc2ff
update manifest
lei9444 Oct 22, 2025
005ae97
update the name
lei9444 Oct 23, 2025
c0d25aa
remove unused file
lei9444 Oct 23, 2025
402473a
remove backslash
lei9444 Oct 23, 2025
f8e0911
test add entrrpoint
lei9444 Oct 23, 2025
6ebc499
add entrypoint
lei9444 Oct 23, 2025
1fe58af
remove all
lei9444 Oct 23, 2025
cb91c93
Merge branch 'main' of https://github.com/microsoft/PowerToys into le…
lei9444 Oct 28, 2025
f6008bd
hide
lei9444 Oct 30, 2025
63b0e7b
Merge branch 'main' of https://github.com/microsoft/PowerToys into le…
lei9444 Oct 31, 2025
2cd8a5d
Merge branch 'leilzh/mcp' of https://github.com/microsoft/PowerToys i…
lei9444 Oct 31, 2025
0520463
add gpo
lei9444 Nov 3, 2025
97bb0b5
Merge branch 'main' of https://github.com/microsoft/PowerToys into le…
lei9444 Nov 4, 2025
2572598
set false
lei9444 Nov 5, 2025
63475ff
Merge branch 'main' of https://github.com/microsoft/PowerToys into le…
lei9444 Nov 5, 2025
f57c031
add ref
lei9444 Nov 5, 2025
fffb6ce
update the notice.md
lei9444 Nov 5, 2025
32d102b
add telemetry
lei9444 Nov 5, 2025
e0bf663
clean config when set awak
lei9444 Nov 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,7 @@ maxversiontested
mber
MBM
MBR
mcp
MDICHILD
MDL
mdtext
Expand Down
4 changes: 3 additions & 1 deletion .pipelines/ESRPSigning_core.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@
"PowerToys.DSC.dll",
"PowerToys.DSC.exe",

"PowerToysSparse.msix"
"PowerToysSparse.msix",
"PowerToys.McpServer.dll",
"PowerToys.McpServer.exe"
],
"SigningInfo": {
"Operations": [
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.250907003" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageVersion Include="ModelContextProtocol" Version="0.4.0-preview.2" />
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
<!-- Moq to stay below v4.20 due to behavior change. need to be sure fixed -->
<PackageVersion Include="Moq" Version="4.18.4" />
Expand Down
1 change: 1 addition & 0 deletions NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1517,6 +1517,7 @@ SOFTWARE.
- Mages
- Markdig.Signed
- MessagePack
- ModelContextProtocol
- ModernWpfUI
- Moq
- MSTest
Expand Down
11 changes: 11 additions & 0 deletions PowerToys.sln
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AwakeModuleInterface", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Awake", "src\modules\awake\Awake\Awake.csproj", "{D940E07F-532C-4FF3-883F-790DA014F19A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.McpServer", "src\McpServer\PowerToys.McpServer.csproj", "{8A6F5D3B-F59E-4F34-A1D3-3F69D3FDBD9D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.UnitConverter", "src\modules\launcher\Plugins\Community.PowerToys.Run.Plugin.UnitConverter\Community.PowerToys.Run.Plugin.UnitConverter.csproj", "{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.UnitConverter.UnitTest", "src\modules\launcher\Plugins\Community.PowerToys.Run.Plugin.UnitConverter.UnitTest\Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj", "{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}"
Expand Down Expand Up @@ -1456,6 +1458,14 @@ Global
{D940E07F-532C-4FF3-883F-790DA014F19A}.Release|ARM64.Build.0 = Release|ARM64
{D940E07F-532C-4FF3-883F-790DA014F19A}.Release|x64.ActiveCfg = Release|x64
{D940E07F-532C-4FF3-883F-790DA014F19A}.Release|x64.Build.0 = Release|x64
{8A6F5D3B-F59E-4F34-A1D3-3F69D3FDBD9D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{8A6F5D3B-F59E-4F34-A1D3-3F69D3FDBD9D}.Debug|ARM64.Build.0 = Debug|ARM64
{8A6F5D3B-F59E-4F34-A1D3-3F69D3FDBD9D}.Debug|x64.ActiveCfg = Debug|x64
{8A6F5D3B-F59E-4F34-A1D3-3F69D3FDBD9D}.Debug|x64.Build.0 = Debug|x64
{8A6F5D3B-F59E-4F34-A1D3-3F69D3FDBD9D}.Release|ARM64.ActiveCfg = Release|ARM64
{8A6F5D3B-F59E-4F34-A1D3-3F69D3FDBD9D}.Release|ARM64.Build.0 = Release|ARM64
{8A6F5D3B-F59E-4F34-A1D3-3F69D3FDBD9D}.Release|x64.ActiveCfg = Release|x64
{8A6F5D3B-F59E-4F34-A1D3-3F69D3FDBD9D}.Release|x64.Build.0 = Release|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Debug|ARM64.ActiveCfg = Debug|ARM64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Debug|ARM64.Build.0 = Debug|ARM64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Debug|x64.ActiveCfg = Debug|x64
Expand Down Expand Up @@ -3356,6 +3366,7 @@ Global
{4E0FCF69-B06B-D272-76BF-ED3A559B4EDA} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{45354F4F-1414-45CE-B600-51CD1209FD19} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{A66E9270-5D93-EC9C-F06E-CE7295BB9A6C} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}

EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
Expand Down
3 changes: 2 additions & 1 deletion installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1493,11 +1493,12 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
}
processes.resize(bytes / sizeof(processes[0]));

std::array<std::wstring_view, 42> processesToTerminate = {
std::array<std::wstring_view, 43> processesToTerminate = {
L"PowerToys.PowerLauncher.exe",
L"PowerToys.Settings.exe",
L"PowerToys.AdvancedPaste.exe",
L"PowerToys.Awake.exe",
L"PowerToys.McpServer.exe",
L"PowerToys.FancyZones.exe",
L"PowerToys.FancyZonesEditor.exe",
L"PowerToys.FileLocksmithUI.exe",
Expand Down
38 changes: 38 additions & 0 deletions src/McpServer/PowerToys.McpServer.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\Common.SelfContained.props" />

<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>PowerToys.McpServer</RootNamespace>
<AssemblyName>PowerToys.McpServer</AssemblyName>
<Nullable>enable</Nullable>
<OutputPath>..\..\$(Platform)\$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>

<PropertyGroup>
<ApplicationManifest>PowerToys.McpServer.dev.manifest</ApplicationManifest>
</PropertyGroup>

<PropertyGroup Condition="'$(CIBuild)'=='true'">
<ApplicationManifest>PowerToys.McpServer.prod.manifest</ApplicationManifest>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="ModelContextProtocol" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.WindowsDesktop.App" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
<ProjectReference Include="..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
<ProjectReference Include="..\common\GPOWrapperProjection\GPOWrapperProjection.csproj" />
</ItemGroup>
</Project>
8 changes: 8 additions & 0 deletions src/McpServer/PowerToys.McpServer.dev.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="PowerToys.McpServer.app" />
<msix xmlns="urn:schemas-microsoft-com:msix.v1"
publisher="CN=PowerToys Dev, O=PowerToys, L=Redmond, S=Washington, C=US"
packageName="Microsoft.PowerToys.SparseApp"
applicationId="PowerToys.McpServer" />
</assembly>
8 changes: 8 additions & 0 deletions src/McpServer/PowerToys.McpServer.prod.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="PowerToys.McpServer.app" />
<msix xmlns="urn:schemas-microsoft-com:msix.v1"
publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
packageName="Microsoft.PowerToys.SparseApp"
applicationId="PowerToys.McpServer" />
</assembly>
97 changes: 97 additions & 0 deletions src/McpServer/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.IO;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.PowerToys.Telemetry;
using ModelContextProtocol.Server;
using PowerToys.GPOWrapperProjection;
using PowerToys.McpServer.Telemetry.Events;
using PowerToys.McpServer.Tools;
using GpoProjection = PowerToys.GPOWrapperProjection.GPOWrapper;

namespace PowerToys.McpServer
{
internal sealed class Program
{
private static async Task<int> Main(string[] args)
{
// Initialize PowerToys logger
// Logger.InitializeLogger expects path relative to Constants.AppDataPath()
// which already points to LocalAppData\Microsoft\PowerToys
string logPath = Path.Combine("McpServer", "Logs");
Logger.InitializeLogger(logPath);
Logger.LogInfo("Starting PowerToys MCP Server with official SDK");

try
{
var mcpGpoState = GpoProjection.GetConfiguredMcpServerEnabledValue();
if (mcpGpoState == GpoRuleConfigured.Disabled)
{
PowerToysTelemetry.Log.WriteEvent(new McpServerStartFailedEvent
{
Reason = "BlockedByPolicy",
});
const string policyMessage = "MCP server launch blocked by Group Policy.";
Logger.LogWarning(policyMessage);
Console.Error.WriteLine(policyMessage);
return 0;
}

var builder = Host.CreateApplicationBuilder(args);

// Configure all logs to go to stderr (required for MCP protocol)
builder.Logging.AddConsole(consoleLogOptions =>
{
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});

// Register MCP server with stdio transport and tools
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();

using var host = builder.Build();

PowerToysTelemetry.Log.WriteEvent(new McpServerStartedEvent());

Logger.LogInfo("Building and running MCP host...");
await host.RunAsync();
Logger.LogInfo("MCP server shutdown complete");

return 0;
}
catch (Exception ex)
{
PowerToysTelemetry.Log.WriteEvent(new McpServerStartFailedEvent
{
Reason = BuildFailureReason(ex),
});
Logger.LogError("Fatal error in MCP server", ex);
return 1;
}
}

private static string BuildFailureReason(Exception ex)
{
string type = ex.GetType().FullName ?? "UnknownException";
string message = ex.Message ?? string.Empty;
string combined = $"UnhandledException:{type}:{message}";

const int maxLength = 256;
if (combined.Length <= maxLength)
{
return combined;
}

return combined.Substring(0, maxLength);
}
}
}
102 changes: 102 additions & 0 deletions src/McpServer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# PowerToys Model Context Protocol Server

This module hosts a standalone Model Context Protocol (MCP) server that exposes PowerToys functionality to MCP-compliant AI agents. The server is built as a .NET 9 console application that implements the MCP specification using the official ModelContextProtocol SDK.

## Project Structure

- **Program.cs**: Main entry point that configures the MCP server with stdio transport
- **Tools/AwakeTools.cs**: Implementation of Awake-related MCP tools
- **PowerToys.McpServer.csproj**: .NET 9 project configuration with MCP dependencies

## Dependencies

- **Microsoft.Extensions.Hosting**: For hosting infrastructure and dependency injection
- **ModelContextProtocol**: Official MCP SDK for .NET
- **PowerToys Settings Library**: Integration with PowerToys settings system
- **ManagedCommon**: PowerToys logging and utilities

## Available Tools

| Tool Name | Description | Parameters | Module |
|-----------|-------------|------------|--------|
| `GetAwakeStatus` | Returns the current Awake configuration (mode, timers, display policy) | None | Awake |
| `SetAwakePassive` | Set Awake to passive mode (allow system to sleep normally) | None | Awake |
| `SetAwakeIndefinite` | Set Awake to indefinite mode (keep system awake until manually changed) | `keepDisplayOn` (bool), `force` (bool) | Awake |
| `SetAwakeTimed` | Set Awake to timed mode (keep system awake for a specific duration) | `durationSeconds` (int), `keepDisplayOn` (bool), `force` (bool) | Awake |

## Building and Running

### Prerequisites
- .NET 9 SDK
- Visual Studio 2022 (recommended) or VS Code with C# extension

### Build
```bash
# From PowerToys root directory
msbuild src/McpServer/PowerToys.McpServer.csproj /p:Platform=x64 /p:Configuration=Debug

# Or using dotnet CLI
cd src/McpServer
dotnet build -c Debug
```

### Run the Server
The executable is built to `x64\Debug\PowerToys.McpServer.exe`. The server communicates over standard input/output using MCP framing (`Content-Length` header followed by JSON).

**Example MCP Client Session:**
1. Client sends `initialize` request with MCP version and capabilities
2. Client calls `tools/list` to discover available PowerToys tools
3. Client invokes `tools/call` with the desired tool name and arguments
4. Server responds with tool execution results or errors

The server will remain active until the process is terminated or a `shutdown` request is received.

### Logging
- Application logs are written to `%LOCALAPPDATA%\Microsoft\PowerToys\McpServer\Logs\`
- MCP protocol logs are sent to stderr (required by MCP specification)

## Architecture

The server uses the official ModelContextProtocol .NET SDK and follows these patterns:

- **Tool Discovery**: Tools are automatically discovered using `WithToolsFromAssembly()`
- **Tool Attributes**: Methods marked with `[McpServerTool]` and `[Description]` are exposed as MCP tools
- **Parameter Binding**: Method parameters are automatically bound from MCP tool call arguments
- **Error Handling**: Exceptions are caught and returned as MCP error responses
- **Settings Integration**: Uses PowerToys settings system for configuration persistence

## Adding New Module Tools

1. Create a new static class in the `Tools/` directory (e.g., `FancyZonesTools.cs`)
2. Mark the class with `[McpServerToolType]` attribute
3. Implement static methods with `[McpServerTool]` and `[Description]` attributes:
```csharp
[McpServerToolType]
public static class MyModuleTools
{
[McpServerTool]
[Description("Description of what this tool does")]
public static JsonObject MyTool(
[Description("Parameter description")] string parameter)
{
// Implementation here
return new JsonObject();
}
}
```
4. Follow existing patterns in `AwakeTools.cs` for:
- Settings integration using `SettingsUtils`
- Logging using `Logger.LogInfo/LogError`
- Error handling and response formatting
- PowerToys process detection and module status checks

## Integration with PowerToys

The MCP server integrates with PowerToys through:

- **Settings System**: Uses the same settings files as the main PowerToys application
- **Process Management**: Detects and interacts with running PowerToys processes
- **Module Status**: Checks if specific PowerToys modules are enabled
- **Logging**: Uses PowerToys logging infrastructure for troubleshooting

Refer to the PowerToys developer documentation for build and packaging instructions.
25 changes: 25 additions & 0 deletions src/McpServer/Telemetry/Events/McpServerStartFailedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;

namespace PowerToys.McpServer.Telemetry.Events
{
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class McpServerStartFailedEvent : EventBase, IEvent
{
public McpServerStartFailedEvent()
{
EventName = "McpServer_StartFailed";
}

public string Reason { get; set; } = string.Empty;

public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServicePerformance;
}
}
23 changes: 23 additions & 0 deletions src/McpServer/Telemetry/Events/McpServerStartedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;

namespace PowerToys.McpServer.Telemetry.Events
{
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class McpServerStartedEvent : EventBase, IEvent
{
public McpServerStartedEvent()
{
EventName = "McpServer_Started";
}

public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}
Loading
Loading