Skip to content

Commit face1be

Browse files
authored
Merge pull request #21831 from unoplatform/mergify/bp/release/stable/6.4/pr-21820
fix(mcp): Adjust for no tools_update notification on claude/codex (backport #21820)
2 parents ff43b41 + 050449f commit face1be

File tree

7 files changed

+42
-13
lines changed

7 files changed

+42
-13
lines changed

build/ci/tests/.azure-devops-tests-templates.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ jobs:
221221

222222
- job: Dotnet_Tests_Validate_DevServerCli
223223
displayName: 'DevServer CLI Tests'
224-
timeoutInMinutes: 120
224+
timeoutInMinutes: 25
225225
cancelTimeoutInMinutes: 1
226226

227227
pool:

doc/articles/get-started-ai-claude.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This guide will walk you through the setup process for getting started with Clau
1717

1818
```bash
1919
claude mcp add --transport http uno https://mcp.platform.uno/v1
20-
claude mcp add --transport stdio "uno-app" -- dnx -y uno.devserver --mcp-app
20+
claude mcp add --transport stdio "uno-app" -- dotnet dnx -y uno.devserver --mcp-app
2121
```
2222

2323
1. Open Claude and run:

doc/articles/get-started-ai-codex.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This guide will walk you through the setup process for getting started with Code
1717

1818
```bash
1919
codex mcp add "uno" --url "https://mcp.platform.uno/v1"
20-
codex mcp add "uno-app" "dnx -y uno.devserver --mcp-app"
20+
codex mcp add "uno-app" -- dotnet dnx -y uno.devserver --mcp-app
2121
```
2222

2323
1. Start Codex CLI and type the following:

doc/articles/get-started-ai-gh-copilot-cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ To get started with GitHub Copilot CLI:
2424
1. Type `/mcp` again and register the following (app-specific MCP):
2525
1. Unique Name: `uno-app`
2626
1. Server Type: Local
27-
1. Command: `dnx -y uno.devserver --mcp-app`
27+
1. Command: `dotnet dnx -y uno.devserver --mcp-app`
2828
1. Skip Environment Variables and leave tools with `*`
2929

3030
## Next Steps

src/Uno.UI.DevServer.Cli/CliManager.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ private async Task<int> RunMcpProxyAsync(string[] args)
123123
_logger.LogInformation("Starting MCP Mode");
124124

125125
int requestedPort = 0;
126+
bool mcpWaitToolsList = false;
126127
var forwardedArgs = new List<string>();
128+
127129
for (int i = 0; i < args.Length; i++)
128130
{
129131
var a = args[i];
@@ -142,10 +144,16 @@ private async Task<int> RunMcpProxyAsync(string[] args)
142144
i++; // skip value
143145
continue; // do not forward port arguments to controller
144146
}
147+
else if (a == "--mcp-wait-tools-list")
148+
{
149+
mcpWaitToolsList = true;
150+
continue; // do not forward mcp-specific arguments to controller
151+
}
145152
forwardedArgs.Add(a);
146153
}
147154

