Repository: unoplatform/uno
File: src/Uno.UI.DevServer.Cli/McpProxy.cs (C#)

Code before:
		startInfo.ArgumentList.Add(Environment.ProcessId.ToString(System.Globalization.CultureInfo.InvariantCulture));

		foreach (var fa in forwardedArgs)
		{
			startInfo.ArgumentList.Add(fa);
		}

		_logger.LogInformation("Launching devserver controller: {File} {Args}", startInfo.FileName, string.Join(" ", startInfo.ArgumentList));

		var controller = Process.Start(startInfo);
		if (controller is null)
		{
			_logger.LogError("Failed to start devserver controller process");
			return (success: false, exitCode: 1);
		}

		var stdoutCapture = controller.StandardOutput.ReadToEndAsync();
		var stderrCapture = controller.StandardError.ReadToEndAsync();

		// We launch the devserver controller, which in turn launches the actual devserver.
		await controller.WaitForExitAsync(ct);

		if (controller.ExitCode != 0)
		{
			_logger.LogError("DevServer controller exited with code {ExitCode}", controller.ExitCode);
			var so = await stdoutCapture;
			var se = await stderrCapture;

			if (!string.IsNullOrWhiteSpace(so))
			{
				_logger.LogDebug("Controller stdout:\n{Stdout}", so);
			}

			if (!string.IsNullOrWhiteSpace(se))
			{
				_logger.LogError("Controller stderr:\n{Stderr}", se);
			}

			return (success: false, exitCode: controller.ExitCode);
		}

		controller.Dispose();

		return (success: true, exitCode: null);
	}

	private async Task<int> StartMcpStdIoProxyAsync(string remoteEndpoint, CancellationToken ct)
	{
		var builder = Host.CreateApplicationBuilder();

		IMcpClient? upstreamClient = null;

Review comment (bug):
NOTE: Waiting for stdout then waiting for stderr can lead to deadlocks.

The stream buffers are somewhere around 4kb.

If the child process tries to write more than 4kb to stderr, this will deadlock.

- Child process will successfully write the first 4kb.
- Parent process here is waiting to consume the whole stdout.
- Child process cannot write more to stderr, because it's waiting for the parent process to consume from the stream so that there is space in the buffer to write.
- Parent process isn't consuming stderr because it's waiting for stdout.
- Deadlock.

Current approach is so risky.

Code after:

				var name = ctx.Params!.Name;
				var args = ctx.Params.Arguments ?? new Dictionary<string, JsonElement>();
				var adjustedArguments = args.ToDictionary(v => v.Key, v => (object?)v.Value);

				var result = await upstreamClient!.CallToolAsync(
					name,
					adjustedArguments,
					cancellationToken: ct
				);

				return result;
			})
			.WithListToolsHandler(async (ctx, ct) =>
			{
				_logger.LogTrace("Client requested tools list update");

				var list = await upstreamClient!.ListToolsAsync(cancellationToken: ct);

				_logger.LogDebug("Reporting {Count} tools", list.Count);

				return new ListToolsResult
				{
					Tools = list.Select(t => t.ProtocolTool).ToList()
				};
			});

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

		var host = builder.Build();

		var logger = host.Services.GetRequiredService<ILoggerFactory>().CreateLogger("ToolPoller");

		upstreamClient = await ConnectOrDieAsync(
			remoteEndpoint,
			async () =>
			{
				await host.Services.GetRequiredService<IMcpServer>().SendNotificationAsync(
					NotificationMethods.ToolListChangedNotification,
					new ResourceUpdatedNotificationParams()
				);
			},
			host.Services.GetRequiredService<ILoggerFactory>().CreateLogger("UpstreamConnector")
		);

		try
		{