Skip to main content
Glama
JsonRpcRequest.cs10.4 kB
using System; using System.Collections; using System.Reflection; using UnityEngine; namespace LocalMcp.UnityServer { /// <summary> /// Represents a JSON-RPC 2.0 request. /// </summary> [Serializable] public class JsonRpcRequest { public string jsonrpc = "2.0"; public string method; public object @params; public object id; /// <summary> /// Parses a JSON string into a JsonRpcRequest object. /// </summary> public static JsonRpcRequest Parse(string json) { try { return JsonUtility.FromJson<JsonRpcRequest>(json); } catch (Exception e) { Debug.LogError($"[MCP] Failed to parse JSON-RPC request: {e.Message}"); return null; } } } /// <summary> /// Represents a JSON-RPC 2.0 response. /// </summary> [Serializable] public class JsonRpcResponse { public string jsonrpc = "2.0"; public object result; public JsonRpcError error; public object id; /// <summary> /// Creates a successful response. /// </summary> public static JsonRpcResponse Success(object result, object id) { return new JsonRpcResponse { result = result, id = id }; } /// <summary> /// Creates an error response. /// </summary> public static JsonRpcResponse Error(int code, string message, object id) { return new JsonRpcResponse { error = new JsonRpcError { code = code, message = message }, id = id }; } /// <summary> /// Converts the response to a JSON string. /// Note: We manually construct JSON because JsonUtility cannot serialize 'object' type fields or anonymous types. /// </summary> public string ToJson() { var sb = new System.Text.StringBuilder(); sb.Append("{"); sb.Append("\"jsonrpc\":\"2.0\""); if (result != null) { sb.Append(",\"result\":"); sb.Append(SerializeObject(result)); } if (error != null) { sb.Append(",\"error\":"); sb.Append(JsonUtility.ToJson(error)); } if (id != null) { sb.Append(",\"id\":"); if (id is string) { sb.Append("\"").Append(EscapeJsonString((string)id)).Append("\""); } else { sb.Append(id.ToString()); } } sb.Append("}"); return sb.ToString(); } /// <summary> /// Serializes an object to JSON, handling anonymous types, arrays, and nested objects. /// </summary> private static string SerializeObject(object obj) { if (obj == null) return "null"; var type = obj.GetType(); // Handle primitive types if (obj is string str) { return "\"" + EscapeJsonString(str) + "\""; } if (obj is bool b) { return b ? "true" : "false"; } if (obj is int || obj is long || obj is short || obj is byte) { return obj.ToString(); } if (obj is float f) { return f.ToString(System.Globalization.CultureInfo.InvariantCulture); } if (obj is double d) { return d.ToString(System.Globalization.CultureInfo.InvariantCulture); } // Handle arrays and collections if (obj is IEnumerable enumerable && !(obj is string)) { var sb = new System.Text.StringBuilder(); sb.Append("["); bool first = true; foreach (var item in enumerable) { if (!first) sb.Append(","); sb.Append(SerializeObject(item)); first = false; } sb.Append("]"); return sb.ToString(); } // Check if it's an anonymous type or class with public properties if (type.IsClass && type != typeof(string)) { // For Unity serializable types, try JsonUtility first if (type.GetCustomAttribute<SerializableAttribute>() != null && !type.Name.Contains("AnonymousType")) { try { string json = JsonUtility.ToJson(obj); if (!string.IsNullOrEmpty(json) && json != "{}") { return json; } } catch { } } // Use reflection for anonymous types or when JsonUtility fails var sb = new System.Text.StringBuilder(); sb.Append("{"); bool first = true; // Get all public properties (for anonymous types) var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in properties) { if (!prop.CanRead) continue; var value = prop.GetValue(obj); if (!first) sb.Append(","); sb.Append("\"").Append(prop.Name).Append("\":"); sb.Append(SerializeObject(value)); first = false; } // Also get public fields (for Unity-style serializable classes) var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (var field in fields) { var value = field.GetValue(obj); if (!first) sb.Append(","); sb.Append("\"").Append(field.Name).Append("\":"); sb.Append(SerializeObject(value)); first = false; } sb.Append("}"); return sb.ToString(); } // Fallback to ToString for unknown types return "\"" + EscapeJsonString(obj.ToString()) + "\""; } /// <summary> /// Escapes special characters in JSON strings. /// </summary> private static string EscapeJsonString(string str) { if (string.IsNullOrEmpty(str)) return str; return str .Replace("\\", "\\\\") .Replace("\"", "\\\"") .Replace("\n", "\\n") .Replace("\r", "\\r") .Replace("\t", "\\t"); } } /// <summary> /// Represents a JSON-RPC error object. /// </summary> [Serializable] public class JsonRpcError { public int code; public string message; public object data; } /// <summary> /// Standard JSON-RPC error codes. /// </summary> public static class JsonRpcErrorCodes { public const int ParseError = -32700; public const int InvalidRequest = -32600; public const int MethodNotFound = -32601; public const int InvalidParams = -32602; public const int InternalError = -32603; } /// <summary> /// Simple result class for success responses (JsonUtility requires explicit class, not anonymous type). /// </summary> [Serializable] public class SimpleResult { public string status; public string message; public SimpleResult() { } public SimpleResult(string status, string message) { this.status = status; this.message = message; } } /// <summary> /// Helper class for creating JSON-RPC responses. /// </summary> public static class JsonRpcResponseHelper { /// <summary> /// Creates a success response with a simple message. /// </summary> public static string SuccessMessage(string message, object id) { var response = JsonRpcResponse.Success(new SimpleResult("success", message), id); return response.ToJson(); } /// <summary> /// Creates a success response with raw JSON result. /// The resultJson should be a valid JSON object/value that will be used as-is in the result field. /// </summary> public static string SuccessJson(string resultJson, object id) { string idStr = id is string ? $"\"{id}\"" : id?.ToString() ?? "null"; return $"{{\"jsonrpc\":\"2.0\",\"id\":{idStr},\"result\":{resultJson}}}"; } /// <summary> /// Creates an error response with a message. /// </summary> public static string ErrorMessage(string message, object id) { var response = JsonRpcResponse.Error(JsonRpcErrorCodes.InternalError, message, id); return response.ToJson(); } /// <summary> /// Creates a method not found error response. /// </summary> public static string MethodNotFound(string method, object id) { var response = JsonRpcResponse.Error( JsonRpcErrorCodes.MethodNotFound, $"Method '{method}' not found", id ); return response.ToJson(); } /// <summary> /// Creates an invalid params error response. /// </summary> public static string InvalidParams(string message, object id) { var response = JsonRpcResponse.Error( JsonRpcErrorCodes.InvalidParams, message, id ); return response.ToJson(); } } }

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/dsgarage/UniMCP4CC'

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