# MetaLend (remote · mcp.metalend.tech)

Browse, deposit, withdraw & rebalance stablecoins on Aave, Morpho & Euler across major EVM chains.

- Trust score: 78/100 (medium)
- Change this week: +3
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-20

## Components

- remote · `mcp.metalend.tech`: 78/100 (this document), [markdown](https://verifymcp.io/servers/rabeles11-metalend/mcp.md), [page](https://verifymcp.io/servers/rabeles11-metalend/mcp)

## Channel facts

- Endpoint: `https://mcp.metalend.tech/mcp`
- Transports: `streamable-http`
- Auth: `none`
- Version: `1.0.1`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-20.

- **Endpoint Security**: 63/100
  - The endpoint's TLS certificate is valid, in date, and uses a strong key.
  - Authorisation check failed: no authorisation is required to call this server, and it exposes a tool marked destructive (submit_deposit).
  - HTTPS is enforced; there's no plaintext access path.
  - The HSTS (Strict-Transport-Security) header is present.
  - DNSSEC check failed: this domain isn't protected by DNSSEC.
- **Transport & Reachability**: 100/100
  - Verified streamable-http transport via a live MCP handshake.
- **Schema Quality & AI Usability**: 75/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 9925 tokens (~472/item across 21 items; 19 tools + 2 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 80/100
  - Stability observed for 24 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 99% of tool parameters carry a description.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 19 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 21 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the MetaLend MCP server?

MetaLend is a hosted endpoint at https://mcp.metalend.tech/mcp, so there is nothing to install locally. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add --transport http rabeles11-metalend 'https://mcp.metalend.tech/mcp'
```

### Cursor

```json
{
  "mcpServers": {
    "rabeles11-metalend": {
      "url": "https://mcp.metalend.tech/mcp"
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "rabeles11-metalend": {
      "type": "http",
      "url": "https://mcp.metalend.tech/mcp"
    }
  }
}
```

### Codex

```toml
[mcp_servers.rabeles11-metalend]
url = "https://mcp.metalend.tech/mcp"
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "rabeles11-metalend": {
      "type": "remote",
      "url": "https://mcp.metalend.tech/mcp",
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add rabeles11-metalend --url 'https://mcp.metalend.tech/mcp' --transport streamable-http
```

### Hermes

```yaml
mcp_servers:
  rabeles11-metalend:
    url: "https://mcp.metalend.tech/mcp"
```

### Netclaw

```json
{
  "McpServers": {
    "rabeles11-metalend": {
      "Transport": "http",
      "Url": "https://mcp.metalend.tech/mcp"
    }
  }
}
```

### Vellum

```bash
assistant mcp add rabeles11-metalend -t streamable-http -u 'https://mcp.metalend.tech/mcp'
```

### Other

```json
{
  "mcpServers": {
    "rabeles11-metalend": {
      "type": "http",
      "url": "https://mcp.metalend.tech/mcp"
    }
  }
}
```

The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-20 (score 78, +1)

No change was recorded against any check on this day. Stability & Change Management went from 77 to 80. That category is still filling its 30-day observation window: 23 days of observed history at the previous scan, 24 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-17 (score 77, +1)

No change was recorded against any check on this day. Stability & Change Management went from 67 to 70. That category is still filling its 30-day observation window: 20 days of observed history at the previous scan, 21 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-15 (score 76, +1)

No change was recorded against any check on this day. Stability & Change Management went from 60 to 63. That category is still filling its 30-day observation window: 18 days of observed history at the previous scan, 19 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-13 (score 75, +1)

No change was recorded against any check on this day. Stability & Change Management went from 53 to 57. That category is still filling its 30-day observation window: 16 days of observed history at the previous scan, 17 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-11 (score 74, +1)

No change was recorded against any check on this day. Stability & Change Management went from 47 to 50. That category is still filling its 30-day observation window: 14 days of observed history at the previous scan, 15 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-09 (score 73, +1)

No change was recorded against any check on this day. Stability & Change Management went from 40 to 43. That category is still filling its 30-day observation window: 12 days of observed history at the previous scan, 13 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-07 (score 72, +1)

No change was recorded against any check on this day. Stability & Change Management went from 33 to 37. That category is still filling its 30-day observation window: 10 days of observed history at the previous scan, 11 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-05 (score 71, +1)

No change was recorded against any check on this day. Stability & Change Management went from 27 to 30. That category is still filling its 30-day observation window: 8 days of observed history at the previous scan, 9 at this one. The score rises as the window fills, whether or not the server changes.

## MCP tools (19)

### `list_pools` (~239 tokens)

List rebalancer pools

List all pools MetaLend's rebalancer can deposit into: protocol (Aave, Morpho, Euler), chain, APY breakdown (native/rewards/total/net-of-fee), TVL, liquidity, and the signData (protocolId, poolAddress, domainId) needed to build a rebalancer config. Does NOT return a wallet's current balances or configuration — use get_balances / get_config for that. Callers should filter out pools where `blacklisted` is true. `poolTvl`/`poolLiquidity` can read "0" either because the pool is genuinely empty or because upstream data is momentarily missing — the two are not distinguishable here. There is no pool-level paused/capacity/deposit-or-withdraw-enabled flag: deposits can be paused globally, and withdrawals from specific pools can be temporarily blocked per-wallet (e.g. during a funding-cap refill) — neither shows up in this list, only as an error from submit_deposit/submit_withdrawal itself. Rate limited to 20 calls/minute per caller, no more than one call every 3s.

### `get_balances` (~351 tokens)

Get rebalancer balances

Get a wallet's deployed rebalancer balances broken down per token, per chain, and per protocol/pool, including net earnings and blended APY. Also returns the wallet's rebalancerAddress (required only for approvals during deposits, never use this address as input into tools) and per-pool withdrawRequest EIP-712 data. Does NOT return rebalancingManagerAddress (needed for config signing) — use get_config for that. Does NOT return pool catalog/APY data for pools the wallet isn't in — use list_pools for that. Fails if the wallet has no rebalancer yet; use get_default_config to see what a first-time config would look like. Balances here already reflect every on-behalf gas fee paid so far — not just from this wallet's own deposits/withdrawals, but also from MetaLend automatically moving funds between pools/protocols/chains to chase yield or honor this wallet's config; each such move costs its own gas fee. So the total across pools can be slightly less than the sum of everything ever deposited, with no matching withdrawal — see get_config's `totalGasFee` for the lifetime total. Rate limited to 5 calls/minute per caller, no more than one call every 12s.

Input parameters:

- `tokens` (string): Comma-separated token symbols to restrict to (e.g. 'USDC,USDT'). Omit for all supported tokens.
- `walletAddress` (string, required): The user's own wallet address (EOA or smart-contract wallet) — the owner address they used to make deposits. This is NOT the rebalancerAddress that this tool returns: never pass a rebalancerAddress h…

### `get_bridge_balances` (~219 tokens)

Get bridge balances

Get a wallet's in-transit USDC bridge balances (funds moving cross-chain toward a destination pool), including estimated completion time. Does NOT include already-settled rebalancer balances — use get_balances for those. Only applies to USDC. Other tokens do not support bridging. Each entry's `sourcePool` is null for a cross-chain deposit in transit, or a populated pool for an internal rebalance moving funds between pools; `isForSpending: true` marks a funding-cap refill specifically. There is NO trackingId on these entries — they cannot be correlated back to a specific submit_deposit call; use get_deposit_status with the trackingId from that call instead to track one particular deposit. Rate limited to 20 calls/minute per caller, no more than one call every 3s.

Input parameters:

- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `get_config` (~524 tokens)

Get rebalancer config

Get a wallet's current rebalancer configuration per token: which protocols/pools/chains are enabled, rebalance frequency, lifetime deposits/withdrawals/fees, and spending cap. `spendingCapRaw`/`spendingCapFormatted` (USDC only, null/"0" for every other token) is NOT a spending/deposit/withdrawal limit despite the name. When set (nonzero), it's the target balance MetaLend automatically keeps topped up in the wallet's OWN address (not the rebalancer contract) as Aave aUSDC + native USDC on Linea combined — funding a card tied to this wallet that spends directly from that Linea balance. Refills happen automatically on a weekly cycle and after fresh USDC deposits, not instantly. null/"0" means this auto-funding is off, not "uncapped" (there is no general spending ceiling to remove) — see prepare_config's own spendingCapRaw field description for the full mechanics. Also returns rebalancerAddress and rebalancingManagerAddress. Does NOT return live balances — use get_balances for that. `rebalancerExists: false` means the wallet has never configured a rebalancer; use get_default_config to see recommended starting values. Each configuration's `totalGasFee` is the lifetime sum of on-behalf gas fees this wallet has paid for this token — not just from deposits/withdrawals it explicitly requested. MetaLend periodically moves a wallet's funds between pools/protocols/chains on its own (chasing better yield, honoring `requiredTvl`/`requiredLiquidityMultiplier`/`collateralExposure`), and each such move is its own on-chain transaction with its own gas fee, deducted the same way a deposit/withdrawal fee is. So a wallet's balance can drift down slightly between get_balances polls with no deposit/withdrawal in between — that's this, not a bug or lost funds. IMPORTANT: `configurations` always includes a default/template entry for every supported token, whether or not the wallet ever signed one — a matching entry existing does NOT mean the wallet has a real config. Check that entry's `ha…

Input parameters:

- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `get_default_config` (~123 tokens)

Get default rebalancer config for a token

Get MetaLend's recommended default rebalancer configuration for a given token symbol (e.g. 'USDC'): rebalancingManagerAddress plus recommended protocolIds/poolAddresses/domainIds. Intended for wallets with no existing config yet — for an existing wallet's current config use get_config instead. Rate limited to 20 calls/minute per caller, no more than one call every 3s.

Input parameters:

- `token` (string, required): Token symbol, e.g. USDC, USDT, MUSD, RLUSD, USDG, USDE, PYUSD.

### `get_rewards` (~314 tokens)

Get rebalancer rewards

Get a wallet's aggregated rewards/earnings across all reward sources (e.g. Merkle), including claimed/available USD totals. Does NOT include base rebalancer yield/APY earnings — those are in get_balances' netEarning field. There is NO backend endpoint to submit a claim — this server cannot claim rewards for you. Each rewards[].rewardItems[] entry with an available balance carries an optional claimTransaction (chain-specific, per RewardItem.chain) with everything needed to broadcast the claim directly: `to` (send the transaction here — this is MetaLend's RebalancingManager contract, NOT the same item's own `distributorAddress`, which is only an input parameter, not the call target), `abi`, and pre-encoded `calldata`. Your own wallet must sign and broadcast this on-chain (this server has no RPC access and cannot do it for you) — if claimTransaction is absent on an item, treat it as nothing currently claimable there (e.g. still vesting) rather than assuming one should exist. Rate limited to 20 calls/minute per caller, no more than one call every 3s.

Input parameters:

- `reloadChain` (string): Chain name to force a fresh reload for (e.g. 'BASE'). Omit to use cached data.
- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `get_transaction_costs` (~104 tokens)

Get transaction costs

Get constant on-behalf deposit/withdraw gas costs and minimum deposit/withdraw amounts for a token, broken down per supported chain. Use before a deposit to check the amount meets the chain's minimum. Rate limited to 20 calls/minute per caller, no more than one call every 3s.

Input parameters:

- `token` (string, required): Token symbol, e.g. USDC, USDT, MUSD, RLUSD, USDG, USDE, PYUSD.

### `get_token_info` (~126 tokens)

Get token info

Get a token's on-chain metadata (display name, decimals, EIP-712 version) for a given chain. Needed when building an EIP-3009 deposit signature's tokenName/tokenVersion fields. Rate limited to 6 calls/minute per caller, no more than one call every 10s.

Input parameters:

- `chain` (string, required): Chain name, e.g. BASE, ETHEREUM, POLYGON.
- `token` (string, required): Token symbol, e.g. USDC, USDT, MUSD, RLUSD, USDG, USDE, PYUSD.

### `get_withdrawal_version` (~63 tokens)

Get withdrawal contract version

Get the contract signing version string required for the EIP-712 domain data used when signing a withdrawal. Rarely changes; safe to cache client-side. Rate limited to 10 calls/minute per caller, no more than one call every 6s.

### `get_auth_challenge` (~281 tokens)

Get SIWE auth challenge

Get a Sign-In-With-Ethereum challenge message for a wallet, required before any deposit/withdrawal/config-update tool. The returned `message` must be signed with personal_sign (EIP-191) by the wallet's own signer, then passed to submit_auth_verify. Does NOT itself authenticate anything — it only issues the message to sign. Repeated calls for the same walletAddress within a short window (a few minutes) return the SAME message rather than a fresh one — the backend only keeps one pending challenge per wallet at a time, and generating a new one would invalidate whatever an earlier caller is about to sign, so this is deliberate, not a caching bug. `chain` does not affect which cached message you get back. Rate limited to 10 calls/minute per caller, no more than one call every 6s.

Input parameters:

- `chain` (string): Chain name, e.g. BASE, ETHEREUM, POLYGON. Defaults to ETHEREUM if omitted. Cosmetic only — shown as the 'Chain ID:' line in the SIWE message text, does not need to match the chain passed to submit_au…
- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `submit_auth_verify` (~349 tokens)

Verify SIWE signature and get a JWT

Exchange a signed SIWE challenge message (from get_auth_challenge) for a JWT scoped to that wallet. The returned `jwt` must be passed explicitly as the `jwt` argument to submit_deposit/submit_withdrawal/submit_config for that same wallet — this server does not cache or store it. The JWT is only valid for the walletAddress that produced the signature; using it for a different wallet's write call will be rejected upstream. Reuse the same jwt for subsequent write calls to this wallet instead of re-authenticating every time — the response's `expiresAt` (decoded from the JWT's own exp claim) says how long it's good for. Rate limited to 5 calls/minute per caller, no more than one call every 12s.

Input parameters:

- `chain` (string): Chain name, e.g. BASE, ETHEREUM, POLYGON. Defaults to ETHEREUM if omitted. For an EOA wallet this is irrelevant (signature recovery is chain-agnostic). For a smart-contract wallet, this MUST be the c…
- `signature` (string, required): personal_sign signature of the SIWE message from get_auth_challenge.
- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `prepare_deposit` (~685 tokens)

Prepare a deposit

Build everything needed to deposit into MetaLend's rebalancer, without signing anything. For gasless tokens (USDC, MUSD, PYUSD) returns an EIP-712 ReceiveWithAuthorization typed-data payload — sign it with your own wallet and pass the signature to submit_deposit. For approval-only tokens (USDT, RLUSD, USDG, USDE) returns on-chain approve() parameters instead — your wallet must broadcast that approval itself (this server has no RPC access and cannot do it for you), then call submit_deposit with no signature. The signature flow only accepts a raw 65-byte EOA-style ECDSA signature — smart-contract wallets (including via ERC-6492 counterfactual deployment) are rejected regardless of validity, even for gasless-eligible tokens. A smart-contract wallet should instead pass `method: "approval"` explicitly here (works for any token, needs no signature at all) — but `chain` must then be one that wallet can actually transact on (see `chain`'s own field description); this server cannot validate that. Requires a signed rebalancer config for this token already (use prepare_config/submit_config first if get_config shows none) — and validates the amount against the chain's minimum deposit — before returning anything, so a doomed request never reaches signing. A fixed on-behalf gas fee (see `fee` in the response, also available standalone via get_transaction_costs) is deducted from `amount` before the rebalancer credits it — the response's `expectedCreditedAmountRaw` is what will actually show up in get_balances after the deposit lands, not the full `amount` you send. Rate limited to 6 calls/minute per caller, no more than one call every 10s.

Input parameters:

- `amount` (string, required): Raw token amount as a decimal-digit string (smallest denomination). Use get_token_info for decimals.
- `chain` (string, required): Chain name, e.g. BASE, ETHEREUM, POLYGON — the originating chain of this deposit: where the approve() call is broadcast, or for the signature flow, the chain the EIP-712 signature must validate on. N…
- `method` (string): Force a specific flow. Defaults to 'signature' for gasless-eligible tokens, else 'approval'.
- `token` (string, required): Token symbol, e.g. USDC, USDT, MUSD, RLUSD, USDG, USDE, PYUSD.
- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `submit_deposit` (~1026 tokens)

Submit a deposit

Submit a deposit to MetaLend's rebalancer — moves funds. Requires a `jwt` from submit_auth_verify for this walletAddress, and either a `signature` (from prepare_deposit's signature-method output) or, for approval-based tokens, no signature at all once your wallet has already broadcast the approve() transaction on-chain. Always call prepare_deposit first — this tool does not validate amounts or resolve addresses itself (it does reject a partial set of validAfter/validBefore/nonce/signature/tokenName/tokenVersion — provide all six, or none). This call can time out on its own if the backend accepted the deposit but its response took too long to arrive. For a signature-based deposit, the trackingId needed for get_deposit_status is still recoverable then: retry with the exact same signature/nonce — it cannot double-spend (a real duplicate is rejected with a 409 signature-already-used error, which itself confirms the original deposit went through), and that error's message now includes the original trackingId, so read it from there and call get_deposit_status directly rather than escalating to support. If that error's message doesn't include a trackingId (it's only included when the conflicting deposit belongs to this same wallet), treat the timeout as inconclusive rather than a failure and escalate to support with the wallet address and approximate time instead. For an approval-based deposit (no signature), retrying after an uncertain outcome is safe in the ordinary case without needing to check status first: this tool's own `amount` is only used by the backend to check against your current on-chain allowance before anything is broadcast — the on-chain deposit transaction itself takes no amount parameter, it just pulls and consumes your entire then-current allowance atomically. So if the original attempt already landed, a retry finds a zero allowance and is rejected outright with a 400 ApprovalValidationError from that same pre-flight check, before anything is broadcast…

Input parameters:

- `amount` (string, required): Raw token amount as a decimal-digit string (smallest denomination). Use get_token_info for decimals.
- `chain` (string, required): Chain name, e.g. BASE, ETHEREUM, POLYGON — the originating chain of this deposit: where the approve() call is broadcast, or for the signature flow, the chain the EIP-712 signature must validate on. N…
- `jwt` (string, required): JWT from submit_auth_verify for this walletAddress.
- `nonce` (string): From prepare_deposit's signature-method output.
- `signature` (string): Signature over prepare_deposit's typedData. Omit for approval-based deposits.
- `token` (string, required): Token symbol, e.g. USDC, USDT, MUSD, RLUSD, USDG, USDE, PYUSD.
- `tokenName` (string): From prepare_deposit's signature-method output.
- `tokenVersion` (string): From prepare_deposit's signature-method output.
- `validAfter` (string): From prepare_deposit's signature-method output.
- `validBefore` (string): From prepare_deposit's signature-method output.
- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `get_deposit_status` (~280 tokens)

Get deposit status

Poll the status of a deposit by trackingId (returned by submit_deposit). `status` is one of: PROCESSING — still confirming on-chain, keep polling; BRIDGING — deposit accepted, funds are now crossing chains via CCTP to the target pool (typically 20-30 minutes, up to several hours from Linea) — treat this as done, do NOT keep polling waiting for the bridge to finish; SUCCESS — terminal, deposit fully completed (reached directly for a same-chain deposit, or later for a bridged one once funds land — you do not need to wait for it after BRIDGING); FAILED — terminal, deposit did not go through; EMERGENCY_WITHDRAW — terminal but NOT success: after the bridge completed and funds reached the destination chain, the destination pool could not accept the deposit (e.g. it was at capacity), so funds were automatically sent back directly to your OWN wallet address — not to the rebalancer, not lost or stuck. This resolution (bridge completion plus destination-side processing) can itself take 30+ minutes after BRIDGING is first observed, so don't expect it right away. Rate limited to 20 calls/minute per caller, no more than one call every 3s.

Input parameters:

- `trackingId` (string, required): trackingId returned by submit_deposit.

### `prepare_withdrawal` (~509 tokens)

Prepare a withdrawal

Build the EIP-712 typed-data payload to sign for withdrawing from a specific pool, without signing anything. Looks up the exact withdrawRequest domain/types/value MetaLend expects from your current balances, fills in amount and a short-lived deadline, and returns it ready to sign with your own wallet. Pass the resulting signature to submit_withdrawal. Omit `amount` (or pass "MAX") to withdraw the pool's entire balance — this uses the maxUint256 convention rather than the exact current balance, which avoids stale-balance/rounding failures. `chain` is required: the same pool contract address can exist on multiple chains (e.g. Aave reuses one address across Polygon/Arbitrum/Avalanche/Optimism), so poolContract alone cannot disambiguate a wallet holding balance in that pool on more than one of those chains. Also refuses to build typedData when the pool's live liquidity (see list_pools' poolLiquidity) does not exceed the amount being withdrawn (this also applies to MAX, using the pool's own balance) — the backend would reject the signed request either way, so this catches it before a wallet signs. A fixed on-behalf gas fee (see `fee` in the response, also available standalone via get_transaction_costs) is deducted from the withdrawn amount before it reaches your wallet — the response's `expectedReceivedAmountRaw` is what will actually arrive, not the full `amount` being withdrawn from the pool. Rate limited to 5 calls/minute per caller, no more than one call every 12s.

Input parameters:

- `amount`: Raw amount to withdraw. Omit or pass "MAX" for a full withdrawal (uses maxUint256).
- `chain` (string, required): Chain name, e.g. BASE, ETHEREUM, POLYGON.
- `poolContract` (string, required): Pool contract address, from get_balances' perPool[].poolAddress. Combined with `chain` to identify the exact balance — the same address can exist on several chains.
- `token` (string, required): Token symbol, e.g. USDC, USDT, MUSD, RLUSD, USDG, USDE, PYUSD. Required to disambiguate — some pool addresses serve multiple tokens.
- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `submit_withdrawal` (~535 tokens)

Submit a withdrawal

Submit a withdrawal from MetaLend's rebalancer — moves funds. Requires a `jwt` from submit_auth_verify for this walletAddress and a `signature` over prepare_withdrawal's typedData. Always call prepare_withdrawal first — this tool does not look up pool data or validate amounts. The signature's EIP-712 domain is scoped to this one `chain` — for a smart-contract wallet, it must have (or, if undeployed, would have via ERC-6492 counterfactual deployment) a valid signer on that exact chain (an EOA is chain-agnostic and unaffected). This call can time out on its own if the backend accepted the withdrawal but its response took too long to arrive — the trackingId needed for get_withdrawal_status is still recoverable then: retry with the exact same signature — it cannot double-spend (a real duplicate is rejected with a 409 signature-already-used error, which itself confirms the original withdrawal went through), and that error's message now includes the original trackingId, so read it from there and call get_withdrawal_status directly rather than escalating to support. If that error's message doesn't include a trackingId (it's only included when the conflicting withdrawal belongs to this same wallet), treat the timeout as inconclusive rather than a failure and escalate to support with the wallet address and approximate time instead. Can also fail with a 403 if the jwt wasn't issued for this walletAddress, or a 409 if withdrawing from the Linea Aave USDC pool specifically while a funding-cap refill for that wallet is in progress there (temporary; not visible ahead of time from list_pools or get_balances) — wait and retry. Rate limited to 20 calls/minute per caller, no more than one call every 3s.

Input parameters:

- `amount` (string, required): From prepare_withdrawal's output.
- `chain` (string, required): From prepare_withdrawal's output.
- `deadline` (string, required): From prepare_withdrawal's output.
- `jwt` (string, required): JWT from submit_auth_verify for this walletAddress.
- `poolContract` (string, required)
- `signature` (string, required): Signature over prepare_withdrawal's typedData.
- `token` (string, required): Token symbol, e.g. USDC, USDT, MUSD, RLUSD, USDG, USDE, PYUSD.
- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `get_withdrawal_status` (~111 tokens)

Get withdrawal status

Poll the status of a withdrawal by trackingId (returned by submit_withdrawal). `status` is one of: PROCESSING — still in progress, keep polling (withdrawals have no cross-chain bridging leg, unlike deposits); SUCCESS — terminal, withdrawal completed; FAILED — terminal, withdrawal did not go through. Rate limited to 20 calls/minute per caller, no more than one call every 3s.

Input parameters:

- `trackingId` (string, required): trackingId returned by submit_withdrawal.

### `prepare_config` (~1979 tokens)

Prepare a rebalancer config update

Build the signing hash for updating a wallet's rebalancer configuration (which pools/protocols/chains it's allowed to move funds into), without signing anything. Computes keccak256(abi.encode(managerAddress, protocolIds, poolAddresses, domainIds, spendingCapRaw)) server-side — sign the returned hashToSign with personal_sign over its raw 32 bytes (not the UTF-8 text of the hex string) and pass the signature to submit_config. Refuses to build a config that would drop a pool you still hold a nonzero balance in — withdraw from it first (prepare_withdrawal) and retry. Also refuses an invalid `spendingCapRaw` (see its own field description for the exact rules), a non-null `collateralExposure` with no Aave pool included, containing an untracked symbol, or where no requested Aave pool's live TVL meets requiredTvl, any (domainId, protocolId, poolAddress) tuple that doesn't match a real pool in the current catalog for this token (see list_pools), or a config where every requested pool is blacklisted (see list_pools' `blacklisted` field) — a deposit under such a config would have no eligible destination and fail later, well after signing — rather than building a hash for it. For a smart-contract wallet: there is no single `chain` for this signature — the backend derives candidate chains straight from `domainIds` and requires the SAME signature to independently pass ERC-1271/ERC-6492 verification on EVERY chain named in domainIds, not just one; if the wallet doesn't have (deployed, or via ERC-6492 counterfactual deployment) a valid signer on all of them, the whole update is rejected. That signature must also be cross-chain transferable — supporting ERC-6492 counterfactual deployment does not by itself guarantee that. Some ERC-6492-compliant wallets intentionally bind their signature to a single network domain and are not cross-chain transferable — e.g. Coinbase's Smart Wallet (Base Smart Wallet) supports ERC-6492 but scopes its signature to one chain, so it does not support cr…

Input parameters:

- `collateralExposure`: Whitelist of collateral asset symbols — applies only to Morpho vault selection; a vault is excluded from rebalancing if its own tracked collateral exposure includes any symbol not in this list. Each…
- `domainIds` (array, required): Chain domain IDs, one per poolAddresses entry, from list_pools' signData.domainId. Max 200 entries. For a smart-contract wallet, every distinct chain named here must independently verify the SAME sig…
- `includeRewardsApy` (boolean): Whether reward-token (e.g. Merkle) APY counts toward the total-APY comparison used to pick the best pool to rebalance into — false restricts the comparison to native lending APY only. Defaults to tru…
- `poolAddresses` (array, required): Pool contract addresses to allow, from list_pools' signData.poolAddress. Max 200 entries.
- `protocolIds` (array, required): 0=Aave, 1=Morpho, 2=Euler — one entry per poolAddresses/domainIds entry, from list_pools' signData.protocolId. Pass only the exact set of (domainId, protocolId, poolAddress) pools you intend to allow…
- `requiredLiquidityMultiplier` (number|string): Requires a candidate pool's available liquidity to be at least this multiplier times the deposit amount (USD) before it's eligible as a rebalance target — a liquidity safety margin, not a percentage.…
- `requiredTvl` (number|string): Minimum pool TVL in USD required for a pool to be eligible as a rebalance target — pools below this are skipped when the rebalancer picks where to move funds. 0 (default if omitted) disables the filt…
- `spendingCapRaw`: Raw units, USDC only (must be "0"/null/omitted for every other token). "0" means this feature is off (the default if omitted or null — get_config's own response serializes it as null, pass that strai…
- `token` (string, required): Token symbol this config applies to, e.g. USDC, USDT, MUSD, RLUSD, USDG, USDE, PYUSD.
- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

### `submit_config` (~1578 tokens)

Submit a rebalancer config update

Submit a signed rebalancer configuration update. Requires a `jwt` from submit_auth_verify for this walletAddress and a `signature` over prepare_config's hashToSign. Always call prepare_config first — this tool does not compute the signing hash or check the balance invariant itself. Can return a 409 if a rebalance or deposit is currently in progress for this wallet+token, or (USDC only) a funding-cap refill is in progress — this is unrelated to the signature and not something prepare_config could have caught; wait and retry (the error body includes a retryAfterSeconds hint). This call can also time out on its own if the backend accepted the update but its response took too long to arrive — if that happens, check get_config afterward rather than assuming it didn't happen. Rate limited to 10 calls/minute per caller, no more than one call every 6s.

Input parameters:

- `collateralExposure`: Whitelist of collateral asset symbols — applies only to Morpho vault selection; a vault is excluded from rebalancing if its own tracked collateral exposure includes any symbol not in this list. Each…
- `domainIds` (array, required): Chain domain IDs, one per poolAddresses entry, from list_pools' signData.domainId. Max 200 entries. For a smart-contract wallet, every distinct chain named here must independently verify the SAME sig…
- `includeRewardsApy` (boolean): Whether reward-token (e.g. Merkle) APY counts toward the total-APY comparison used to pick the best pool to rebalance into — false restricts the comparison to native lending APY only. Defaults to tru…
- `jwt` (string, required): JWT from submit_auth_verify for this walletAddress.
- `poolAddresses` (array, required): Pool contract addresses to allow, from list_pools' signData.poolAddress. Max 200 entries.
- `protocolIds` (array, required): 0=Aave, 1=Morpho, 2=Euler — one entry per poolAddresses/domainIds entry, from list_pools' signData.protocolId. Pass only the exact set of (domainId, protocolId, poolAddress) pools you intend to allow…
- `requiredLiquidityMultiplier` (number|string): Requires a candidate pool's available liquidity to be at least this multiplier times the deposit amount (USD) before it's eligible as a rebalance target — a liquidity safety margin, not a percentage.…
- `requiredTvl` (number|string): Minimum pool TVL in USD required for a pool to be eligible as a rebalance target — pools below this are skipped when the rebalancer picks where to move funds. 0 (default if omitted) disables the filt…
- `signature` (string, required): Signature over prepare_config's hashToSign.
- `spendingCapRaw`: Raw units, USDC only (must be "0"/null/omitted for every other token). "0" means this feature is off (the default if omitted or null — get_config's own response serializes it as null, pass that strai…
- `token` (string, required): Token symbol this config applies to, e.g. USDC, USDT, MUSD, RLUSD, USDG, USDE, PYUSD.
- `walletAddress` (string, required): EVM wallet address (0x-prefixed, 40 hex chars). Any casing is accepted — it is normalized to its EIP-55 checksum before being forwarded upstream.

## Diagnostics

Captured diagnostic sections: TLS, DNSSEC, Authorisation, Transports. The full working is on the page: https://verifymcp.io/servers/rabeles11-metalend/mcp#diagnostics

## Score history

- 2026-09-20: 78
- 2026-09-19: 77
- 2026-09-18: 77
- 2026-09-17: 77
- 2026-09-16: 76
- 2026-09-15: 76
- 2026-09-14: 75
- 2026-09-13: 75
- 2026-09-12: 74
- 2026-09-11: 74
- 2026-09-10: 73
- 2026-09-09: 73
- 2026-09-08: 72
- 2026-09-07: 72
- 2026-09-06: 71
- 2026-09-05: 71
- 2026-09-04: 70
- 2026-09-03: 70
- 2026-09-02: 70
- 2026-09-01: 69
- 2026-08-31: 69
- 2026-08-30: 68
- 2026-08-29: 68
- 2026-08-28: 67
- 2026-08-27: 67

## Common questions

### What is the MetaLend MCP server?

MetaLend is an MCP server listed in the public MCP registry as io.github.rabeles11/metalend. Browse, deposit, withdraw & rebalance stablecoins on Aave, Morpho & Euler across major EVM chains. This page covers its hosted endpoint (https://mcp.metalend.tech/mcp).

### Is the MetaLend MCP server safe to use?

MetaLend scores 78 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the MetaLend MCP server expose?

MetaLend exposes 19 tools: list_pools, get_balances, get_bridge_balances, get_config, get_default_config, and 14 more. Their descriptions and schemas cost roughly 9,396 tokens of context every time the server is loaded.

### Does the MetaLend MCP server require authentication?

No. We connected to MetaLend without credentials and it answered, so anything it exposes is reachable by anyone who knows the address.

### Is the MetaLend MCP server still maintained?

MetaLend is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- Remote endpoint: https://mcp.metalend.tech/mcp
- Repository: https://github.com/rabeles11/metalend-mcp
- Website: https://mcp.metalend.tech/docs/
- Changelog RSS feed: https://verifymcp.io/servers/rabeles11-metalend/mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/rabeles11-metalend/mcp.json
- HTML version of this page: https://verifymcp.io/servers/rabeles11-metalend/mcp
