Swapper - XMR to USDC swaps

XMR <-> USDC (Arbitrum One) swaps for AI agents. Swapper never holds funds. 8 tools, 3 prompts.

Should I use this

Quality & Safety

A
Description quality
100%
Schema completeness
88%
Naming quality
98%
Poisoning risk
100%
Permission match
100%
Protocol compliance
100%

Based on automated analysis of tool definitions and protocol compliance.

Context Cost

~3,298Tokens (tool definitions)
~3.6 KBTypical response size
Significant attention impact (2.58% of 128k context)

This is the approximate number of tokens consumed each time the server's tools are loaded into a model's context. Higher counts reduce the attention available for other tasks.

Install

One-Click Install

Add this to your `claude_desktop_config.json` file:

{
  "mcpServers": {
    "swapper": {
      "url": "https://xmrtousdc.xyz/mcp"
    }
  }
}

Remote endpoints

https://xmrtousdc.xyz/mcpstreamable-http

What it can do

Tool inventory

Tools (8)

🟢 Read-only🟡 Write🔴 Delete⚪ Unknown
🟢get_quote(amount, receiveAmount, direction, to, basis, ...)

Step 1. Quote Monero (XMR) <-> native USDC on Arbitrum One; no KYC, account or API key. Read-only; quotes are estimates. Buy XMR ("get me 1 XMR"): {direction:"u2x", receiveAmount:"1"}. Sell XMR: {direction:"x2u", amount:"0.5"}. direction plus exactly one of amount (what the user sends) or receiveAmount (what they want). Pass to if known; re-quote with {requoteOf}. Next: create_swap with next.arguments; no confirmation step is required.

Input Schema

{
  "type": "object",
  "properties": {
    "amount": {
      "type": "string",
      "pattern": "^(?:[0-9]+(?:\\.[0-9]+)?|\\.[0-9]+)$",
      "description": "What the user SENDS (x2u: XMR, u2x: USDC). Digits and one dot; no commas or units."
    },
    "receiveAmount": {
      "type": "string",
      "pattern": "^(?:[0-9]+(?:\\.[0-9]+)?|\\.[0-9]+)$",
      "description": "What the user wants to RECEIVE (u2x: XMR, x2u: USDC); needs direction.",
      "examples": [
        "1"
      ]
    },
    "direction": {
      "type": "string",
      "enum": [
        "x2u",
        "u2x"
      ],
      "description": "Required. u2x: send USDC, get XMR. x2u: send XMR, get USDC."
    },
    "to": {
      "type": "string",
      "description": "Destination from the user: x2u an Arbitrum One 0x address, u2x a Monero address. Never guess."
    },
    "basis": {
      "type": "string",
      "enum": [
        "estimate",
        "minimum"
      ],
      "description": "receiveAmount target met by the estimate (default) or the provider minimum (only if one is published)."
    },
    "requoteOf": {
      "type": "string",
      "pattern": "^q_[a-f0-9]{32}$",
      "description": "Re-quote this quoteId's stored parameters; pass it alone."
    },
    "chain": {
      "type": "string",
      "description": "Default arbitrum (42161)."
    },
    "detail": {
      "type": "string",
      "enum": [
        "lean",
        "full"
      ]
    }
  },
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "operation": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "quoteId": {
      "type": [
        "string",
        "null"
      ]
    },
    "direction": {
      "type": "string"
    },
    "acceptUntil": {
      "type": [
        "string",
        "null"
      ]
    },
    "expiresInSec": {
      "type": [
        "integer",
        "null"
      ]
    },
    "acceptWindowSec": {
      "type": [
        "integer",
        "null"
      ]
    },
    "guaranteed": {
      "type": "boolean"
    },
    "quote": {
      "type": [
        "object",
        "null"
      ]
    },
    "destination": {
      "type": [
        "string",
        "null"
      ]
    },
    "belowProviderMinimum": {
      "type": [
        "boolean",
        "null"
      ]
    },
    "minimumSwapUsd": {
      "type": [
        "number",
        "null"
      ]
    },
    "target": {
      "type": [
        "object",
        "null"
      ]
    },
    "delta": {
      "type": [
        "object",
        "null"
      ]
    },
    "prerequisites": {
      "type": [
        "object",
        "null"
      ]
    },
    "userMessage": {
      "type": "string"
    },
    "next": {
      "type": "object",
      "required": [
        "tool",
        "arguments"
      ],
      "properties": {
        "tool": {
          "type": [
            "string",
            "null"
          ]
        },
        "arguments": {
          "type": "object"
        }
      }
    },
    "error": {
      "type": "object"
    },
    "data": {
      "type": "object"
    }
  },
  "required": [
    "ok",
    "operation",
    "next"
  ]
}
🟡create_swap(quoteId, to, idempotencyKey, quoteIntentDigest, detail)

