From 51fb16cafd2691c12a8adde55dced0586eab0abb Mon Sep 17 00:00:00 2001 From: Akshay Khandizod Date: Tue, 30 Dec 2025 11:57:51 +0530 Subject: [PATCH 1/2] feat: Add OpenCode (opencode.ai) client configurator - Support for OpenCode's custom config format at ~/.config/opencode/opencode.json - Uses "mcp" object with "type": "remote" for HTTP transport - Auto-configures and preserves existing MCP servers in config --- .../Configurators/OpenCodeConfigurator.cs | 165 ++++++++++++++++++ .../OpenCodeConfigurator.cs.meta | 2 + 2 files changed, 167 insertions(+) create mode 100644 MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs create mode 100644 MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs.meta diff --git a/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs b/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs new file mode 100644 index 000000000..d4df5b05a --- /dev/null +++ b/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.IO; +using MCPForUnity.Editor.Helpers; +using MCPForUnity.Editor.Models; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace MCPForUnity.Editor.Clients.Configurators +{ + /// + /// Configurator for OpenCode (opencode.ai) - a Go-based terminal AI coding assistant. + /// OpenCode uses ~/.config/opencode/opencode.json with a custom "mcp" format. + /// + public class OpenCodeConfigurator : McpClientConfiguratorBase + { + private const string ServerName = "unityMCP"; + + public OpenCodeConfigurator() : base(new McpClient + { + name = "OpenCode", + windowsConfigPath = BuildConfigPath(), + macConfigPath = BuildConfigPath(), + linuxConfigPath = BuildConfigPath() + }) + { } + + private static string BuildConfigPath() + { + string configDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + return Path.Combine(configDir, ".config", "opencode", "opencode.json"); + } + + public override string GetConfigPath() => CurrentOsPath(); + + public override McpStatus CheckStatus(bool attemptAutoRewrite = true) + { + try + { + string path = GetConfigPath(); + if (!File.Exists(path)) + { + client.SetStatus(McpStatus.NotConfigured); + return client.status; + } + + string json = File.ReadAllText(path); + var config = JsonConvert.DeserializeObject(json); + var mcpSection = config?["mcp"] as JObject; + + if (mcpSection == null || mcpSection[ServerName] == null) + { + client.SetStatus(McpStatus.NotConfigured); + return client.status; + } + + var unityMcp = mcpSection[ServerName] as JObject; + if (unityMcp == null) + { + client.SetStatus(McpStatus.NotConfigured); + return client.status; + } + + string configuredUrl = unityMcp["url"]?.ToString(); + string expectedUrl = HttpEndpointUtility.GetMcpRpcUrl(); + + if (UrlsEqual(configuredUrl, expectedUrl)) + { + client.SetStatus(McpStatus.Configured); + } + else if (attemptAutoRewrite) + { + Configure(); + } + else + { + client.SetStatus(McpStatus.IncorrectPath); + } + } + catch (Exception ex) + { + client.SetStatus(McpStatus.Error, ex.Message); + } + + return client.status; + } + + public override void Configure() + { + try + { + string path = GetConfigPath(); + string dir = Path.GetDirectoryName(path); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + JObject config; + if (File.Exists(path)) + { + string existingJson = File.ReadAllText(path); + config = JsonConvert.DeserializeObject(existingJson) ?? new JObject(); + } + else + { + config = new JObject + { + ["$schema"] = "https://opencode.ai/config.json" + }; + } + + if (config["mcp"] == null) + { + config["mcp"] = new JObject(); + } + + var mcpSection = config["mcp"] as JObject; + string httpUrl = HttpEndpointUtility.GetMcpRpcUrl(); + + mcpSection[ServerName] = new JObject + { + ["type"] = "remote", + ["url"] = httpUrl, + ["enabled"] = true + }; + + string output = JsonConvert.SerializeObject(config, Formatting.Indented); + File.WriteAllText(path, output); + + client.SetStatus(McpStatus.Configured); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to configure OpenCode: {ex.Message}"); + } + } + + public override string GetManualSnippet() + { + string httpUrl = HttpEndpointUtility.GetMcpRpcUrl(); + var snippet = new JObject + { + ["mcp"] = new JObject + { + [ServerName] = new JObject + { + ["type"] = "remote", + ["url"] = httpUrl, + ["enabled"] = true + } + } + }; + return JsonConvert.SerializeObject(snippet, Formatting.Indented); + } + + public override IList GetInstallationSteps() => new List + { + "Install OpenCode (https://opencode.ai)", + "Click Configure to add Unity MCP to ~/.config/opencode/opencode.json", + "Restart OpenCode", + "The Unity MCP server should be detected automatically" + }; + } +} diff --git a/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs.meta b/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs.meta new file mode 100644 index 000000000..8094fb1d0 --- /dev/null +++ b/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 489f99ffb7e6743e88e3203552c8b37b \ No newline at end of file From 1ab182d4ab600511ee745087e8b55a20e8c2581d Mon Sep 17 00:00:00 2001 From: Akshay Khandizod Date: Tue, 30 Dec 2025 12:06:21 +0530 Subject: [PATCH 2/2] feat: Add OpenCode (opencode.ai) client configurator - Support for OpenCode's custom config format at ~/.config/opencode/opencode.json - Uses 'mcp' object with 'type': 'remote' for HTTP transport - Auto-configures and preserves existing MCP servers in config --- .../Editor/Clients/Configurators/OpenCodeConfigurator.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs b/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs index d4df5b05a..3107fb5b1 100644 --- a/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs +++ b/MCPForUnity/Editor/Clients/Configurators/OpenCodeConfigurator.cs @@ -110,12 +110,13 @@ public override void Configure() }; } - if (config["mcp"] == null) + var mcpSection = config["mcp"] as JObject; + if (mcpSection == null) { - config["mcp"] = new JObject(); + mcpSection = new JObject(); + config["mcp"] = mcpSection; } - var mcpSection = config["mcp"] as JObject; string httpUrl = HttpEndpointUtility.GetMcpRpcUrl(); mcpSection[ServerName] = new JObject @@ -132,7 +133,7 @@ public override void Configure() } catch (Exception ex) { - throw new InvalidOperationException($"Failed to configure OpenCode: {ex.Message}"); + throw new InvalidOperationException("Failed to configure OpenCode.", ex); } }