@supernova123/defillama-mcp-server 1.0.0 → 1.0.3

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.
@@ -0,0 +1,62 @@
1
+ # GitHub Actions: Publish MCP Server to npm + Official Registry
2
+ #
3
+ # Setup per repo:
4
+ # 1. Add NPM_TOKEN secret to repo Settings → Secrets → Actions
5
+ # 2. Enable "Allow GitHub Actions to create and approve pull requests"
6
+ # in repo Settings → Actions → General → Workflow permissions
7
+ # 3. Push to main or trigger manually
8
+ #
9
+ # This workflow handles:
10
+ # - TypeScript build
11
+ # - npm publish (public)
12
+ # - Official MCP Registry publish (via GitHub OIDC)
13
+
14
+ name: Publish MCP Server
15
+
16
+ on:
17
+ push:
18
+ branches: [main]
19
+ paths:
20
+ - 'src/**'
21
+ - 'package.json'
22
+ - 'tsconfig.json'
23
+ workflow_dispatch:
24
+
25
+ permissions:
26
+ contents: read
27
+ id-token: write # Required for OIDC auth with MCP Registry
28
+
29
+ jobs:
30
+ publish:
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - name: Checkout
34
+ uses: actions/checkout@v4
35
+
36
+ - name: Setup Node.js
37
+ uses: actions/setup-node@v4
38
+ with:
39
+ node-version: '20'
40
+ registry-url: 'https://registry.npmjs.org'
41
+
42
+ - name: Install dependencies
43
+ run: npm ci
44
+
45
+ - name: Build
46
+ run: npm run build
47
+
48
+ - name: Publish to npm
49
+ run: npm publish --access public
50
+ env:
51
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
52
+
53
+ - name: Install mcp-publisher
54
+ run: |
55
+ curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz" | tar xz mcp-publisher
56
+ sudo mv mcp-publisher /usr/local/bin/
57
+
58
+ - name: Publish to Official MCP Registry
59
+ run: |
60
+ mcp-publisher login github-oidc
61
+ mcp-publisher init
62
+ mcp-publisher publish
package/README.md CHANGED
@@ -1,49 +1,51 @@
1
1
  # DeFi Llama MCP Server
2
2
 