Step 2. Creates the order from get_quote's next.arguments (quoteId, plus to if the quote had none): one order per quote; moves no funds. Retry with the same arguments: the default idempotencyKey returns the same order. Fund once, only while payable is true: before signing deposit.payment, get_swap_status {orderId, payment} must return paymentCheck.matches. Next: get_swap_status.

Input Schema

{
  "type": "object",
  "properties": {
    "quoteId": {
      "type": "string",
      "pattern": "^q_[a-f0-9]{32}$",
      "description": "From get_quote's next.arguments."
    },
    "to": {
      "type": "string",
      "description": "Destination, only if the quote had none (next.missing). Never guess."
    },
    "idempotencyKey": {
      "type": "string",
      "pattern": "^[A-Za-z0-9:_-]{8,128}$",
      "description": "Optional; default q_<quoteId>. Reuse on every retry. A custom key: a fresh random UUID per intent.",
      "examples": [
        "75d26b1d-11b6-4283-9a06-8a612dcc4ffd"
      ]
    },
    "quoteIntentDigest": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "From next.arguments: create fails if the quote's terms differ."
    },
    "detail": {
      "type": "string",
      "enum": [
        "lean",
        "full"
      ]
    }
  },
  "required": [
    "quoteId"
  ],
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "operation": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "orderId": {
      "type": [
        "string",
        "null"
      ]
    },
    "replayed": {
      "type": "boolean"
    },
    "recovered": {
      "type": "boolean"
    },
    "payable": {
      "type": "boolean"
    },
    "payableReason": {
      "type": [
        "string",
        "null"
      ]
    },
    "deposit": {
      "type": [
        "object",
        "null"
      ]
    },
    "expectedReceive": {
      "type": [
        "object",
        "null"
      ]
    },
    "destination": {
      "type": [
        "string",
        "null"
      ]
    },
    "reconfirmRequired": {
      "type": "boolean"
    },
    "trackUrl": {
      "type": [
        "string",
        "null"
      ]
    },
    "userMessage": {
      "type": "string"
    },
    "next": {
      "type": "object",
      "required": [
        "tool",
        "arguments"
      ],
      "properties": {
        "tool": {
          "type": [
            "string",
            "null"
          ]
        },
        "arguments": {
          "type": "object"
        }
      }
    },
    "error": {
      "type": "object"
    },
    "data": {
      "type": "object"
    }
  },
  "required": [
    "ok",
    "operation",
    "next"
  ]
}
🟢get_swap_status(orderId, waitSec, sinceState, verify, payment, ...)

Step 3. waiting_for_funding -> deposit_detected -> swap_processing -> completed, or refunding / terminal_failure (outcome refunded, failed, expired). Long-poll with waitSec 25 and sinceState = the last state; repeat next until terminal. deposit.payment may be used only while payable is true. Call it before funding or showing a deposit again. payment: what your wallet will sign; sign only if paymentCheck.matches.

Input Schema

