Skip to main content
Glama
TelemetryHelper.cs7.68 kB
using System; using System.Collections.Generic; using System.Threading; using MCPForUnity.Editor.Constants; using MCPForUnity.Editor.Services.Transport.Transports; using UnityEngine; namespace MCPForUnity.Editor.Helpers { /// <summary> /// Unity Bridge telemetry helper for collecting usage analytics /// Following privacy-first approach with easy opt-out mechanisms /// </summary> public static class TelemetryHelper { private const string TELEMETRY_DISABLED_KEY = EditorPrefKeys.TelemetryDisabled; private const string CUSTOMER_UUID_KEY = EditorPrefKeys.CustomerUuid; private static Action<Dictionary<string, object>> s_sender; /// <summary> /// Check if telemetry is enabled (can be disabled via Environment Variable or EditorPrefs) /// </summary> public static bool IsEnabled { get { // Check environment variables first var envDisable = Environment.GetEnvironmentVariable("DISABLE_TELEMETRY"); if (!string.IsNullOrEmpty(envDisable) && (envDisable.ToLower() == "true" || envDisable == "1")) { return false; } var unityMcpDisable = Environment.GetEnvironmentVariable("UNITY_MCP_DISABLE_TELEMETRY"); if (!string.IsNullOrEmpty(unityMcpDisable) && (unityMcpDisable.ToLower() == "true" || unityMcpDisable == "1")) { return false; } // Honor protocol-wide opt-out as well var mcpDisable = Environment.GetEnvironmentVariable("MCP_DISABLE_TELEMETRY"); if (!string.IsNullOrEmpty(mcpDisable) && (mcpDisable.Equals("true", StringComparison.OrdinalIgnoreCase) || mcpDisable == "1")) { return false; } // Check EditorPrefs return !UnityEditor.EditorPrefs.GetBool(TELEMETRY_DISABLED_KEY, false); } } /// <summary> /// Get or generate customer UUID for anonymous tracking /// </summary> public static string GetCustomerUUID() { var uuid = UnityEditor.EditorPrefs.GetString(CUSTOMER_UUID_KEY, ""); if (string.IsNullOrEmpty(uuid)) { uuid = System.Guid.NewGuid().ToString(); UnityEditor.EditorPrefs.SetString(CUSTOMER_UUID_KEY, uuid); } return uuid; } /// <summary> /// Disable telemetry (stored in EditorPrefs) /// </summary> public static void DisableTelemetry() { UnityEditor.EditorPrefs.SetBool(TELEMETRY_DISABLED_KEY, true); } /// <summary> /// Enable telemetry (stored in EditorPrefs) /// </summary> public static void EnableTelemetry() { UnityEditor.EditorPrefs.SetBool(TELEMETRY_DISABLED_KEY, false); } /// <summary> /// Send telemetry data to MCP server for processing /// This is a lightweight bridge - the actual telemetry logic is in the MCP server /// </summary> public static void RecordEvent(string eventType, Dictionary<string, object> data = null) { if (!IsEnabled) return; try { var telemetryData = new Dictionary<string, object> { ["event_type"] = eventType, ["timestamp"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), ["customer_uuid"] = GetCustomerUUID(), ["unity_version"] = Application.unityVersion, ["platform"] = Application.platform.ToString(), ["source"] = "unity_bridge" }; if (data != null) { telemetryData["data"] = data; } // Send to MCP server via existing bridge communication // The MCP server will handle actual telemetry transmission SendTelemetryToMcpServer(telemetryData); } catch (Exception e) { // Never let telemetry errors interfere with functionality if (IsDebugEnabled()) { McpLog.Warn($"Telemetry error (non-blocking): {e.Message}"); } } } /// <summary> /// Allows the bridge to register a concrete sender for telemetry payloads. /// </summary> public static void RegisterTelemetrySender(Action<Dictionary<string, object>> sender) { Interlocked.Exchange(ref s_sender, sender); } public static void UnregisterTelemetrySender() { Interlocked.Exchange(ref s_sender, null); } /// <summary> /// Record bridge startup event /// </summary> public static void RecordBridgeStartup() { RecordEvent("bridge_startup", new Dictionary<string, object> { ["bridge_version"] = AssetPathUtility.GetPackageVersion(), ["auto_connect"] = StdioBridgeHost.IsAutoConnectMode() }); } /// <summary> /// Record bridge connection event /// </summary> public static void RecordBridgeConnection(bool success, string error = null) { var data = new Dictionary<string, object> { ["success"] = success }; if (!string.IsNullOrEmpty(error)) { data["error"] = error.Substring(0, Math.Min(200, error.Length)); } RecordEvent("bridge_connection", data); } /// <summary> /// Record tool execution from Unity side /// </summary> public static void RecordToolExecution(string toolName, bool success, float durationMs, string error = null) { var data = new Dictionary<string, object> { ["tool_name"] = toolName, ["success"] = success, ["duration_ms"] = Math.Round(durationMs, 2) }; if (!string.IsNullOrEmpty(error)) { data["error"] = error.Substring(0, Math.Min(200, error.Length)); } RecordEvent("tool_execution_unity", data); } private static void SendTelemetryToMcpServer(Dictionary<string, object> telemetryData) { var sender = Volatile.Read(ref s_sender); if (sender != null) { try { sender(telemetryData); return; } catch (Exception e) { if (IsDebugEnabled()) { McpLog.Warn($"Telemetry sender error (non-blocking): {e.Message}"); } } } // Fallback: log when debug is enabled if (IsDebugEnabled()) { McpLog.Info($"Telemetry: {telemetryData["event_type"]}"); } } private static bool IsDebugEnabled() { try { return UnityEditor.EditorPrefs.GetBool(EditorPrefKeys.DebugLogs, false); } catch { return false; } } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/CoplayDev/unity-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server