Files
2026-07-13 12:49:17 +08:00

147 lines
5.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using MCPForUnity.Editor.Clients;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Models;
namespace MCPForUnity.Editor.Services
{
/// <summary>
/// Implementation of client configuration service
/// </summary>
public class ClientConfigurationService : IClientConfigurationService
{
private readonly List<IMcpClientConfigurator> configurators;
public ClientConfigurationService()
{
configurators = McpClientRegistry.All.ToList();
}
public IReadOnlyList<IMcpClientConfigurator> GetAllClients() => configurators;
public void ConfigureClient(IMcpClientConfigurator configurator)
{
// When using a local server path, clean stale build artifacts first.
// This prevents old deleted .py files from being picked up by Python's auto-discovery.
if (AssetPathUtility.IsLocalServerPath())
{
AssetPathUtility.CleanLocalServerBuildArtifacts();
}
ConfigureWithTransportCoercion(configurator);
}
public ClientConfigurationSummary ConfigureAllDetectedClients()
{
// When using a local server path, clean stale build artifacts once before configuring all clients.
if (AssetPathUtility.IsLocalServerPath())
{
AssetPathUtility.CleanLocalServerBuildArtifacts();
}
var summary = new ClientConfigurationSummary();
foreach (var configurator in configurators)
{
if (!configurator.IsInstalled)
{
summary.SkippedCount++;
continue;
}
try
{
// Always re-run configuration so core fields stay current
configurator.CheckStatus(attemptAutoRewrite: false);
ConfigureWithTransportCoercion(configurator);
summary.SuccessCount++;
summary.Messages.Add($"✓ {configurator.DisplayName}: Configured successfully");
}
catch (Exception ex)
{
summary.FailureCount++;
summary.Messages.Add($"⚠ {configurator.DisplayName}: {ex.Message}");
}
}
return summary;
}
private static void ConfigureWithTransportCoercion(IMcpClientConfigurator configurator)
{
bool originalHttp = EditorConfigurationCache.Instance.UseHttpTransport;
try
{
CoerceTransportFor(configurator);
configurator.Configure();
}
finally
{
EditorConfigurationCache.Instance.SetUseHttpTransport(originalHttp);
}
}
public bool CheckClientStatus(IMcpClientConfigurator configurator, bool attemptAutoRewrite = true)
{
var previous = configurator.Status;
var current = configurator.CheckStatus(attemptAutoRewrite);
return current != previous;
}
private static void CoerceTransportFor(IMcpClientConfigurator configurator)
{
var supported = configurator.SupportedTransports;
if (supported == null || supported.Count == 0) return;
bool currentlyHttp = EditorConfigurationCache.Instance.UseHttpTransport;
var requested = currentlyHttp ? ConfiguredTransport.Http : ConfiguredTransport.Stdio;
// Accept any HTTP variant (Http, HttpRemote) when the user wants HTTP — a client that
// only supports HttpRemote should not get coerced to stdio just because Http isn't
// explicitly listed.
if (SupportsRequested(supported, requested)) return;
// Fall back in the direction of the user's intent: if they wanted HTTP, prefer any
// HTTP variant the client does support; otherwise prefer stdio. Honors the
// configurator's declared order when more than one option remains.
ConfiguredTransport chosen = PickFallback(supported, requested);
bool needHttp = IsHttpVariant(chosen);
if (EditorConfigurationCache.Instance.UseHttpTransport != needHttp)
{
EditorConfigurationCache.Instance.SetUseHttpTransport(needHttp);
McpLog.Info(
$"[{configurator.DisplayName}] auto-selected {chosen} transport (client does not support {requested}).");
}
}
private static bool IsHttpVariant(ConfiguredTransport t)
=> t == ConfiguredTransport.Http || t == ConfiguredTransport.HttpRemote;
private static bool SupportsRequested(IReadOnlyList<ConfiguredTransport> supported, ConfiguredTransport requested)
{
if (requested == ConfiguredTransport.Http)
{
foreach (var t in supported)
if (IsHttpVariant(t)) return true;
return false;
}
return supported.Contains(requested);
}
private static ConfiguredTransport PickFallback(IReadOnlyList<ConfiguredTransport> supported, ConfiguredTransport requested)
{
if (requested == ConfiguredTransport.Http)
{
foreach (var t in supported)
if (IsHttpVariant(t)) return t;
}
else
{
foreach (var t in supported)
if (t == ConfiguredTransport.Stdio) return t;
}
return supported[0];
}
}
}