/* ┌──────────────────────────────────────────────────────────────────┐ │ Author: Ivan Murzak (https://github.com/IvanMurzak) │ │ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │ │ Copyright (c) 2025 Ivan Murzak │ │ Licensed under the Apache License, Version 2.0. │ │ See the LICENSE file in the project root for more information. │ └──────────────────────────────────────────────────────────────────┘ */ #nullable enable using System.IO; using System.Text.Json.Nodes; using com.IvanMurzak.Unity.MCP.Editor.Utils; using UnityEngine.UIElements; using static com.IvanMurzak.McpPlugin.Common.Consts.MCP.Server; namespace com.IvanMurzak.Unity.MCP.Editor.UI { /// /// Configurator for Gemini AI agent. /// public class GeminiConfigurator : AiAgentConfigurator { public override string AgentName => "Gemini"; public override string AgentId => "gemini"; public override string DownloadUrl => "https://geminicli.com/docs/get-started/installation/"; public override string? SkillsPath => ".gemini/skills"; protected override string? IconFileName => "gemini-64.png"; private static string LocalConfigPath => Path.Combine(ProjectRootPath, ".gemini", "settings.json"); protected override AiAgentConfig CreateConfigStdioWindows() => new JsonAiAgentConfig( name: AgentName, configPath: LocalConfigPath, bodyPath: DefaultBodyPath ) .SetProperty("type", JsonValue.Create("stdio"), requiredForConfiguration: true) .SetProperty("command", JsonValue.Create(McpServerManager.ExecutableFullPath.Replace('\\', '/')), requiredForConfiguration: true, comparison: ValueComparisonMode.Path) .SetProperty("args", new JsonArray { $"{Args.Port}={UnityMcpPluginEditor.Port}", $"{Args.PluginTimeout}={UnityMcpPluginEditor.TimeoutMs}", $"{Args.ClientTransportMethod}={TransportMethod.stdio}", $"{Args.Authorization}={UnityMcpPluginEditor.AuthOption}", $"{Args.Token}={UnityMcpPluginEditor.Token}" }, requiredForConfiguration: true) .SetPropertyToRemove("url"); protected override AiAgentConfig CreateConfigStdioMacLinux() => new JsonAiAgentConfig( name: AgentName, configPath: LocalConfigPath, bodyPath: DefaultBodyPath ) .SetProperty("type", JsonValue.Create("stdio"), requiredForConfiguration: true) .SetProperty("command", JsonValue.Create(McpServerManager.ExecutableFullPath.Replace('\\', '/')), requiredForConfiguration: true, comparison: ValueComparisonMode.Path) .SetProperty("args", new JsonArray { $"{Args.Port}={UnityMcpPluginEditor.Port}", $"{Args.PluginTimeout}={UnityMcpPluginEditor.TimeoutMs}", $"{Args.ClientTransportMethod}={TransportMethod.stdio}", $"{Args.Authorization}={UnityMcpPluginEditor.AuthOption}", $"{Args.Token}={UnityMcpPluginEditor.Token}" }, requiredForConfiguration: true) .SetPropertyToRemove("url"); protected override AiAgentConfig CreateConfigHttpWindows() => new JsonAiAgentConfig( name: AgentName, configPath: LocalConfigPath, bodyPath: DefaultBodyPath ) .SetProperty("type", JsonValue.Create("http"), requiredForConfiguration: true) .SetProperty("url", JsonValue.Create(UnityMcpPluginEditor.Host), requiredForConfiguration: true, comparison: ValueComparisonMode.Url) .SetPropertyToRemove("command") .SetPropertyToRemove("args"); protected override AiAgentConfig CreateConfigHttpMacLinux() => new JsonAiAgentConfig( name: AgentName, configPath: LocalConfigPath, bodyPath: DefaultBodyPath ) .SetProperty("type", JsonValue.Create("http"), requiredForConfiguration: true) .SetProperty("url", JsonValue.Create(UnityMcpPluginEditor.Host), requiredForConfiguration: true, comparison: ValueComparisonMode.Url) .SetPropertyToRemove("command") .SetPropertyToRemove("args"); protected override void OnUICreated(VisualElement root) { base.OnUICreated(root); var addMcpServerCommandStdio = $"gemini mcp add {AiAgentConfig.DefaultMcpServerName} \"{McpServerManager.ExecutableFullPath}\" port={UnityMcpPluginEditor.Port} plugin-timeout={UnityMcpPluginEditor.TimeoutMs} client-transport=stdio"; var addMcpServerCommandHttp = $"gemini mcp add --transport http {AiAgentConfig.DefaultMcpServerName} {UnityMcpPluginEditor.Host}"; // STDIO Configuration var manualStepsOption1 = TemplateFoldoutFirst("Manual Configuration Steps - Option 1"); manualStepsOption1!.Add(TemplateLabelDescription("1. Open a terminal and run the following command to be in the folder of the Unity project")); manualStepsOption1!.Add(TemplateTextFieldReadOnly($"cd \"{ProjectRootPath}\"")); manualStepsOption1!.Add(TemplateLabelDescription("2. Run the following command in the folder of the Unity project to configure Gemini")); manualStepsOption1!.Add(TemplateTextFieldReadOnly(addMcpServerCommandStdio)); manualStepsOption1!.Add(TemplateLabelDescription("3. Start Gemini")); manualStepsOption1!.Add(TemplateTextFieldReadOnly("gemini --debug")); ContainerStdio!.Add(manualStepsOption1); var manualStepsOption2 = TemplateFoldout("Manual Configuration Steps - Option 2"); manualStepsOption2!.Add(TemplateLabelDescription("1. Open or create file '.gemini/settings.json'")); manualStepsOption2!.Add(TemplateLabelDescription("2. Copy and paste the configuration json into the file.")); manualStepsOption2!.Add(TemplateTextFieldReadOnly(ConfigStdio.ExpectedFileContent)); ContainerStdio!.Add(manualStepsOption2); var troubleshootingContainerStdio = TemplateFoldout("Troubleshooting"); troubleshootingContainerStdio.Add(TemplateLabelDescription("- Ensure Gemini CLI is installed and accessible from terminal")); troubleshootingContainerStdio.Add(TemplateLabelDescription("- Start Gemini with --debug flag, it helps MCP server to work properly with Gemini in stdio transport mode.")); troubleshootingContainerStdio.Add(TemplateLabelDescription("- Ensure MCP configuration file doesn't have syntax errors")); troubleshootingContainerStdio.Add(TemplateLabelDescription("- Restart Gemini after configuration changes")); ContainerStdio!.Add(troubleshootingContainerStdio); // HTTP Configuration var manualStepsOption1Http = TemplateFoldoutFirst("Manual Configuration Steps - Option 1"); manualStepsOption1Http!.Add(TemplateLabelDescription("1. Open a terminal and run the following command to be in the folder of the Unity project")); manualStepsOption1Http!.Add(TemplateTextFieldReadOnly($"cd \"{ProjectRootPath}\"")); manualStepsOption1Http!.Add(TemplateLabelDescription("2. Run the following command in the folder of the Unity project to configure Gemini")); manualStepsOption1Http!.Add(TemplateTextFieldReadOnly(addMcpServerCommandHttp)); manualStepsOption1Http!.Add(TemplateLabelDescription("3. Start Gemini")); manualStepsOption1Http!.Add(TemplateTextFieldReadOnly("gemini --debug")); ContainerHttp!.Add(manualStepsOption1Http); var manualStepsOption2Http = TemplateFoldout("Manual Configuration Steps - Option 2"); manualStepsOption2Http!.Add(TemplateLabelDescription("1. Open or create file '.gemini/settings.json'")); manualStepsOption2Http!.Add(TemplateLabelDescription("2. Copy and paste the configuration json into the file.")); manualStepsOption2Http!.Add(TemplateTextFieldReadOnly(ConfigHttp.ExpectedFileContent)); ContainerHttp!.Add(manualStepsOption2Http); var troubleshootingContainerHttp = TemplateFoldout("Troubleshooting"); troubleshootingContainerHttp.Add(TemplateLabelDescription("- Ensure Gemini CLI is installed and accessible from terminal")); troubleshootingContainerHttp.Add(TemplateLabelDescription("- Ensure MCP configuration file doesn't have syntax errors")); troubleshootingContainerHttp.Add(TemplateLabelDescription("- Restart Gemini after configuration changes")); ContainerHttp!.Add(troubleshootingContainerHttp); } } }