3
- > An MCP server for [DeFi Llama](https://defillama.com)connect any MCP-compatible client to free DeFi protocol data.
3
+ > Free DeFi data for AI assistantsTVL, yields, stablecoins, bridges, DEX volumes, and protocol fees via [DeFi Llama](https://defillama.com).
4
4
 
5
5
  [![MCP Compatible](https://img.shields.io/badge/MCP-compatible-blueviolet)](https://modelcontextprotocol.io)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.5-blue)](https://www.typescriptlang.org/)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
8
+ [![npm](https://img.shields.io/npm/v/@supernova123/defillama-mcp-server)](https://www.npmjs.com/package/@supernova123/defillama-mcp-server)
8
9
 
9
10
  ## What is this?
10
11
 
11
- An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that gives AI assistants and agents access to DeFi Llama's free DeFi data API — protocol TVL, chain TVL, yield pools, stablecoins, cross-chain bridges, DEX volumes, and protocol fees — through natural language.
12
+ An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that gives AI assistants access to [DeFi Llama](https://defillama.com)'s comprehensive free DeFi data — protocol TVL, yield rates, stablecoin stats, bridge volumes, DEX activity, and protocol fees.
12
13
 
13
- Use it with **Claude Desktop**, **Cursor**, **Windsurf**, **Cline**, **Continue**, or any MCP-compatible client to ask questions about DeFi protocols, track TVL movements, compare yields, and explore the on-chain economy.
14
+ Use it with **Claude Desktop**, **Cursor**, **Windsurf**, **Cline**, **Continue**, or any MCP-compatible client to query on-chain data, compare protocols, and analyze DeFi trends through natural language.
14
15
 
15
16
  ## Why use this?
16
17
 
17
- - **No API key required** — DeFi Llama is a free public API
18
- - **8 built-in tools** — covers the most common DeFi data queries
19
- - **Clean markdown output** — results read naturally in chat
20
- - **Rate-limited automatically** — polite 500ms throttle across all endpoints
18
+ - **No API key required** — DeFi Llama's API is free and open
19
+ - **8 built-in tools** — covers the most important DeFi data queries
20
+ - **Clean formatted output** — TVL as `$1.2B`, yields as `+4.52%`, easy to read
21
+ - **Rate-limited automatically** — polite 500ms spacing between calls
22
+ - **No wallet connection needed** — reads public data only, no transactions
21
23
 
22
24
  ## Tools
23
25
 
24
26
  | Tool | Description |
25
27
  |------|-------------|
26
- | `search_protocols` | Search DeFi protocols by name — returns top results with TVL, chains, category |
27
- | `get_protocol_tvl` | Get detailed TVL breakdown for a specific protocol (chain distribution, history, description) |
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 |
30
- | `get_stablecoins` | Get stablecoin market cap data and rankings |
31
- | `get_bridges` | Get cross-chain bridge TVL and volume data |
32
- | `get_dex_volumes` | Get DEX trading volumes across chains |
33
- | `get_protocol_fees` | Get protocol fee and revenue data |
28
+ | `search_protocols` | Search for DeFi protocols by name |
29
+ | `get_protocol_tvl` | Get detailed TVL breakdown for a protocol (chains, history, category) |
30
+ | `get_tvl_by_chain` | Get total TVL for each blockchain |
31
+ | `get_yields` | Get current yield rates across pools (APY, TVL, chain) |
32
+ | `get_stablecoins` | Get stablecoin stats (market cap, peg, chain distribution) |
33
+ | `get_bridges` | Get bridge volume and TVL data |
34
+ | `get_dex_volumes` | Get DEX trading volumes |
35
+ | `get_protocol_fees` | Get protocol fee revenue data |
34
36
 
35
37
  ## Quick Start
36
38
 
37
39
  ### 1. Install
38
40
 
39
41
  ```bash
40
- npm install -g defillama-mcp-server
42
+ npm install -g @supernova123/defillama-mcp-server
41
43
  ```
42
44
 
43
45
  Or run directly with npx:
44
46
 
45
47
  ```bash
46
- npx -y defillama-mcp-server
48
+ npx -y @supernova123/defillama-mcp-server
47
49
  ```
48
50
 
49
51
  ### 2. Configure your MCP client
@@ -55,7 +57,7 @@ Add to your MCP client config (e.g. `claude_desktop_config.json`):
55
57
  "mcpServers": {
56
58
  "defillama": {
57
59
  "command": "npx",
58
- "args": ["-y", "defillama-mcp-server"]
60
+ "args": ["-y", "@supernova123/defillama-mcp-server"]
59
61
  }
60
62
  }
61
63
  }
@@ -77,75 +79,108 @@ Or with global install:
77
79
 
78
80
  Ask your AI assistant things like:
79
81
 
80
- - "What are the top DeFi protocols by TVL on Ethereum?"
81
- - "Search for Aave and show me the chain breakdown"
82
- - "What's the total TVL on Arbitrum?"
83
- - "Show me the highest yield stablecoin pools on Base with at least $10M TVL"
84
- - "List the top 5 stablecoins by market cap"
82
+ - "What's the current TVL of Aave?"
83
+ - "Which chains have the highest TVL?"
84
+ - "Show me the top 10 yield pools by APY"
85
+ - "What are the biggest stablecoins by market cap?"
86
+ - "How much volume did Uniswap handle this week?"
85
87
  - "Which bridges have the most TVL?"
86
- - "Show me DEX trading volumes for the last 24h"
87
- - "What are the protocols with the most fees?"
88
+ - "What are the highest-fee protocols in DeFi?"
89
+ - "Compare the TVL of Lido vs Rocket Pool"
90
+
91
+ ## Use Cases
92
+
93
+ ### Portfolio Research
94
+
95
+ Query protocol-specific data before making investment decisions:
96
+
97
+ - "What's Aave's TVL breakdown by chain?"
98
+ - "How has MakerDAO's TVL changed over time?"
99
+ - "What category is Pendle in and what's its TVL?"
100
+
101
+ ### Yield Farming
102
+
103
+ Find the best yield opportunities across DeFi:
104
+
105
+ - "What are the top 5 stablecoin yield pools?"
106
+ - "Show me ETH staking yields above 4%"
107
+ - "Which pools on Arbitrum have the highest APY?"
108
+
109
+ ### Market Intelligence
110
+
111
+ Track DeFi trends and competition:
112
+
113
+ - "Which DEX had the highest volume last month?"
114
+ - "How much are the top protocols earning in fees?"
115
+ - "What's the total stablecoin market cap?"
116
+
117
+ ### Chain Analysis
118
+
119
+ Compare blockchain ecosystems:
120
+
121
+ - "How does Solana's TVL compare to Ethereum?"
122
+ - "Which chain has the most bridge inflows?"
123
+ - "What's the TVL distribution across all chains?"
88
124
 
89
125
  ## Example Output
90
126
 
91
- ### `search_protocols`
127
+ ### `get_protocol_tvl`
92
128
 
93
129
  ```
94
- Top 5 protocols matching "aave" (by TVL):
95
-
96
- - **Aave** (AAVE) — TVL: $12.45B | Category: Lending | Chains: Ethereum, Arbitrum, Polygon, Base, Optimism, +6 | Slug: `aave`
97
- - **Aave v2** (AAVE) — TVL: $4.20B | Category: Lending | Chains: Ethereum, Polygon, Avalanche | Slug: `aave-v2`
98
- - **Aave v3** (AAVE) — TVL: $8.10B | Category: Lending | Chains: Ethereum, Arbitrum, Polygon, Base, Optimism | Slug: `aave-v3`
130
+ Protocol: Aave
131
+ Category: Lending
132
+ Description: Decentralized lending & borrowing protocol
133
+
134
+ TVL by Chain:
135
+ Ethereum: $12.4B
136
+ Polygon: $892M
137
+ Arbitrum: $2.1B
138
+ Optimism: $645M
139
+ Avalanche: $312M
140
+ ...
141
+
142
+ Total TVL: $16.3B
99
143
  ```
100
144
 
101
145
  ### `get_yields`
102
146
 
103
147
  ```
104
- Top 5 yield pools (chain: Ethereum | min TVL: $10.00M | min APY: 0%):
148
+ Top Yield Pools:
105
149
 
106
- - **Lido** stETH on Ethereum 🟢 | APY: 3.42% (3.42% base) | TVL: $23.45B
107
- - **Aave v3** — USDC on Ethereum 🟢 | APY: 4.85% (1.50% base + 3.35% reward) | TVL: $1.85B
108
- - **Compound v3** — USDC on Ethereum 🟢 | APY: 5.12% (3.20% base + 1.92% reward) | TVL: $890.45M
150
+ | Pool | Chain | Project | APY | TVL | Category |
151
+ |------|-------|---------|-----|-----|----------|
152
+ | USDC-Eth | Ethereum | Convex | 12.4% | $45M | Yield |
153
+ | stETH | Ethereum | Lido | 3.8% | $14.2B | Liquid Staking |
154
+ | USDT | Tron | JustLend | 8.2% | $580M | Lending |
109
155
  ```
110
156
 
111
- ### `get_tvl_by_chain`
157
+ ### `get_stablecoins`
112
158
 
113
159
  ```
114
- Ethereum — Chain TVL
115
-
116
- - **Total TVL:** $115.32B
117
- - **Native Token:** ETH
118
- - **CoinGecko ID:** ethereum
119
-
120
- ### Top Tokens by TVL
121
- - **ETH** (Ether): $67.23B
122
- - **USDC** (USD Coin): $4.12B
123
- - **USDT** (Tether): $3.45B
124
- - **WBTC** (Wrapped BTC): $2.89B
125
- - **WSTETH** (Wrapped stETH): $2.34B
160
+ Stablecoin Market:
161
+
162
+ | Name | Symbol | Peg | Market Cap | Chains |
163
+ |------|--------|-----|------------|--------|
164
+ | Tether | USDT | $1.00 | $112B | 12 |
165
+ | USD Coin | USDC | $1.00 | $34B | 8 |
166
+ | DAI | DAI | $1.00 | $5.3B | 1 |
126
167
  ```
127
168
 
128
169
  ## Requirements
129
170
 
130
171
  - Node.js 18+
131
- - No API key needed (DeFi Llama is a free public API)
172
+ - No API key needed (DeFi Llama API is free)
132
173
 
133
174
  ## Rate Limits
134
175
 
135
- DeFi Llama doesn't publish hard rate limits, but the server automatically throttles requests to ~2 calls/second (500ms minimum interval) to be a polite citizen. The `/protocols` and `/pools` endpoints are large (multi-MB), so the throttle also helps avoid unnecessary load.
136
-
137
- ## Data Sources
138
-
139
- - **Protocols & TVL:** `https://api.llama.fi`
140
- - **Yields:** `https://yields.llama.fi`
141
- - **Stablecoins:** `https://stablecoins.llama.fi`
176
+ The server automatically spaces requests 500ms apart to respect DeFi Llama's API. No hard rate limits are published, but this conservative spacing handles heavy use without issues.
142
177
 
143
- All endpoints are free and require no authentication. Full API documentation: https://defillama.com/docs/api
178
+ ## Contributing
144
179
 
145
- ## Development
180
+ Contributions welcome. The server wraps DeFi Llama's public API, so adding new tools means mapping new endpoints.
146
181
 
147
182
  ```bash
148
- git clone https://github.com/nova/defillama-mcp-server.git
183
+ git clone https://github.com/friendlygeorge/defillama-mcp-server.git
149
184
  cd defillama-mcp-server
150
185
  npm install
151
186
  npm run build
package/glama.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://glama.ai/mcp/schemas/server.json",
3
+ "maintainers": [
4
+ "friendlygeorge"
5
+ ]
6
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@supernova123/defillama-mcp-server",
3
- "version": "1.0.0",
4
- "description": "MCP server for DeFi Llama \u2014 free DeFi TVL, yields, stablecoins, bridges, DEX, and fee data for AI assistants",
3
+ "version": "1.0.3",
4
+ "description": "MCP server for DeFi Llama free DeFi TVL, yields, stablecoins, bridges, DEX, and fee data for AI assistants",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
@@ -33,8 +33,8 @@
33
33
  "zod": "^3.23.0"
34
34
  },
35
35
  "devDependencies": {
36
- "typescript": "^5.5.0",
37
- "@types/node": "^20.0.0"
36
+ "@types/node": "^20.0.0",
37
+ "typescript": "^5.5.0"
38
38
  },
39
39
  "mcpName": "io.github.friendlygeorge/defillama-mcp-server",
40
40
  "repository": {
@@ -47,5 +47,15 @@
47
47
  },
48
48
  "publishConfig": {
49
49
  "access": "public"
50
- }
50
+ },
51
+ "topics": [
52
+ "mcp",
53
+ "mcp-server",
54
+ "model-context-protocol",
55
+ "defi",
56
+ "tvl",
57
+ "yields",
58
+ "defillama",
59
+ "ai"
60
+ ]
51
61
  }
package/dist/index.d.ts DELETED
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * DeFi Llama MCP Server
4
- *
5
- * Connect AI assistants to DeFi Llama's free DeFi data API.
6
- * Query protocol TVL, chain TVL, yields, stablecoins, bridges,
7
- * DEX volumes, and protocol fees through the Model Context Protocol.
8
- *
9
- * Works with Claude Desktop, Cursor, Windsurf, Cline, and any MCP client.
10
- */
11
- export {};
package/dist/index.js DELETED
@@ -1,413 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * DeFi Llama MCP Server
4
- *
5
- * Connect AI assistants to DeFi Llama's free DeFi data API.
6
- * Query protocol TVL, chain TVL, yields, stablecoins, bridges,
7
- * DEX volumes, and protocol fees through the Model Context Protocol.
8
- *
9
- * Works with Claude Desktop, Cursor, Windsurf, Cline, and any MCP client.
10
- */
11
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
- import { z } from "zod";
14
- const LLAMA_BASE = "https://api.llama.fi";
15
- const YIELDS_BASE = "https://yields.llama.fi";
16
- const STABLECOINS_BASE = "https://stablecoins.llama.fi";
17
- // Rate limiter: DeFi Llama doesn't publish hard limits, but be polite.
18
- // ~500ms between calls is safe and well below any reasonable limit.
19
- let lastCall = 0;
20
- const MIN_INTERVAL = 500;
21
- async function rateLimitedFetch(url) {
22
- const now = Date.now();
23
- const wait = MIN_INTERVAL - (now - lastCall);
24
- if (wait > 0) {
25
- await new Promise((r) => setTimeout(r, wait));
26
- }
27
- lastCall = Date.now();
28
- const res = await fetch(url, {
29
- headers: { Accept: "application/json" },
30
- });
31
- if (!res.ok) {
32
- throw new Error(`DeFi Llama API error: ${res.status} ${res.statusText}`);
33
- }
34
- return res.json();
35
- }
36
- function formatTVL(value) {
37
- if (value == null || isNaN(value))
38
- return "N/A";
39
- if (value >= 1e12)
40
- return `$${(value / 1e12).toFixed(2)}T`;
41
- if (value >= 1e9)
42
- return `$${(value / 1e9).toFixed(2)}B`;
43
- if (value >= 1e6)
44
- return `$${(value / 1e6).toFixed(2)}M`;
45
- if (value >= 1e3)
46
- return `$${(value / 1e3).toFixed(2)}K`;
47
- return `$${value.toFixed(2)}`;
48
- }
49
- function formatPct(value) {
50
- if (value == null || isNaN(value))
51
- return "N/A";
52
- return `${value >= 0 ? "+" : ""}${value.toFixed(2)}%`;
53
- }
54
- // Create server
55
- const server = new McpServer({
56
- name: "defillama",
57
- version: "1.0.0",
58
- });
59
- // ── Tool: search_protocols ──
60
- server.tool("search_protocols", "Search DeFi protocols by name. Returns top results with TVL, chains, and category.", {
61
- query: z.string().describe("Search term (e.g. 'aave', 'uniswap', 'lido')"),
62
- limit: z.number().optional().default(10).describe("Max results to return (default 10)"),
63
- }, async ({ query, limit }) => {
64
- try {
65
- const data = await rateLimitedFetch(`${LLAMA_BASE}/protocols`);
66
- if (!Array.isArray(data)) {
67
- return { content: [{ type: "text", text: "Unexpected API response from DeFi Llama." }] };
68
- }
69
- const q = query.toLowerCase().trim();
70
- const matches = data
71
- .filter((p) => {
72
- const name = (p.name || "").toLowerCase();
73
- const symbol = (p.symbol || "").toLowerCase();
74
- const slug = (p.slug || "").toLowerCase();
75
- return name.includes(q) || symbol.includes(q) || slug.includes(q);
76
- })
77
- .sort((a, b) => (b.tvl || 0) - (a.tvl || 0))
78
- .slice(0, limit);
79
- if (matches.length === 0) {
80
- return { content: [{ type: "text", text: `No protocols found for "${query}".` }] };
81
- }
82
- const lines = matches.map((p) => {
83
- const chains = (p.chains || []).slice(0, 4).join(", ");
84
- const more = (p.chains || []).length > 4 ? ` +${p.chains.length - 4}` : "";
85
- const category = p.category || "Unknown";
86
- return `- **${p.name}** (${(p.symbol || "").toUpperCase()}) — TVL: ${formatTVL(p.tvl)} | Category: ${category} | Chains: ${chains}${more} | Slug: \`${p.slug}\``;
87
- });
88
- return {
89
- content: [{
90
- type: "text",
91
- text: `**Top ${matches.length} protocols matching "${query}" (by TVL):**\n\n${lines.join("\n")}`,
92
- }],
93
- };
94
- }
95
- catch (e) {
96
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
97
- }
98
- });
99
- // ── Tool: get_protocol_tvl ──
100
- server.tool("get_protocol_tvl", "Get detailed TVL breakdown for a specific DeFi protocol (chain distribution, TVL history, category, description).", {
101
- slug: z.string().describe("Protocol slug (e.g. 'aave', 'uniswap', 'lido'). Use search_protocols to find slugs."),
102
- }, async ({ slug }) => {
103
- try {
104
- const data = await rateLimitedFetch(`${LLAMA_BASE}/protocol/${encodeURIComponent(slug)}`);
105
- const lines = [
106
- `**${data.name}** (${(data.symbol || "").toUpperCase()})`,
107
- "",
108
- `- **Category:** ${data.category || "Unknown"}`,
109
- `- **Chain:** ${data.chain || "Multi-chain"}`,
110
- `- **Current TVL:** ${formatTVL(data.tvl)}`,
111
- `- **mcap / TVL:** ${data.mcap || "N/A"}`,
112
- `- **Website:** ${data.url || "N/A"}`,
113
- ];
114
- // Chain breakdown
115
- if (Array.isArray(data.currentChainTvls) && data.currentChainTvls.length > 0) {
116
- const chainLines = data.currentChainTvls
117
- .map((c) => ` - ${c.name}: ${formatTVL(c.tvl)}`)
118
- .join("\n");
119
- lines.push("", "### TVL by Chain", chainLines);
120
- }
121
- // Recent TVL change
122
- if (data.change_1d != null || data.change_7d != null) {
123
- lines.push("", "### TVL Change", `- **24h:** ${formatPct(data.change_1d)}`, `- **7d:** ${formatPct(data.change_7d)}`);
124
- }
125
- // Description
126
- if (data.description) {
127
- const desc = String(data.description).replace(/<[^>]*>/g, "").slice(0, 400);
128
- lines.push("", "### Description", desc + (String(data.description).length > 400 ? "..." : ""));
129
- }
130
- // Recent TVL history (sample)
131
- if (data.tvl && Array.isArray(data.tvl) && data.tvl.length > 0) {
132
- const hist = data.tvl;
133
- const step = Math.max(1, Math.floor(hist.length / 10));
134
- const samples = hist.filter((_, i) => i % step === 0);
135
- const histLines = samples
136
- .map(([ts, v]) => `${new Date(ts * 1000).toISOString().split("T")[0]}: ${formatTVL(v)}`)
137
- .join("\n");
138
- lines.push("", "### TVL History (sampled)", histLines);
139
- }
140
- return { content: [{ type: "text", text: lines.join("\n") }] };
141
- }
142
- catch (e) {
143
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
144
- }
145
- });
146
- // ── Tool: get_tvl_by_chain ──
147
- server.tool("get_tvl_by_chain", "Get total TVL for a specific chain (Ethereum, Arbitrum, Base, Solana, etc.) plus chain-level token breakdown.", {
148
- chain: z.string().describe("Chain name (e.g. 'Ethereum', 'Arbitrum', 'Base', 'Solana', 'BSC', 'Polygon')"),
149
- }, async ({ chain }) => {
150
- try {
151
- const data = await rateLimitedFetch(`${LLAMA_BASE}/v2/chains`);
152
- if (!Array.isArray(data)) {
153
- return { content: [{ type: "text", text: "Unexpected API response from DeFi Llama." }] };
154
- }
155
- const c = chain.toLowerCase().trim();
156
- const match = data.find((x) => (x.name || "").toLowerCase() === c)
157
- || data.find((x) => (x.name || "").toLowerCase().includes(c));
158
- if (!match) {
159
- return { content: [{ type: "text", text: `Chain "${chain}" not found. Try one of: ${data.slice(0, 15).map((x) => x.name).join(", ")}...` }] };
160
- }
161
- const lines = [
162
- `**${match.name} — Chain TVL**`,
163
- "",
164
- `- **Total TVL:** ${formatTVL(match.tvl)}`,
165
- ];
166
- if (match.tokenSymbol)
167
- lines.push(`- **Native Token:** ${match.tokenSymbol}`);
168
- if (match.gecko_id)
169
- lines.push(`- **CoinGecko ID:** ${match.gecko_id}`);
170
- if (match.cmcdId)
171
- lines.push(`- **CMC ID:** ${match.cmcdId}`);
172
- // Top tokens on this chain by TVL
173
- if (Array.isArray(match.tokens) && match.tokens.length > 0) {
174
- const topTokens = match.tokens
175
- .filter((t) => t.tvl != null && t.tvl > 0)
176
- .sort((a, b) => b.tvl - a.tvl)
177
- .slice(0, 10);
178
- if (topTokens.length > 0) {
179
- const tokenLines = topTokens
180
- .map((t) => `- **${t.symbol || t.name || "?"}** (${t.name || ""}): ${formatTVL(t.tvl)}`)
181
- .join("\n");
182
- lines.push("", "### Top Tokens by TVL", tokenLines);
183
- }
184
- }
185
- // gecko_id link hint
186
- if (match.gecko_id) {
187
- lines.push("", `_View chart: https://defillama.com/chain/${match.name}_${match.gecko_id}_Coingecko__${match.name}_Coingecko_?_${match.geckoId || ""}_`);
188
- }
189
- return { content: [{ type: "text", text: lines.join("\n") }] };
190
- }
191
- catch (e) {
192
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
193
- }
194
- });
195
- // ── Tool: get_yields ──
196
- server.tool("get_yields", "Get yield/APY data for lending pools and staking across DeFi protocols. Filter by chain, project, or min TVL.", {
197
- chain: z.string().optional().describe("Filter by chain (e.g. 'Ethereum', 'Arbitrum', 'Base', 'Solana')"),
198
- project: z.string().optional().describe("Filter by project (e.g. 'Aave', 'Lido', 'Compound')"),
199
- min_tvl: z.number().optional().default(1_000_000).describe("Minimum TVL in USD (default $1M)"),
200
- min_apy: z.number().optional().default(0).describe("Minimum APY % (default 0)"),
201
- limit: z.number().optional().default(20).describe("Max results (default 20)"),
202
- }, async ({ chain, project, min_tvl, min_apy, limit }) => {
203
- try {
204
- const data = await rateLimitedFetch(`${YIELDS_BASE}/pools`);
205
- if (!data || !Array.isArray(data.data)) {
206
- return { content: [{ type: "text", text: "Unexpected API response from DeFi Llama yields." }] };
207
- }
208
- const chainFilter = chain ? chain.toLowerCase().trim() : null;
209
- const projectFilter = project ? project.toLowerCase().trim() : null;
210
- const matches = data.data
211
- .filter((p) => {
212
- if (p.tvlUsd == null || p.tvlUsd < min_tvl)
213
- return false;
214
- if (p.apy == null || p.apy < min_apy)
215
- return false;
216
- if (p.ilRisk === "yes")
217
- return false; // Skip impermanent-loss pools by default
218
- if (chainFilter && !(p.chain || "").toLowerCase().includes(chainFilter))
219
- return false;
220
- if (projectFilter && !(p.project || "").toLowerCase().includes(projectFilter))
221
- return false;
222
- return true;
223
- })
224
- .sort((a, b) => (b.tvlUsd || 0) - (a.tvlUsd || 0))
225
- .slice(0, limit);
226
- if (matches.length === 0) {
227
- return { content: [{ type: "text", text: `No yield pools matched (chain: ${chain || "any"}, project: ${project || "any"}, min TVL $${min_tvl.toLocaleString()}, min APY ${min_apy}%).` }] };
228
- }
229
- const lines = matches.map((p) => {
230
- const apy = p.apy != null ? `${p.apy.toFixed(2)}%` : "N/A";
231
- const apyBase = p.apyBase != null ? `${p.apyBase.toFixed(2)}% base` : "";
232
- const apyReward = p.apyReward != null ? `+ ${p.apyReward.toFixed(2)}% reward` : "";
233
- const apyDetails = [apyBase, apyReward].filter(Boolean).join(" ");
234
- const tvl = formatTVL(p.tvlUsd);
235
- const symbol = p.symbol || "?";
236
- const projectName = p.project || "?";
237
- const chainName = p.chain || "?";
238
- const stable = p.stablecoin ? " 🟢" : "";
239
- return `- **${projectName}** — ${symbol} on ${chainName}${stable} | APY: ${apy}${apyDetails ? ` (${apyDetails})` : ""} | TVL: ${tvl}`;
240
- });
241
- const headerParts = [];
242
- if (chain)
243
- headerParts.push(`chain: ${chain}`);
244
- if (project)
245
- headerParts.push(`project: ${project}`);
246
- headerParts.push(`min TVL: ${formatTVL(min_tvl)}`);
247
- headerParts.push(`min APY: ${min_apy}%`);
248
- const header = headerParts.join(" | ");
249
- return {
250
- content: [{
251
- type: "text",
252
- text: `**Top ${matches.length} yield pools (${header}):**\n\n${lines.join("\n")}`,
253
- }],
254
- };
255
- }
256
- catch (e) {
257
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
258
- }
259
- });
260
- // ── Tool: get_stablecoins ──
261
- server.tool("get_stablecoins", "Get stablecoin market cap data and rankings (circulating supply, chains, prices).", {
262
- limit: z.number().optional().default(25).describe("Max results (default 25)"),
263
- }, async ({ limit }) => {
264
- try {
265
- const data = await rateLimitedFetch(`${STABLECOINS_BASE}/stablecoins`);
266
- if (!Array.isArray(data)) {
267
- return { content: [{ type: "text", text: "Unexpected API response from DeFi Llama stablecoins." }] };
268
- }
269
- const sorted = [...data]
270
- .filter((s) => s.circulating != null)
271
- .sort((a, b) => (b.circulating?.usd || 0) - (a.circulating?.usd || 0))
272
- .slice(0, limit);
273
- if (sorted.length === 0) {
274
- return { content: [{ type: "text", text: "No stablecoins returned by API." }] };
275
- }
276
- const lines = sorted.map((s, i) => {
277
- const mcap = formatTVL(s.circulating?.usd);
278
- const price = s.price != null ? `$${Number(s.price).toFixed(4)}` : "N/A";
279
- const symbol = s.symbol || s.name || "?";
280
- const pegType = s.pegType || "USD";
281
- const chains = Array.isArray(s.chains) ? s.chains.length : 0;
282
- return `${i + 1}. **${symbol}** (${pegType}) — MCap: ${mcap} | Price: ${price} | Chains: ${chains}`;
283
- });
284
- const totalMcap = sorted.reduce((acc, s) => acc + (s.circulating?.usd || 0), 0);
285
- return {
286
- content: [{
287
- type: "text",
288
- text: `**Top ${sorted.length} Stablecoins by Market Cap (combined: ${formatTVL(totalMcap)}):**\n\n${lines.join("\n")}`,
289
- }],
290
- };
291
- }
292
- catch (e) {
293
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
294
- }
295
- });
296
- // ── Tool: get_bridges ──
297
- server.tool("get_bridges", "Get bridge TVL and volume data — cross-chain bridges ranked by total value locked.", {
298
- limit: z.number().optional().default(20).describe("Max results (default 20)"),
299
- }, async ({ limit }) => {
300
- try {
301
- const data = await rateLimitedFetch(`${LLAMA_BASE}/v2/bridges`);
302
- const bridges = Array.isArray(data) ? data : (data.bridges || []);
303
- if (!Array.isArray(bridges)) {
304
- return { content: [{ type: "text", text: "Unexpected API response from DeFi Llama bridges." }] };
305
- }
306
- const sorted = [...bridges]
307
- .filter((b) => b.tvl != null && b.tvl > 0)
308
- .sort((a, b) => (b.tvl || 0) - (a.tvl || 0))
309
- .slice(0, limit);
310
- if (sorted.length === 0) {
311
- return { content: [{ type: "text", text: "No bridge data returned." }] };
312
- }
313
- const lines = sorted.map((b, i) => {
314
- const tvl = formatTVL(b.tvl);
315
- const dayVol = b.lastDayVolume != null ? `${formatTVL(b.lastDayVolume)} 24h` : "";
316
- const weekVol = b.weeklyVolume != null ? `${formatTVL(b.weeklyVolume)} 7d` : "";
317
- const volDetails = [dayVol, weekVol].filter(Boolean).join(" / ");
318
- return `${i + 1}. **${b.name || b.displayName || "Unknown"}** — TVL: ${tvl}${volDetails ? ` | Vol: ${volDetails}` : ""}`;
319
- });
320
- const totalTvl = sorted.reduce((acc, b) => acc + (b.tvl || 0), 0);
321
- return {
322
- content: [{
323
- type: "text",
324
- text: `**Top ${sorted.length} Bridges by TVL (combined: ${formatTVL(totalTvl)}):**\n\n${lines.join("\n")}`,
325
- }],
326
- };
327
- }
328
- catch (e) {
329
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
330
- }
331
- });
332
- // ── Tool: get_dex_volumes ──
333
- server.tool("get_dex_volumes", "Get DEX trading volumes across chains — decentralized exchanges ranked by 24h/7d volume.", {
334
- limit: z.number().optional().default(25).describe("Max results (default 25)"),
335
- }, async ({ limit }) => {
336
- try {
337
- const data = await rateLimitedFetch(`${LLAMA_BASE}/overview/dexs`);
338
- const dexs = (data && (data.protocols || data)) || [];
339
- if (!Array.isArray(dexs)) {
340
- return { content: [{ type: "text", text: "Unexpected API response from DeFi Llama dexs." }] };
341
- }
342
- const sorted = [...dexs]
343
- .filter((d) => d.total24h != null)
344
- .sort((a, b) => (b.total24h || 0) - (a.total24h || 0))
345
- .slice(0, limit);
346
- if (sorted.length === 0) {
347
- return { content: [{ type: "text", text: "No DEX data returned." }] };
348
- }
349
- const lines = sorted.map((d, i) => {
350
- const v24 = formatTVL(d.total24h);
351
- const v7d = d.total7d != null ? formatTVL(d.total7d) : "N/A";
352
- const v30d = d.total30d != null ? formatTVL(d.total30d) : "N/A";
353
- const change = d.change_1d != null ? formatPct(d.change_1d) : "N/A";
354
- return `${i + 1}. **${d.name || d.displayName || "Unknown"}** — 24h: ${v24} | 7d: ${v7d} | 30d: ${v30d} | Δ24h: ${change}`;
355
- });
356
- const total24h = sorted.reduce((acc, d) => acc + (d.total24h || 0), 0);
357
- return {
358
- content: [{
359
- type: "text",
360
- text: `**Top ${sorted.length} DEXes by 24h Volume (combined: ${formatTVL(total24h)}):**\n\n${lines.join("\n")}`,
361
- }],
362
- };
363
- }
364
- catch (e) {
365
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
366
- }
367
- });
368
- // ── Tool: get_protocol_fees ──
369
- server.tool("get_protocol_fees", "Get protocol fee and revenue data — protocols ranked by 24h/7d/30d fees and revenue.", {
370
- limit: z.number().optional().default(25).describe("Max results (default 25)"),
371
- }, async ({ limit }) => {
372
- try {
373
- const data = await rateLimitedFetch(`${LLAMA_BASE}/overview/fees`);
374
- const protos = (data && (data.protocols || data)) || [];
375
- if (!Array.isArray(protos)) {
376
- return { content: [{ type: "text", text: "Unexpected API response from DeFi Llama fees." }] };
377
- }
378
- const sorted = [...protos]
379
- .filter((p) => p.total24h != null)
380
- .sort((a, b) => (b.total24h || 0) - (a.total24h || 0))
381
- .slice(0, limit);
382
- if (sorted.length === 0) {
383
- return { content: [{ type: "text", text: "No fee data returned." }] };
384
- }
385
- const lines = sorted.map((p, i) => {
386
- const f24 = formatTVL(p.total24h);
387
- const f7d = p.total7d != null ? formatTVL(p.total7d) : "N/A";
388
- const f30d = p.total30d != null ? formatTVL(p.total30d) : "N/A";
389
- const rev = p.revenue24h != null ? `${formatTVL(p.revenue24h)} rev` : "";
390
- return `${i + 1}. **${p.name || p.displayName || "Unknown"}** — Fees 24h: ${f24} | 7d: ${f7d} | 30d: ${f30d}${rev ? ` | ${rev}` : ""}`;
391
- });
392
- const total24h = sorted.reduce((acc, p) => acc + (p.total24h || 0), 0);
393
- return {
394
- content: [{
395
- type: "text",
396
- text: `**Top ${sorted.length} Protocols by 24h Fees (combined: ${formatTVL(total24h)}):**\n\n${lines.join("\n")}`,
397
- }],
398
- };
399
- }
400
- catch (e) {
401
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
402
- }
403
- });
404
- // Start server
405
- async function main() {
406
- const transport = new StdioServerTransport();
407
- await server.connect(transport);
408
- }
409
- main().catch((err) => {
410
- console.error("Fatal error:", err);
411
- process.exit(1);
412
- });
413
- //# sourceMappingURL=index.js.map