Skip to main content
Glama
sand0vvv

mayan-mcp

by sand0vvv

mayan-mcp

An MCP server for Mayan Finance cross-chain swaps.

Ask an AI assistant what a swap will cost, then get the exact transaction to sign — between Solana and seven EVM chains, through Mayan's Swift and MCTP routes.

This server holds no private keys. It cannot sign and it cannot broadcast. build_swap returns an unsigned transaction; the caller signs it in their own wallet. There is no tool here that can move funds.

Every build is cross-checked against its quote before it is returned

Before any transaction is handed over, its own encoded arguments are read back and compared against the quote you were shown — the token, the amount, and the contract it forwards to. A payload that spends more than it quoted is refused rather than returned.

Those are real numbers, not a mockup: scripts/capture-demo.ts drives the built server over stdio against the live API and writes what came back to docs/captured-output.json, which is what the image above renders. Run it yourself and compare — a tool whose claim is "check what you are signing" should be checkable itself.


Custody model

Reads quotes, tokens, swap status

yes

Builds transactions

yes, unsigned

Holds private keys or seed phrases

no

Signs transactions

no

Broadcasts to a chain

no

The guarantee is structural rather than procedural: there is no key material in the process, so there is no code path from a compromised prompt to a moved balance. The signing step belongs to the wallet, which is also where the user sees the recipient address one last time.

Two consequences worth stating plainly:

  • Tool annotations are not the guarantee. This server sets readOnlyHint, but the MCP specification requires clients to treat annotations from an untrusted server as untrusted. The claim that matters is "no keys in the process", which you can check by reading src/.

  • A prompt injection cannot move funds through this server, but it can still propose a bad swap. Every response echoes the recipient address, the minimum received and the slippage next to the payload, so the terms are visible before anyone signs.

See SECURITY.md for the threat model, the invariant checks, and a reachability analysis of the current npm audit output.


Related MCP server: MCP Wallet Signer

Install

Requires Node.js 20 or newer.

npx @sand0vvv/mayan-mcp --help

Claude Desktop / Claude Code

{
  "mcpServers": {
    "mayan": {
      "command": "npx",
      "args": ["-y", "@sand0vvv/mayan-mcp"],
      "env": {
        "SOLANA_RPC_URL": "https://your-solana-rpc-endpoint"
      }
    }
  }
}

Cursor / other MCP clients

Same command, over stdio: npx -y @sand0vvv/mayan-mcp.

Streamable HTTP

npx @sand0vvv/mayan-mcp --http --port 8787

Stateless — a fresh server per request, no session state. It binds to 127.0.0.1 and validates the Host and Origin headers, so a page in your browser cannot reach it by pointing its own hostname at loopback. There is no authentication: exposing it on another interface (--host 0.0.0.0) is a deliberate act that belongs behind a proxy providing auth and rate limiting.


Configuration

Everything comes from the environment. Nothing is required to get a quote.

Variable

Required

Purpose

SOLANA_RPC_URL

for Solana builds

Building a Solana-source swap needs a live RPC connection. Without it the server falls back to the public mainnet endpoint, which throttles hard.

MAYAN_RPC_URL_<CHAIN>

for allowance reads

Per-chain EVM endpoint, e.g. MAYAN_RPC_URL_BASE. Lets the server read your existing allowance instead of assuming an approve is needed. Read-only: the only call made is eth_call.

MAYAN_API_KEY

no

Lifts Mayan's per-IP rate limit. Request one from support@mayan.finance.

MAYAN_HTTP_TIMEOUT_MS

no

Default 30000.

MAYAN_MAX_RETRIES

no

Default 3. Retries are full-jitter backoff, reads only.

MAYAN_TOKEN_CACHE_TTL_MS

no

Default 600000.

MAYAN_REFERRER_EVM / MAYAN_REFERRER_SOLANA

no

Attaches a fee-split referrer to quotes. Unset by default, and left unset in this project — see SECURITY.md.

MAYAN_MCP_DEBUG

no

1 for verbose logging on stderr.

There is no variable for a private key, and there never will be.


Tools

Tool

What it does

get_quote

Every available route for a swap: expected output, guaranteed minimum, slippage, fees, ETA.

build_swap

The unsigned transaction, plus the quote terms and any ERC20 approval needed first.

list_tokens

Tokens Mayan can route on a chain, with addresses and decimals.

track_order

Status of a swap by its source-chain transaction hash.

check_allowance

How much the Mayan forwarder is currently allowed to spend, and whether that covers a swap.

list_chains

Supported chains and their EVM chain ids.