148-
return await _services.GetRequiredService<McpProxy>().RunAsync(Environment.CurrentDirectory, requestedPort, forwardedArgs, CancellationToken.None);
155+
var waitForTools = mcpWaitToolsList;
156+
return await _services.GetRequiredService<McpProxy>().RunAsync(Environment.CurrentDirectory, requestedPort, forwardedArgs, waitForTools, CancellationToken.None);
149157
}
150158
catch (Exception ex)
151159
{

src/Uno.UI.DevServer.Cli/Mcp/McpProxy.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ internal class McpProxy
1818
private readonly ILogger<McpProxy> _logger;
1919
private readonly DevServerMonitor _devServerMonitor;
2020
private readonly McpClientProxy _mcpClientProxy;
21+
private bool _waitForTools;
22+
23+
// Clients that don't support the list_updated notification
24+
private static readonly string[] ClientsWithoutListUpdateSupport = ["claude-code", "codex"];
2125

2226
public McpProxy(ILogger<McpProxy> logger, DevServerMonitor mcpServerMonitor, McpClientProxy mcpClientProxy)
2327
{
@@ -26,8 +30,9 @@ public McpProxy(ILogger<McpProxy> logger, DevServerMonitor mcpServerMonitor, Mcp
2630
_mcpClientProxy = mcpClientProxy;
2731
}
2832

29-
public async Task<int> RunAsync(string currentDirectory, int port, List<string> forwardedArgs, CancellationToken ct)
33+
public async Task<int> RunAsync(string currentDirectory, int port, List<string> forwardedArgs, bool waitForTools, CancellationToken ct)
3034
{
35+
_waitForTools = waitForTools;
3136
_devServerMonitor.StartMonitoring(currentDirectory, port, forwardedArgs);
3237

3338
try
@@ -44,6 +49,8 @@ public async Task<int> RunAsync(string currentDirectory, int port, List<string>
4449

4550
private async Task<int> StartMcpStdIoProxyAsync(CancellationToken ct)
4651
{
52+
var tcs = new TaskCompletionSource();
53+
4754
var builder = Host.CreateApplicationBuilder();
4855
builder.Services
4956
.AddMcpServer()
@@ -73,6 +80,17 @@ private async Task<int> StartMcpStdIoProxyAsync(CancellationToken ct)
7380
})
7481
.WithListToolsHandler(async (ctx, ct) =>
7582
{
83+
// Claude Code and Codex do not support the list_updated notification.
84+
// To avoid tool invocation failures, we wait for the tools to be available
85+
// after the dev server has started.
86+
if (_waitForTools
87+
|| ClientsWithoutListUpdateSupport.Contains(ctx.Server.ClientInfo?.Name))
88+
{
89+
_logger.LogTrace("Client without list_updated support detected, waiting for upstream server to start");
90+
91+
await tcs.Task;
92+
}
93+
7694
var upstreamClient = _mcpClientProxy.UpstreamClient;
7795

7896
if (upstreamClient is null)
@@ -103,6 +121,8 @@ private async Task<int> StartMcpStdIoProxyAsync(CancellationToken ct)
103121

104122
_mcpClientProxy.RegisterToolListChangedCallback(async () =>
105123
{
124+
tcs.TrySetResult();
125+
106126
await host.Services.GetRequiredService<IMcpServer>().SendNotificationAsync(
107127
NotificationMethods.ToolListChangedNotification,
108128
new ResourceUpdatedNotificationParams()

src/Uno.UI.DevServer.Cli/Program.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ private static async Task<int> Main(string[] args)
1717
Console.WriteLine("Usage: dnx -y uno.devserver [options] [command]");
1818
Console.WriteLine();
1919
Console.WriteLine("Options:");
20-
Console.WriteLine(" --help, -h Show this help message and exit");
21-
Console.WriteLine(" --log-level, -l <level> Set the log level (Trace, Debug, Information, Warning, Error, Critical, None). Default is Information.");
22-
Console.WriteLine(" --file-log, -fl <path> Enable file logging to the provided file path (supports {Date} token). Required path argument.");
23-
Console.WriteLine(" --mcp-app Start in App MCP in STDIO mode");
20+
Console.WriteLine(" --help, -h Show this help message and exit");
21+
Console.WriteLine(" --log-level, -l <level> Set the log level (Trace, Debug, Information, Warning, Error, Critical, None). Default is Information.");
22+
Console.WriteLine(" --file-log, -fl <path> Enable file logging to the provided file path (supports {Date} token). Required path argument.");
23+
Console.WriteLine(" --mcp Start in MCP STDIO mode");
24+
Console.WriteLine(" --mcp-wait-tools-list Wait for upstream server tools before responding to list_tools (MCP mode only)");
2425
Console.WriteLine();
2526
Console.WriteLine("Commands:");
26-
Console.WriteLine(" start Start the DevServer for the current folder");
27-
Console.WriteLine(" stop Stop the DevServer for the current folder");
28-
Console.WriteLine(" list List active DevServer instances");
27+
Console.WriteLine(" start Start the DevServer for the current folder");
28+
Console.WriteLine(" stop Stop the DevServer for the current folder");
29+
Console.WriteLine(" list List active DevServer instances");
2930
Console.WriteLine();
3031
return 0;
3132
}

0 commit comments

Comments
 (0)