@supernova123/defillama-mcp-server 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/index.js +43 -2
- package/package.json +2 -2
- package/server.json +29 -0
- package/src/index.ts +49 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Use it with **Claude Desktop**, **Cursor**, **Windsurf**, **Cline**, **Continue*
|
|
|
15
15
|
## Why use this?
|
|
16
16
|
|
|
17
17
|
- **No API key required** — DeFi Llama is a free public API
|
|
18
|
-
- **
|
|
18
|
+
- **10 built-in tools** — covers the most common DeFi data queries
|
|
19
19
|
- **Clean markdown output** — results read naturally in chat
|
|
20
20
|
- **Rate-limited automatically** — polite 500ms throttle across all endpoints
|
|
21
21
|
|
|
@@ -26,11 +26,12 @@ Use it with **Claude Desktop**, **Cursor**, **Windsurf**, **Cline**, **Continue*
|
|
|
26
26
|
| `search_protocols` | Search DeFi protocols by name — returns top results with TVL, chains, category |
|
|
27
27
|
| `get_protocol_tvl` | Get detailed TVL breakdown for a specific protocol (chain distribution, history, description) |
|
|
28
28
|
| `get_tvl_by_chain` | Get total TVL for a specific chain (Ethereum, Arbitrum, Base, Solana, etc.) |
|
|
29
|
-
| `get_yields` | Get yield/APY data for lending pools and staking, filter by chain / project / min TVL |
|
|
29
|
+
| `get_yields` | Get yield/APY data for lending pools and staking, filter by chain / project / min TVL, sort by APY or TVL |
|
|
30
30
|
| `get_stablecoins` | Get stablecoin market cap data and rankings |
|
|
31
31
|
| `get_bridges` | Get cross-chain bridge TVL and volume data |
|
|
32
32
|
| `get_dex_volumes` | Get DEX trading volumes across chains |
|
|
33
33
|
| `get_protocol_fees` | Get protocol fee and revenue data |
|
|
34
|
+
| `get_token_price` | Get current price for any token by CoinGecko ID, symbol, or chain:address (batch lookups) |
|
|
34
35
|
|
|
35
36
|
## Quick Start
|
|
36
37
|
|
package/dist/index.js
CHANGED
|
@@ -198,8 +198,9 @@ server.tool("get_yields", "Get yield/APY data for lending pools and staking acro
|
|
|
198
198
|
project: z.string().optional().describe("Filter by project (e.g. 'Aave', 'Lido', 'Compound')"),
|
|
199
199
|
min_tvl: z.number().optional().default(1_000_000).describe("Minimum TVL in USD (default $1M)"),
|
|
200
200
|
min_apy: z.number().optional().default(0).describe("Minimum APY % (default 0)"),
|
|
201
|
+
sort_by: z.enum(["tvl", "apy"]).optional().default("tvl").describe("Sort by 'tvl' (default) or 'apy' (highest APY first)"),
|
|
201
202
|
limit: z.number().optional().default(20).describe("Max results (default 20)"),
|
|
202
|
-
}, async ({ chain, project, min_tvl, min_apy, limit }) => {
|
|
203
|
+
}, async ({ chain, project, min_tvl, min_apy, sort_by, limit }) => {
|
|
203
204
|
try {
|
|
204
205
|
const data = await rateLimitedFetch(`${YIELDS_BASE}/pools`);
|
|
205
206
|
if (!data || !Array.isArray(data.data)) {
|
|
@@ -221,7 +222,11 @@ server.tool("get_yields", "Get yield/APY data for lending pools and staking acro
|
|
|
221
222
|
return false;
|
|
222
223
|
return true;
|
|
223
224
|
})
|
|
224
|
-
.sort((a, b) =>
|
|
225
|
+
.sort((a, b) => {
|
|
226
|
+
if (sort_by === "apy")
|
|
227
|
+
return (b.apy || 0) - (a.apy || 0);
|
|
228
|
+
return (b.tvlUsd || 0) - (a.tvlUsd || 0);
|
|
229
|
+
})
|
|
225
230
|
.slice(0, limit);
|
|
226
231
|
if (matches.length === 0) {
|
|
227
232
|
return { content: [{ type: "text", text: `No yield pools matched (chain: ${chain || "any"}, project: ${project || "any"}, min TVL $${min_tvl.toLocaleString()}, min APY ${min_apy}%).` }] };
|
|
@@ -401,6 +406,42 @@ server.tool("get_protocol_fees", "Get protocol fee and revenue data — protocol
|
|
|
401
406
|
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
402
407
|
}
|
|
403
408
|
});
|
|
409
|
+
// ── Tool: get_token_price ──
|
|
410
|
+
server.tool("get_token_price", "Get current price for any token by CoinGecko ID, symbol, or chain:address format. Supports batch lookups.", {
|
|
411
|
+
tokens: z.array(z.string()).describe("Token identifiers — CoinGecko IDs (e.g. 'ethereum', 'bitcoin'), symbols (e.g. 'ETH', 'BTC'), or chain:address (e.g. 'ethereum:0x...')"),
|
|
412
|
+
}, async ({ tokens }) => {
|
|
413
|
+
try {
|
|
414
|
+
// DeFi Llama prices API uses "coingecko:id" or "chain:address" format
|
|
415
|
+
const coinIds = tokens.map((t) => {
|
|
416
|
+
if (t.includes(":"))
|
|
417
|
+
return t; // Already chain:address format
|
|
418
|
+
// Treat as CoinGecko ID or symbol
|
|
419
|
+
if (t.startsWith("0x"))
|
|
420
|
+
return `ethereum:${t}`;
|
|
421
|
+
return `coingecko:${t.toLowerCase()}`;
|
|
422
|
+
});
|
|
423
|
+
const url = `https://coins.llama.fi/prices/current/${coinIds.join(",")}`;
|
|
424
|
+
const data = await rateLimitedFetch(url);
|
|
425
|
+
if (!data || !data.coins) {
|
|
426
|
+
return { content: [{ type: "text", text: "No price data returned." }] };
|
|
427
|
+
}
|
|
428
|
+
const lines = Object.entries(data.coins).map(([id, info]) => {
|
|
429
|
+
const price = info.price != null ? `$${info.price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 })}` : "N/A";
|
|
430
|
+
const symbol = info.symbol || "?";
|
|
431
|
+
const confidence = info.confidence != null ? ` (${Math.round(info.confidence * 100)}% conf)` : "";
|
|
432
|
+
return `- **${symbol}** (${id}) — ${price}${confidence}`;
|
|
433
|
+
});
|
|
434
|
+
return {
|
|
435
|
+
content: [{
|
|
436
|
+
type: "text",
|
|
437
|
+
text: `**Token Prices:**\n\n${lines.join("\n")}`,
|
|
438
|
+
}],
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
catch (e) {
|
|
442
|
+
return { content: [{ type: "text", text: `Error: ${e.message}` }] };
|
|
443
|
+
}
|
|
444
|
+
});
|
|
404
445
|
// Start server
|
|
405
446
|
async function main() {
|
|
406
447
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supernova123/defillama-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "MCP server for DeFi Llama \u2014 free DeFi TVL, yields, stablecoins, bridges, DEX, and fee data for AI assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -48,4 +48,4 @@
|
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"access": "public"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.friendlygeorge/defillama-mcp-server",
|
|
4
|
+
"description": "MCP server for DeFi Llama \u2014 free DeFi TVL, yields, stablecoins, bridges, DEX, and fee data for AI...",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/friendlygeorge/defillama-mcp-server",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "npm",
|
|
13
|
+
"identifier": "@supernova123/defillama-mcp-server",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"transport": {
|
|
16
|
+
"type": "stdio"
|
|
17
|
+
},
|
|
18
|
+
"environmentVariables": [
|
|
19
|
+
{
|
|
20
|
+
"description": "Your API key for the service",
|
|
21
|
+
"isRequired": true,
|
|
22
|
+
"format": "string",
|
|
23
|
+
"isSecret": true,
|
|
24
|
+
"name": "YOUR_API_KEY"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -236,9 +236,10 @@ server.tool(
|
|
|
236
236
|
project: z.string().optional().describe("Filter by project (e.g. 'Aave', 'Lido', 'Compound')"),
|
|
237
237
|
min_tvl: z.number().optional().default(1_000_000).describe("Minimum TVL in USD (default $1M)"),
|
|
238
238
|
min_apy: z.number().optional().default(0).describe("Minimum APY % (default 0)"),
|
|
239
|
+
sort_by: z.enum(["tvl", "apy"]).optional().default("tvl").describe("Sort by 'tvl' (default) or 'apy' (highest APY first)"),
|
|
239
240
|
limit: z.number().optional().default(20).describe("Max results (default 20)"),
|
|
240
241
|
},
|
|
241
|
-
async ({ chain, project, min_tvl, min_apy, limit }) => {
|
|
242
|
+
async ({ chain, project, min_tvl, min_apy, sort_by, limit }) => {
|
|
242
243
|
try {
|
|
243
244
|
const data = await rateLimitedFetch(`${YIELDS_BASE}/pools`);
|
|
244
245
|
if (!data || !Array.isArray(data.data)) {
|
|
@@ -257,7 +258,10 @@ server.tool(
|
|
|
257
258
|
if (projectFilter && !(p.project || "").toLowerCase().includes(projectFilter)) return false;
|
|
258
259
|
return true;
|
|
259
260
|
})
|
|
260
|
-
.sort((a: any, b: any) =>
|
|
261
|
+
.sort((a: any, b: any) => {
|
|
262
|
+
if (sort_by === "apy") return (b.apy || 0) - (a.apy || 0);
|
|
263
|
+
return (b.tvlUsd || 0) - (a.tvlUsd || 0);
|
|
264
|
+
})
|
|
261
265
|
.slice(0, limit);
|
|
262
266
|
|
|
263
267
|
if (matches.length === 0) {
|
|
@@ -472,6 +476,49 @@ server.tool(
|
|
|
472
476
|
}
|
|
473
477
|
);
|
|
474
478
|
|
|
479
|
+
// ── Tool: get_token_price ──
|
|
480
|
+
server.tool(
|
|
481
|
+
"get_token_price",
|
|
482
|
+
"Get current price for any token by CoinGecko ID, symbol, or chain:address format. Supports batch lookups.",
|
|
483
|
+
{
|
|
484
|
+
tokens: z.array(z.string()).describe("Token identifiers — CoinGecko IDs (e.g. 'ethereum', 'bitcoin'), symbols (e.g. 'ETH', 'BTC'), or chain:address (e.g. 'ethereum:0x...')"),
|
|
485
|
+
},
|
|
486
|
+
async ({ tokens }) => {
|
|
487
|
+
try {
|
|
488
|
+
// DeFi Llama prices API uses "coingecko:id" or "chain:address" format
|
|
489
|
+
const coinIds = tokens.map((t: string) => {
|
|
490
|
+
if (t.includes(":")) return t; // Already chain:address format
|
|
491
|
+
// Treat as CoinGecko ID or symbol
|
|
492
|
+
if (t.startsWith("0x")) return `ethereum:${t}`;
|
|
493
|
+
return `coingecko:${t.toLowerCase()}`;
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
const url = `https://coins.llama.fi/prices/current/${coinIds.join(",")}`;
|
|
497
|
+
const data = await rateLimitedFetch(url);
|
|
498
|
+
|
|
499
|
+
if (!data || !data.coins) {
|
|
500
|
+
return { content: [{ type: "text" as const, text: "No price data returned." }] };
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const lines = Object.entries(data.coins).map(([id, info]: [string, any]) => {
|
|
504
|
+
const price = info.price != null ? `$${info.price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 })}` : "N/A";
|
|
505
|
+
const symbol = info.symbol || "?";
|
|
506
|
+
const confidence = info.confidence != null ? ` (${Math.round(info.confidence * 100)}% conf)` : "";
|
|
507
|
+
return `- **${symbol}** (${id}) — ${price}${confidence}`;
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
return {
|
|
511
|
+
content: [{
|
|
512
|
+
type: "text" as const,
|
|
513
|
+
text: `**Token Prices:**\n\n${lines.join("\n")}`,
|
|
514
|
+
}],
|
|
515
|
+
};
|
|
516
|
+
} catch (e: any) {
|
|
517
|
+
return { content: [{ type: "text" as const, text: `Error: ${e.message}` }] };
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
);
|
|
521
|
+
|
|
475
522
|
// Start server
|
|
476
523
|
async function main() {
|
|
477
524
|
const transport = new StdioServerTransport();
|