estimate_gas

Gas estimate for a built EVM transaction.

Supported chains: solana, ethereum, base, arbitrum, optimism, polygon, bsc, avalanche.


Worked example

"What do I get for 0.5 SOL into USDC on Base?"

get_quote → two routes:

0.5 SOL on solana -> USDC on base

  SWIFT V2 · Swift
    expected  37.137748 USDC
    minimum   36.933584 USDC  (slippage 55 bps)
    eta       3s
    price     GOOD (ratio 0.9996)
    quote id  0xa4c71a85927b104fe039e05d56efe944

  FAST_MCTP · Best Price
    expected  37.155133 USDC
    minimum   36.954199 USDC  (slippage 50 bps)
    eta       35s
    price     GOOD (ratio 0.9996)

"Build it."

build_swap → the transaction, unsigned:

Unsigned swap built: 10 USDC on base -> SOL on solana via SWIFT

  You receive at least 0.130172143 SOL (expected 0.134198086, slippage 300 bps, ETA 3s)
  Recipient: 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM

  Transaction to sign:
    to       0x337685fdaB40D39bd02028545a4FfA7D287cC3E2
    chainId  8453
    value    0x0
    data     932 bytes (forwardERC20)

  Checked before returning it:
    ok target is the Mayan forwarder from the SDK constant
    ok chain id matches the quote
    ok the call spends USDC, the token quoted
    ok the call spends 10000000 base units, the amount quoted
    ok forwards to 0x40fFE85A28DC9993541449464d7529a922142960, the SWIFT contract the quote declared

  Approval required BEFORE this transaction:
    approve 10000000 (base units) of USDC
    spender 0x337685fdaB40D39bd02028545a4FfA7D287cC3E2   (Mayan forwarder)

  Signers returned: 0. Unsigned. This server holds no private keys and cannot sign or broadcast.

Those checks are not decoration. The transaction is assembled from data Mayan's backend served, so the SDK's own encoded arguments are read back and compared against the quote you were shown: the token, the amount, and the protocol contract. A payload that spends more than it quoted, or routes through a substituted contract, is refused rather than returned. See SECURITY.md.

structuredContent carries the same thing machine-readably: evm_transaction, approval, route and a verification block recording which invariants were checked.

Solana output

Two things are different when the source chain is Solana, both stated here rather than left to be discovered:

  • The cross-check above does not apply. It reads an EVM call's encoded arguments; a Solana build is a list of instructions with no such envelope. Those fields are therefore absent from the verification block rather than reported as failures. What still holds is the route allow-list and the empty signer set — the custody claim itself. Review the instructions in your wallet.

  • MCTP from Solana cannot be built here. That route needs an ephemeral keypair to carry its CCTP message, and this server does not emit key material of any kind. SWIFT from Solana builds without one and is what an unpinned build_swap selects.

A Solana-source swap returns instructions in the SDK's own InstructionInfo shape, so the output feeds straight back into deserializeInstructionInfo() without a translation step:

{
  "programId": "ComputeBudget111111111111111111111111111111",
  "accounts": [],
  "data": "AsBcFQA="
}

Address lookup tables come back as base58 addresses, ready for connection.getMultipleAccountsInfo().


What the caller still has to do

  1. Send the approval first on EVM sources, or the swap reverts. build_swap tells you the token, the amount and the spender. With MAYAN_RPC_URL_<CHAIN> set it reads your current allowance and says nothing when you have already approved enough. (USDT on Ethereum additionally requires zeroing an existing allowance before raising it — the response says so when it applies.)

  2. Sign and submit. Not this server's job, by design.

  3. Check the recipient address in your wallet before signing.


Development

npm install
npm run build
npm test          # unit tests: invariants, serialisation round-trip, schemas, layering
npm run typecheck

npx tsx scripts/smoke.ts   # live: quotes and builds against Mayan's API
npx tsx scripts/e2e.ts     # live: spawns the built server and speaks MCP over stdio

The layout keeps one boundary strictly:

src/client/      the only code that talks to @mayanfinance/swap-sdk
src/tools/       MCP tool definitions — Zod in, structuredContent out
src/transport/   stdio and Streamable HTTP

test/layering.test.ts fails the build if anything above src/client imports the SDK or a chain library directly, and it verifies its own detector fires before trusting the result.


License

MIT. See LICENSE.

Not affiliated with Mayan Finance. This is client software for their public API; it makes no warranty about routing, pricing or settlement, and it is not financial advice.

Install Server
A
license - permissive license
-
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sand0vvv/mayan-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server