Skip to main content

r402_mcp/
error.rs

1//! Errors for MCP × x402 (Go `PaymentRequiredError` + client errors).
2
3use r402_core::wire::PaymentRequired;
4
5use crate::constants::MCP_PAYMENT_REQUIRED_CODE;
6
7/// Go `PaymentRequiredError` — JSON-RPC style code **402** with payment data.
8#[derive(Debug, Clone)]
9pub struct PaymentRequiredError {
10    /// Always [`MCP_PAYMENT_REQUIRED_CODE`] (402).
11    pub code: i32,
12    /// Human-readable message.
13    pub message: String,
14    /// Payment requirements payload.
15    pub payment_required: PaymentRequired,
16}
17
18impl PaymentRequiredError {
19    /// Builds a 402 payment-required error.
20    #[must_use]
21    pub fn new(message: impl Into<String>, payment_required: PaymentRequired) -> Self {
22        Self {
23            code: MCP_PAYMENT_REQUIRED_CODE,
24            message: message.into(),
25            payment_required,
26        }
27    }
28}
29
30impl std::fmt::Display for PaymentRequiredError {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.write_str(&self.message)
33    }
34}
35
36impl std::error::Error for PaymentRequiredError {}
37
38/// Client orchestration errors.
39#[derive(Debug, thiserror::Error)]
40pub enum McpClientError {
41    /// Underlying MCP transport / tool call failed.
42    #[error("mcp transport: {0}")]
43    Transport(String),
44    /// Building a payment payload failed.
45    #[error("payment creation: {0}")]
46    Payment(String),
47    /// Server still required payment after a signed retry.
48    #[error("payment still required after retry")]
49    StillRequired,
50    /// Auto-payment disabled or user denied — includes 402 data when available.
51    #[error(transparent)]
52    PaymentRequired(#[from] Box<PaymentRequiredError>),
53}
54
55impl From<PaymentRequiredError> for McpClientError {
56    fn from(value: PaymentRequiredError) -> Self {
57        Self::PaymentRequired(Box::new(value))
58    }
59}