///|
/// Transport-level errors
/// These occur at the communication layer (network, IO, etc.)
pub(all) suberror TransportError {
/// Connection has been closed (clean shutdown)
ConnectionClosed
/// Failed to read from transport
ReadError(String)
/// Failed to write to transport
WriteError(String)
/// Operation timed out
Timeout
/// Invalid transport state (e.g., trying to send after close)
InvalidState(String)
/// HTTP 401 Unauthorized — includes WWW-Authenticate header info
Unauthorized(String)
/// HTTP 403 Forbidden — insufficient scope or permissions
Forbidden(String)
/// HTTP error status carrying the response body, so callers (e.g. the
/// era probe) can inspect it for a recognized modern JSON-RPC error
/// (UnsupportedProtocolVersion / MissingRequiredClientCapability /
/// HeaderMismatch) before deciding on a legacy fallback.
HttpError(Int32, String)
} derive(Debug, Eq)
///|
pub impl Show for TransportError with fn to_string(self) -> String {
match self {
ConnectionClosed => "Connection closed"
ReadError(msg) => "Read error: " + msg
WriteError(msg) => "Write error: " + msg
Timeout => "Timeout"
InvalidState(msg) => "Invalid state: " + msg
Unauthorized(msg) => "Unauthorized: " + msg
Forbidden(msg) => "Forbidden: " + msg
HttpError(status, body) =>
"HTTP " + status.to_string() + " error, body: " + body
}
}
///|
/// MCP protocol-level errors
/// These follow the JSON-RPC 2.0 error code conventions.
///
/// Error code allocation (per 2026-07-28 spec):
/// - `-32700`, `-32600`..`-32603`: standard JSON-RPC 2.0
/// - `-32000`..`-32019`: legacy/implementation-defined (no new allocations)
/// - `-32020`..`-32099`: reserved for the MCP specification
pub(all) suberror MCPError {
/// Failed to parse JSON-RPC request (-32700)
ParseError(String)
/// Invalid JSON-RPC request structure (-32600)
InvalidRequest(String)
/// Method not found (-32601)
MethodNotFound(String)
/// Invalid method parameters (-32602).
/// Also used for resource-not-found (was -32002 in earlier revisions).
InvalidParams(String)
/// Internal MCP server error (-32603)
InternalError(String)
/// Transport layer error (wrapped)
TransportError(TransportError)
/// Tool execution error (implementation-defined, -32000)
ToolError(String)
/// Local request cancellation. No cancellation response is sent to the peer.
Cancelled(String)
/// Peer error retaining its original code, data presence and open fields.
RemoteError(
String,
code~ : JsonInteger,
data~ : Json?,
extensions~ : Map[String, Json]
)
/// Streamable HTTP header/body mismatch (-32020, MCP-spec-defined).
/// `data` carries detail; server returns HTTP 400.
HeaderMismatch(String)
/// Request needs a client capability the client did not declare
/// (-32021, MCP-spec-defined). `required` lists the missing capabilities.
MissingRequiredClientCapability(String, required~ : ClientCapabilities)
/// Client requested an unsupported protocol version
/// (-32022, MCP-spec-defined). `supported` lists the versions the server
/// can speak; `requested` echoes what the client asked for.
UnsupportedProtocolVersion(
String,
supported~ : Array[String],
requested~ : String
)
} derive(Debug, Eq)
///|
/// Exact JSON-RPC error code. Use as_int() only when an Int32 projection is needed.
pub fn MCPError::to_error_code(self : MCPError) -> JsonInteger {
let code = match self {
ParseError(_) => -32700
InvalidRequest(_) => -32600
MethodNotFound(_) => -32601
InvalidParams(_) => -32602
InternalError(_) => -32603
TransportError(_) => -32603
ToolError(_) | Cancelled(_) => -32000
RemoteError(_, code~, ..) => return code
HeaderMismatch(_) => -32020
MissingRequiredClientCapability(_, ..) => -32021
UnsupportedProtocolVersion(_, ..) => -32022
}
JsonInteger::from_int(code)
}
///|
/// Get error message from MCPError
pub fn MCPError::message(self : MCPError) -> String {
match self {
ParseError(msg) => msg
InvalidRequest(msg) => msg
MethodNotFound(msg) => msg
InvalidParams(msg) => msg
InternalError(msg) => msg
TransportError(te) => "Transport error: " + Show::to_string(te)
ToolError(msg) | Cancelled(msg) | RemoteError(msg, ..) => msg
HeaderMismatch(msg) => msg
MissingRequiredClientCapability(msg, ..) => msg
UnsupportedProtocolVersion(msg, ..) => msg
}
}
///|
/// Serialize the structured `data` member of a JSON-RPC error response.
/// Returns `None` for errors that carry no `data` per the spec. The
/// `-3202x` MCP-spec-defined errors populate `data` with fields clients
/// use for recovery (e.g. supported versions to retry with).
pub fn MCPError::to_error_data(self : MCPError) -> Json? {
match self {
RemoteError(_, data~, ..) => data
UnsupportedProtocolVersion(_, supported~, requested~) => {
let versions = supported.map(fn(v) { Json::string(v) })
Some(
Json::object({
"supported": Json::array(versions),
"requested": requested,
}),
)
}
MissingRequiredClientCapability(_, required~) =>
Some(Json::object({ "requiredCapabilities": required.to_json() }))
_ => None
}
}
///|
/// Explicit trait method promotion for current MoonBit toolchains.
pub extend TransportError with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend TransportError with Eq::{not_equal, equal}
///|
pub extend TransportError with Show::{to_string, output}
///|
pub extend MCPError with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend MCPError with Eq::{not_equal, equal}