{
  "type": "object",
  "properties": {
    "orderId": {
      "type": "string",
      "description": "From create_swap or list_swaps."
    },
    "waitSec": {
      "type": "integer",
      "minimum": 0,
      "description": "Long-poll seconds for a change from sinceState (max 25; more is capped)."
    },
    "sinceState": {
      "type": "string",
      "enum": [
        "waiting_for_funding",
        "deposit_detected",
        "swap_processing",
        "refunding",
        "completed",
        "terminal_failure",
        "unknown"
      ],
      "description": "State from your previous call."
    },
    "verify": {
      "type": "string",
      "description": "Destination address (optional extra check)."
    },
    "payment": {
      "type": "string",
      "maxLength": 20000,
      "description": "Raw tx hex, tx JSON, monero:/ethereum: URI or calldata."
    },
    "detail": {
      "type": "string",
      "enum": [
        "lean",
        "full"
      ]
    }
  },
  "required": [
    "orderId"
  ],
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "operation": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "orderId": {
      "type": [
        "string",
        "null"
      ]
    },
    "status": {
      "type": [
        "string",
        "null"
      ]
    },
    "terminal": {
      "type": "boolean"
    },
    "outcome": {
      "type": [
        "string",
        "null"
      ]
    },
    "mayChange": {
      "type": "boolean"
    },
    "payable": {
      "type": "boolean"
    },
    "payableReason": {
      "type": [
        "string",
        "null"
      ]
    },
    "deposit": {
      "type": [
        "object",
        "null"
      ]
    },
    "destination": {
      "type": [
        "string",
        "null"
      ]
    },
    "received": {
      "type": [
        "object",
        "null"
      ]
    },
    "receivedBelowMinimum": {
      "type": "boolean"
    },
    "evidence": {
      "type": [
        "object",
        "null"
      ]
    },
    "attention": {
      "type": "array"
    },
    "paymentCheck": {
      "type": [
        "object",
        "null"
      ]
    },
    "waited": {
      "type": [
        "object",
        "null"
      ]
    },
    "trackUrl": {
      "type": [
        "string",
        "null"
      ]
    },
    "userMessage": {
      "type": "string"
    },
    "next": {
      "type": "object",
      "required": [
        "tool",
        "arguments"
      ],
      "properties": {
        "tool": {
          "type": [
            "string",
            "null"
          ]
        },
        "arguments": {
          "type": "object"
        }
      }
    },
    "error": {
      "type": "object"
    },
    "data": {
      "type": "object"
    }
  },
  "required": [
    "ok",
    "operation",
    "next"
  ]
}
🟢get_funding_options(direction, from, amount, detail)

Offline. How to get what the user sends: native USDC (not USDC.e) plus a little ETH for gas on Arbitrum One (u2x, default), or XMR in their own wallet (x2u). Pass from {chain, asset} (and amount) for one ordered checklist: quote, fund, re-quote (requoteOf).

Input Schema

{
  "type": "object",
  "properties": {
    "direction": {
      "type": "string",
      "enum": [
        "u2x",
        "x2u"
      ],
      "description": "u2x (default): the user sends USDC. x2u: XMR."
    },
    "from": {
      "type": "object",
      "additionalProperties": false,
      "description": "Where the funds are now.",
      "properties": {
        "chain": {
          "type": "string",
          "description": "arbitrum, ethereum, another chain, or exchange (or its name)."
        },
        "asset": {
          "type": "string",
          "description": "E.g. USDC, USDC.e, ETH, XMR."
        }
      }
    },
    "amount": {
      "type": "string",
      "pattern": "^(?:[0-9]+(?:\\.[0-9]+)?|\\.[0-9]+)$",
      "description": "Send amount from a quote, if known."
    },
    "detail": {
      "type": "string",
      "enum": [
        "lean",
        "full"
      ]
    }
  },
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "operation": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "direction": {
      "type": "string"
    },
    "matched": {
      "type": [
        "boolean",
        "null"
      ]
    },
    "route": {
      "type": [
        "object",
        "null"
      ]
    },
    "checklist": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "userMessage": {
      "type": "string"
    },
    "next": {
      "type": "object",
      "required": [
        "tool",
        "arguments"
      ],
      "properties": {
        "tool": {
          "type": [
            "string",
            "null"
          ]
        },
        "arguments": {
          "type": "object"
        }
      }
    },
    "error": {
      "type": "object"
    },
    "data": {
      "type": "object"
    }
  },
  "required": [
    "ok",
    "operation",
    "next"
  ]
}
🟢list_swaps(idempotencyKey, sessionId, detail)

Find orders when the orderId is lost (new chat, crash, unknown create outcome): pass the idempotencyKey create_swap used (default q_<quoteId>), or a sessionId. Read-only; addresses redacted; states can lag. Next: get_swap_status (live state, payable).

Input Schema

{
  "type": "object",
  "properties": {
    "idempotencyKey": {
      "type": "string",
      "pattern": "^[A-Za-z0-9:_-]{8,128}$",
      "description": "The key create_swap used (default q_<quoteId>)."
    },
    "sessionId": {
      "type": "string",
      "description": "Session UUID, instead of idempotencyKey."
    },
    "detail": {
      "type": "string",
      "enum": [
        "lean",
        "full"
      ]
    }
  },
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "operation": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "lookup": {
      "type": "string"
    },
    "payable": {
      "type": "boolean"
    },
    "payableReason": {
      "type": [
        "string",
        "null"
      ]
    },
    "orders": {
      "type": "array"
    },
    "note": {
      "type": [
        "string",
        "null"
      ]
    },
    "userMessage": {
      "type": "string"
    },
    "next": {
      "type": "object",
      "required": [
        "tool",
        "arguments"
      ],
      "properties": {
        "tool": {
          "type": [
            "string",
            "null"
          ]
        },
        "arguments": {
          "type": "object"
        }
      }
    },
    "error": {
      "type": "object"
    },
    "data": {
      "type": "object"
    }
  },
  "required": [
    "ok",
    "operation",
    "next"
  ]
}
🟢plan_swap(direction, amount, receiveAmount, to, detail)

