Subchapter 2.18
plugins/moonwell.mdMarkdown7 KBView on GitHub
[!IMPORTANT] Complete the short Base MCP onboarding flow defined in
SKILL.mdbefore calling any Moonwell endpoint. The user’s wallet address — required forprepareand position queries — is fetched lazily when needed.
Moonwell is a Compound v2 lending protocol on Base and Optimism. The plugin reads positions/rates and prepares unsigned calldata over the Moonwell HTTP API, then executes via send_calls. No additional MCP server required — everything goes through an HTTP call + send_calls.
Supported chains: Base (8453), Optimism (10).
Moonwell is HTTP-only; every capability follows the standard HTTP routing in ../references/custom-plugins.md.
| Capability | Path |
|---|---|
| Read markets/rates/positions/health | Harness HTTP tool if available, else web_request GET against api.moonwell.fi. |
| Prepare a lend/borrow action | Harness HTTP tool or web_request (GET or POST) → calldata → send_calls. |
GET https://api.moonwell.fi/v1/markets?chain=base
GET https://api.moonwell.fi/v1/markets/USDC?chain=base
GET https://api.moonwell.fi/v1/rates?chain=base&asset=USDC
GET https://api.moonwell.fi/v1/yield?chain=base&sort=apy&min-tvl=1000000&limit=5
GET https://api.moonwell.fi/v1/positions/<address>?chain=base
GET https://api.moonwell.fi/v1/health/<address>?chain=base
GET https://api.moonwell.fi/v1/rewards/<address>?chain=base
GET https://api.moonwell.fi/v1/token-balance/<address>?chain=base&asset=USDCMarket reads are edge-cached 30 s. User-scoped reads (positions, health, rewards, token-balance) are never cached.
/positions returns an array — one entry per market. Use ?active=true to filter out markets where both suppliedUsd and borrowedUsd are zero.
Verbs: supply, withdraw, borrow, repay.
GET form (query params):
GET https://api.moonwell.fi/v1/prepare/supply?chain=base&asset=USDC&amountDecimal=100&from=<address>POST form (JSON body — pass as the body object parameter to web_request):
{
"url": "https://api.moonwell.fi/v1/prepare/supply",
"method": "POST",
"headers": { "content-type": "application/json" },
"body": { "chain": "base", "asset": "USDC", "amountDecimal": "100", "from": "<address>" }
}Both return identical response shapes. Use GET when simpler; use POST when the body is complex.
| Field | Notes |
|---|---|
chain | base (default), optimism, or chain ID |
asset | Symbol: USDC, WETH, ETH (alias for WETH) |
amountDecimal | Human-readable string, e.g. "100". Use this or amount (base units), never both. |
from | User’s wallet address (from get_wallets) |
web_request(https://api.moonwell.fi/v1/prepare/<verb>?...)
→ { data: { transactions: [ { to, data, value, chainId }, ... ] } }
↓
send_calls(chain, calls mapped from transactions[])
→ approvalUrl + requestId
↓
User approves at the returned approval URL (present as "Approve Transaction" — see ../references/approval-mode.md)
↓
get_request_status(requestId) → confirmedSteps in transactions[] are ordered — approve and enter-market come before the protocol action. Execute them as a single send_calls batch.
Target tool: send_calls.
A prepare/<verb> response returns ordered transaction steps:
{
"data": {
"transactions": [
{ "step": "approve", "to": "0x...", "data": "0x...", "value": "0x0", "chainId": 8453 },
{ "step": "enter-market", "to": "0x...", "data": "0x...", "value": "0x0", "chainId": 8453 },
{ "step": "moonwell-supply", "to": "0x...", "data": "0x...", "value": "0x0", "chainId": 8453 }
]
}
}Pass all items as the calls array to send_calls, mapping chainId from any transaction item to the Base MCP chain string (base for Base mainnet, optimism for Optimism). Then walk the approval flow (see ../references/approval-mode.md) and poll get_request_status.
1. get_wallets → address
2. web_request GET /token-balance/<address>?chain=base&asset=USDC → confirm balance ≥ 100
3. web_request GET /prepare/supply?chain=base&asset=USDC&amountDecimal=100&from=<address>
4. send_calls(chain="base", calls from transactions[])
5. User approves → get_request_status(requestId)1. get_wallets → address
2. web_request GET /health/<address>?chain=base → verify health > 1.5
3. web_request GET /prepare/borrow?chain=base&asset=USDC&amountDecimal=50&from=<address>
4. send_calls(chain="base", calls from transactions[])
5. User approves → get_request_status(requestId)1. get_wallets → address
2. web_request GET /positions/<address>?chain=base&active=true → show per-market balances
3. web_request GET /health/<address>?chain=base → show health factor/health/<address> before and after a borrow or withdraw, and surface the value to the user.| Value | Status |
|---|---|
> 1.5 | Healthy |
1.1 – 1.5 | Caution |
< 1.1 | Liquidation risk |
null | No borrows |
asset=ETH and asset=WETH resolve to the same mWETH marketmint, borrow, repay return non-zero codes for business-logic failures without reverting. Check the onchain receipt after broadcastmarketAddress or deprecated: true| Chain | Moonwell chainId | Base MCP chain |
|---|---|---|
| Base mainnet | 8453 | base |
| Optimism | 10 | optimism |