import Foundation /// A model context protocol error. public enum Error: Sendable { // Standard JSON-RPC 2.0 errors (-32700 to -32603) case parseError(String?) // -32700 case invalidRequest(String?) // -32600 case methodNotFound(String?) // -32601 case invalidParams(String?) // -32602 case internalError(String?) // -32603 // Server errors (-32000 to -32099) case serverError(code: Int, message: String) // Transport specific errors case connectionClosed case transportError(Swift.Error) /// The JSON-RPC 2.0 error code public var code: Int { switch self { case .parseError: return -32700 case .invalidRequest: return -32600 case .methodNotFound: return -32601 case .invalidParams: return -32602 case .internalError: return -32603 case .serverError(let code, _): return code case .connectionClosed: return -32000 case .transportError: return -32001 } } } // MARK: LocalizedError extension Error: LocalizedError { public var errorDescription: String? { switch self { case .parseError(let detail): return "Parse error: Invalid JSON" + (detail.map { ": \($0)" } ?? "") case .invalidRequest(let detail): return "Invalid Request" + (detail.map { ": \($0)" } ?? "") case .methodNotFound(let detail): return "Method not found" + (detail.map { ": \($0)" } ?? "") case .invalidParams(let detail): return "Invalid params" + (detail.map { ": \($0)" } ?? "") case .internalError(let detail): return "Internal error" + (detail.map { ": \($0)" } ?? "") case .serverError(_, let message): return "Server error: \(message)" case .connectionClosed: return "Connection closed" case .transportError(let error): return "Transport error: \(error.localizedDescription)" } } public var failureReason: String? { switch self { case .parseError: return "The server received invalid JSON that could not be parsed" case .invalidRequest: return "The JSON sent is not a valid Request object" case .methodNotFound: return "The method does not exist or is not available" case .invalidParams: return "Invalid method parameter(s)" case .internalError: return "Internal JSON-RPC error" case .serverError: return "Server-defined error occurred" case .connectionClosed: return "The connection to the server was closed" case .transportError(let error): return (error as? LocalizedError)?.failureReason ?? error.localizedDescription } } public var recoverySuggestion: String? { switch self { case .parseError: return "Verify that the JSON being sent is valid and well-formed" case .invalidRequest: return "Ensure the request follows the JSON-RPC 2.0 specification format" case .methodNotFound: return "Check the method name and ensure it is supported by the server" case .invalidParams: return "Verify the parameters match the method's expected parameters" case .connectionClosed: return "Try reconnecting to the server" default: return nil } } } // MARK: CustomDebugStringConvertible extension Error: CustomDebugStringConvertible { public var debugDescription: String { switch self { case .transportError(let error): return "[\(code)] \(errorDescription ?? "") (Underlying error: \(String(reflecting: error)))" default: return "[\(code)] \(errorDescription ?? "")" } } } // MARK: Codable extension Error: Codable { private enum CodingKeys: String, CodingKey { case code, message, data } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(code, forKey: .code) try container.encode(errorDescription ?? "Unknown error", forKey: .message) // Encode additional data if available switch self { case .parseError(let detail), .invalidRequest(let detail), .methodNotFound(let detail), .invalidParams(let detail), .internalError(let detail): if let detail = detail { try container.encode(["detail": detail], forKey: .data) } case .serverError(_, _): // No additional data for server errors break case .connectionClosed: break case .transportError(let error): try container.encode( ["error": error.localizedDescription], forKey: .data ) } } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let code = try container.decode(Int.self, forKey: .code) let message = try container.decode(String.self, forKey: .message) let data = try container.decodeIfPresent([String: Value].self, forKey: .data) switch code { case -32700: self = .parseError(data?["detail"] as? String ?? message) case -32600: self = .invalidRequest(data?["detail"] as? String ?? message) case -32601: self = .methodNotFound(data?["detail"] as? String ?? message) case -32602: self = .invalidParams(data?["detail"] as? String ?? message) case -32603: self = .internalError(data?["detail"] as? String ?? message) case -32000: self = .connectionClosed case -32001: self = .transportError( NSError( domain: "org.jsonrpc.error", code: code, userInfo: [NSLocalizedDescriptionKey: message] ) ) default: self = .serverError(code: code, message: message) } } } // MARK: Equatable extension Error: Equatable { public static func == (lhs: Error, rhs: Error) -> Bool { lhs.code == rhs.code } } // MARK: Hashable extension Error: Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(code) switch self { case .parseError(let detail): hasher.combine(detail) case .invalidRequest(let detail): hasher.combine(detail) case .methodNotFound(let detail): hasher.combine(detail) case .invalidParams(let detail): hasher.combine(detail) case .internalError(let detail): hasher.combine(detail) case .serverError(_, let message): hasher.combine(message) case .connectionClosed: break case .transportError(let error): hasher.combine(error.localizedDescription) } } }