Optional, offline. Checks a partial request (direction, amount or receiveAmount, to) and returns the next question for the user. Skip it when you have what get_quote needs.

Input Schema

{
  "type": "object",
  "properties": {
    "direction": {
      "type": "string",
      "description": "u2x or x2u, as the user said it."
    },
    "amount": {
      "type": "string",
      "description": "What the user sends."
    },
    "receiveAmount": {
      "type": "string",
      "description": "What the user wants to receive."
    },
    "to": {
      "type": "string",
      "description": "Destination from the user: x2u an Arbitrum One 0x address, u2x a Monero address. Never guess."
    },
    "detail": {
      "type": "string",
      "enum": [
        "lean",
        "full"
      ]
    }
  },
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "operation": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "missingInputs": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "validationErrors": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "question": {
      "type": [
        "object",
        "null"
      ]
    },
    "userMessage": {
      "type": "string"
    },
    "next": {
      "type": "object",
      "required": [
        "tool",
        "arguments"
      ],
      "properties": {
        "tool": {
          "type": [
            "string",
            "null"
          ]
        },
        "arguments": {
          "type": "object"
        }
      }
    },
    "error": {
      "type": "object"
    },
    "data": {
      "type": "object"
    }
  },
  "required": [
    "ok",
    "operation",
    "next"
  ]
}
🟢get_capabilities(detail)

Optional. Chains, limits (provider minimum, USD), quote window, idempotency, validation rules, polling and the flow.

Input Schema

{
  "type": "object",
  "properties": {
    "detail": {
      "type": "string",
      "enum": [
        "lean",
        "full"
      ]
    }
  },
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "operation": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "contractVersion": {
      "type": "string"
    },
    "userMessage": {
      "type": "string"
    },
    "next": {
      "type": "object",
      "required": [
        "tool",
        "arguments"
      ],
      "properties": {
        "tool": {
          "type": [
            "string",
            "null"
          ]
        },
        "arguments": {
          "type": "object"
        }
      }
    },
    "error": {
      "type": "object"
    },
    "data": {
      "type": "object"
    }
  },
  "required": [
    "ok",
    "operation",
    "next"
  ]
}
🟡create_support_ticket(message, orderId, category, walletAddress, sessionId, ...)

For a failed or stuck swap: orderId and what happened; never seed phrases or private keys. Returns ticketId; no tool reads replies (follow up at [email protected]).

Input Schema

{
  "type": "object",
  "properties": {
    "message": {
      "type": "string",
      "minLength": 3,
      "maxLength": 2000,
      "description": "What happened: what was sent, when, what the user sees."
    },
    "orderId": {
      "type": "string",
      "description": "ord_... of the affected order."
    },
    "category": {
      "type": "string",
      "description": "Short topic, e.g. \"stuck\", \"refund\". Default \"agent\"."
    },
    "walletAddress": {
      "type": "string",
      "description": "Public address involved (sending wallet or destination)."
    },
    "sessionId": {
      "type": "string",
      "description": "Session UUID, if known."
    },
    "detail": {
      "type": "string",
      "enum": [
        "lean",
        "full"
      ]
    }
  },
  "required": [
    "message"
  ],
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "operation": {
      "type": "string"
    },
    "state": {
      "type": "string"
    },
    "ticketId": {
      "type": [
        "string",
        "null"
      ]
    },
    "userMessage": {
      "type": "string"
    },
    "next": {
      "type": "object",
      "required": [
        "tool",
        "arguments"
      ],
      "properties": {
        "tool": {
          "type": [
            "string",
            "null"
          ]
        },
        "arguments": {
          "type": "object"
        }
      }
    },
    "error": {
      "type": "object"
    },
    "data": {
      "type": "object"
    }
  },
  "required": [
    "ok",
    "operation",
    "next"
  ]
}

Community

Rate this Server

Evidence

Recent observations

verifiedversion not recorded8 tools