///|
/// Protocol and SDK errors. Application-level tool failures remain ordinary
/// `CallToolResult` values and are not represented here.
pub(all) suberror McpError {
ParseError(String)
InvalidRequest(String)
MethodNotFound(String)
InvalidParams(String)
InvalidParamsWithData(String, Json)
InternalError(String)
HeaderMismatch(String)
MissingRequiredClientCapability(String, ClientCapabilities)
UnsupportedProtocolVersion(requested~ : String, supported~ : Array[String])
RemoteError(code~ : Int, message~ : String, data~ : Json?)
} derive(Debug, Eq)
///|
pub fn McpError::code(self : McpError) -> Int {
match self {
ParseError(_) => @jsonrpc.PARSE_ERROR
InvalidRequest(_) => @jsonrpc.INVALID_REQUEST
MethodNotFound(_) => @jsonrpc.METHOD_NOT_FOUND
InvalidParams(_) => @jsonrpc.INVALID_PARAMS
InvalidParamsWithData(_, _) => @jsonrpc.INVALID_PARAMS
InternalError(_) => @jsonrpc.INTERNAL_ERROR
HeaderMismatch(_) => -32020
MissingRequiredClientCapability(_, _) => -32021
UnsupportedProtocolVersion(_) => -32022
RemoteError(code~, ..) => code
}
}
///|
pub fn McpError::message(self : McpError) -> String {
match self {
ParseError(message) => message
InvalidRequest(message) => message
MethodNotFound(method_name) => "Method not found: \{method_name}"
InvalidParams(message) => message
InvalidParamsWithData(message, _) => message
InternalError(message) => message
HeaderMismatch(message) => message
MissingRequiredClientCapability(message, _) => message
UnsupportedProtocolVersion(_) => "Unsupported protocol version"
RemoteError(message~, ..) => message
}
}
///|
pub fn McpError::to_string(self : McpError) -> String {
self.message()
}
///|
pub impl Show for McpError with fn output(self : McpError, logger : &Logger) -> Unit {
logger.write_string(self.message())
}
///|
pub fn McpError::data(self : McpError) -> Json? {
match self {
MissingRequiredClientCapability(_, required) =>
Some(Json::object({ "requiredCapabilities": ToJson::to_json(required) }))
UnsupportedProtocolVersion(requested~, supported~) =>
Some(
Json::object({
"supported": supported.to_json(),
"requested": Json::string(requested),
}),
)
InvalidParamsWithData(_, data) => Some(data)
RemoteError(data~, ..) => data
_ => None
}
}
///|
pub fn McpError::to_error_object(self : McpError) -> @jsonrpc.ErrorObject {
@jsonrpc.ErrorObject(
code=self.code(),
message=self.message(),
data?=self.data(),
)
}
///|
pub impl ToJson for McpError with fn to_json(self : McpError) -> Json {
self.to_error_object().to_json()
}
///|
priv struct RequiredCapabilitiesPayload {
required_capabilities : ClientCapabilities
}
///|
impl @json.FromJson for RequiredCapabilitiesPayload with fn from_json(
value : Json,
path : @json.JsonPath,
) -> RequiredCapabilitiesPayload raise {
guard value is Object(fields) else {
raise JsonDecodeError((path, "expected object"))
}
guard fields.get("requiredCapabilities") is Some(caps_json) else {
raise JsonDecodeError(
(path.add_key("requiredCapabilities"), "requiredCapabilities is required"),
)
}
let required_capabilities = @json.from_json(
caps_json,
path=path.add_key("requiredCapabilities"),
)
{ required_capabilities, }
}
///|
priv struct UnsupportedVersionPayload {
requested : String
supported : Array[String]
}
///|
impl @json.FromJson for UnsupportedVersionPayload with fn from_json(
value : Json,
path : @json.JsonPath,
) -> UnsupportedVersionPayload raise {
guard value is Object(fields) else {
raise JsonDecodeError((path, "expected object"))
}
let requested = match fields.get("requested") {
Some(String(s)) => s
Some(_) =>
raise JsonDecodeError((path.add_key("requested"), "expected string"))
None =>
raise JsonDecodeError(
(path.add_key("requested"), "requested is required"),
)
}
let supported : Array[String] = match fields.get("supported") {
Some(Array(arr)) => {
let res : Array[String] = []
for i, item in arr {
match item {
String(s) => res.push(s)
_ =>
raise JsonDecodeError(
(path.add_key("supported").add_index(i), "expected string"),
)
}
}
res
}
Some(_) =>
raise JsonDecodeError((path.add_key("supported"), "expected array"))
None =>
raise JsonDecodeError(
(path.add_key("supported"), "supported is required"),
)
}
{ requested, supported }
}
///|
/// Decode an MCP error object from a JSON-RPC error structure.
pub fn McpError::from_error_object(
error : @jsonrpc.ErrorObject,
) -> McpError raise McpError {
McpError::from_rpc_parts(error.code, error.message, data?=error.data)
}
///|
/// Decode an MCP error object after a JSON-RPC implementation has separated
/// its `code`, `message`, and optional `data` members.
pub fn McpError::from_rpc_parts(
code : Int,
message : String,
data? : Json,
) -> McpError raise McpError {
match code {
@jsonrpc.PARSE_ERROR => ParseError(message)
@jsonrpc.INVALID_REQUEST => InvalidRequest(message)
@jsonrpc.METHOD_NOT_FOUND => MethodNotFound(message)
@jsonrpc.INVALID_PARAMS =>
match data {
Some(data) => InvalidParamsWithData(message, data)
None => InvalidParams(message)
}
@jsonrpc.INTERNAL_ERROR => InternalError(message)
-32020 => HeaderMismatch(message)
-32021 => {
guard data is Some(data_val) else {
raise InvalidRequest("-32021 error data must be an object")
}
let payload : RequiredCapabilitiesPayload = @json.from_json(data_val) catch {
_ => raise InvalidRequest("-32021 error data invalid")
}
MissingRequiredClientCapability(message, payload.required_capabilities)
}
-32022 => {
guard data is Some(data_val) else {
raise InvalidRequest("-32022 error data must be an object")
}
let payload : UnsupportedVersionPayload = @json.from_json(data_val) catch {
_ => raise InvalidRequest("-32022 error data invalid")
}
UnsupportedProtocolVersion(
requested=payload.requested,
supported=payload.supported,
)
}
_ => RemoteError(code~, message~, data~)
}
}