defi-mcp 0.1.0__tar.gz

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.
defi_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthieu Ratsitohery
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: defi-mcp
3
+ Version: 0.1.0
4
+ Summary: An MCP server giving AI agents access to DeFi data on EVM chains.
5
+ Author-email: Matthieu Ratsitohery <matiosera3@gmail.com>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/matiosera3-ops/defi-mcp
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: mcp>=1.0.0
12
+ Requires-Dist: web3>=7.0.0
13
+ Requires-Dist: python-dotenv>=1.0.0
14
+ Dynamic: license-file
15
+
16
+ # defi-mcp
17
+
18
+ An MCP (Model Context Protocol) server that gives AI agents direct, on-chain access to DeFi data on EVM chains — wallet balances, Aave v3 lending positions, and live token prices, with no centralized API in the middle.
19
+
20
+ **Status:** Live and functional. 4 tools available today, more on the roadmap.
21
+
22
+ ## Why
23
+
24
+ Most "DeFi + AI" integrations route through a centralized API that can rate-limit, paywall, or simply disappear. `defi-mcp` talks directly to the chain (via your own RPC provider) and to first-party oracles (Chainlink), so the data your AI agent gets is exactly what's on-chain — verifiable, and not dependent on a third-party API staying online.
25
+
26
+ ## Tools
27
+
28
+ ### `get_token_balance`
29
+ Reads the ERC-20 balance of any wallet address for a supported token.
30
+
31
+ Supported tokens on Polygon: `USDC`, `USDC.e`, `USDT`, `WETH`, `WMATIC`, `WBTC`, `DAI`, `AAVE`.
32
+
33
+ ```json
34
+ // get_token_balance(address="0x...", token_symbol="USDC", chain="polygon")
35
+ {
36
+ "address": "0x...",
37
+ "token": "USDC",
38
+ "chain": "polygon",
39
+ "balance": 268.21,
40
+ "raw_balance": "268210000",
41
+ "decimals": 6
42
+ }
43
+ ```
44
+
45
+ ### `get_aave_position`
46
+ Reads a user's full Aave v3 lending/borrowing position: collateral, debt, available borrowing power, and health factor — straight from the Aave v3 Pool contract.
47
+
48
+ ```json
49
+ // get_aave_position(address="0x...", chain="polygon")
50
+ {
51
+ "address": "0x...",
52
+ "chain": "polygon",
53
+ "total_collateral_usd": 969.78,
54
+ "total_debt_usd": 288.16,
55
+ "available_borrows_usd": 484.86,
56
+ "liquidation_threshold_pct": 82.79,
57
+ "ltv_pct": 79.71,
58
+ "health_factor": 2.79,
59
+ "has_active_position": true
60
+ }
61
+ ```
62
+
63
+ A `health_factor` below `1.0` means the position is at risk of liquidation. A `null` health factor means the user has no outstanding debt.
64
+
65
+ ### `get_token_price`
66
+ Reads the current USD price of a token directly from a Chainlink price feed — the same oracle DeFi protocols use internally, so the price is consistent with what `get_aave_position` reports.
67
+
68
+ Supported on Polygon: `ETH`/`WETH`, `MATIC`/`WMATIC`, `WBTC`, `USDC`, `USDT`.
69
+
70
+ ```json
71
+ // get_token_price(token_symbol="WBTC", chain="polygon")
72
+ {
73
+ "token": "WBTC",
74
+ "chain": "polygon",
75
+ "price_usd": 63835.77,
76
+ "decimals": 8,
77
+ "updated_at": 1782032816
78
+ }
79
+ ```
80
+
81
+ ### `hello`
82
+ Simple connectivity check — confirms the MCP server is reachable and responding.
83
+
84
+ ## Supported chains
85
+
86
+ - Polygon
87
+
88
+ More EVM chains are on the roadmap (see below).
89
+
90
+ ## Installation
91
+
92
+ Requires Python 3.10+ and an RPC provider API key (e.g. [Alchemy](https://www.alchemy.com/), free tier works).
93
+
94
+ ```bash
95
+ git clone https://github.com/matiosera3-ops/defi-mcp.git
96
+ cd defi-mcp
97
+ python -m venv venv
98
+
99
+ # Windows
100
+ .\venv\Scripts\Activate.ps1
101
+ # macOS/Linux
102
+ source venv/bin/activate
103
+
104
+ pip install -e .
105
+ ```
106
+
107
+ Create a `.env` file in the project root:
108
+
109
+ ```
110
+ POLYGON_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/YOUR_API_KEY
111
+ ```
112
+
113
+ ## Usage with Claude Desktop
114
+
115
+ Add this to your Claude Desktop MCP config (`claude_desktop_config.json`):
116
+
117
+ ```json
118
+ {
119
+ "mcpServers": {
120
+ "defi-mcp": {
121
+ "command": "python",
122
+ "args": ["/absolute/path/to/defi-mcp/server.py"]
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ Restart Claude Desktop, and the tools above will be available to the model.
129
+
130
+ ## Testing locally
131
+
132
+ You can test the server directly with the official [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
133
+
134
+ ```bash
135
+ npx @modelcontextprotocol/inspector python server.py
136
+ ```
137
+
138
+ ## Roadmap
139
+
140
+ - [ ] Track Uniswap v3 LP positions and impermanent loss
141
+ - [ ] Simulate swaps with realistic slippage estimates
142
+ - [ ] Monitor protocol TVLs
143
+ - [ ] Additional chains (Ethereum, Arbitrum, Base)
144
+ - [ ] `defi-mcp-cloud` — hosted tier with MEV-specific tools, caching, and higher rate limits
145
+
146
+ ## Architecture
147
+
148
+ Open-core model: this repository (MIT licensed) covers standard on-chain read tools. A separate `defi-mcp-cloud` will offer a hosted version with MEV-related tools, request caching, and managed RPC access for users who don't want to run their own infrastructure.
149
+
150
+ ## Contributing
151
+
152
+ Issues and PRs welcome. This is an early-stage project — feedback on what tools would actually be useful to you is especially valuable.
153
+
154
+ ## License
155
+
156
+ MIT
@@ -0,0 +1,141 @@
1
+ # defi-mcp
2
+
3
+ An MCP (Model Context Protocol) server that gives AI agents direct, on-chain access to DeFi data on EVM chains — wallet balances, Aave v3 lending positions, and live token prices, with no centralized API in the middle.
4
+
5
+ **Status:** Live and functional. 4 tools available today, more on the roadmap.
6
+
7
+ ## Why
8
+
9
+ Most "DeFi + AI" integrations route through a centralized API that can rate-limit, paywall, or simply disappear. `defi-mcp` talks directly to the chain (via your own RPC provider) and to first-party oracles (Chainlink), so the data your AI agent gets is exactly what's on-chain — verifiable, and not dependent on a third-party API staying online.
10
+
11
+ ## Tools
12
+
13
+ ### `get_token_balance`
14
+ Reads the ERC-20 balance of any wallet address for a supported token.
15
+
16
+ Supported tokens on Polygon: `USDC`, `USDC.e`, `USDT`, `WETH`, `WMATIC`, `WBTC`, `DAI`, `AAVE`.
17
+
18
+ ```json
19
+ // get_token_balance(address="0x...", token_symbol="USDC", chain="polygon")
20
+ {
21
+ "address": "0x...",
22
+ "token": "USDC",
23
+ "chain": "polygon",
24
+ "balance": 268.21,
25
+ "raw_balance": "268210000",
26
+ "decimals": 6
27
+ }
28
+ ```
29
+
30
+ ### `get_aave_position`
31
+ Reads a user's full Aave v3 lending/borrowing position: collateral, debt, available borrowing power, and health factor — straight from the Aave v3 Pool contract.
32
+
33
+ ```json
34
+ // get_aave_position(address="0x...", chain="polygon")
35
+ {
36
+ "address": "0x...",
37
+ "chain": "polygon",
38
+ "total_collateral_usd": 969.78,
39
+ "total_debt_usd": 288.16,
40
+ "available_borrows_usd": 484.86,
41
+ "liquidation_threshold_pct": 82.79,
42
+ "ltv_pct": 79.71,
43
+ "health_factor": 2.79,
44
+ "has_active_position": true
45
+ }
46
+ ```
47
+
48
+ A `health_factor` below `1.0` means the position is at risk of liquidation. A `null` health factor means the user has no outstanding debt.
49
+
50
+ ### `get_token_price`
51
+ Reads the current USD price of a token directly from a Chainlink price feed — the same oracle DeFi protocols use internally, so the price is consistent with what `get_aave_position` reports.
52
+
53
+ Supported on Polygon: `ETH`/`WETH`, `MATIC`/`WMATIC`, `WBTC`, `USDC`, `USDT`.
54
+
55
+ ```json
56
+ // get_token_price(token_symbol="WBTC", chain="polygon")
57
+ {
58
+ "token": "WBTC",
59
+ "chain": "polygon",
60
+ "price_usd": 63835.77,
61
+ "decimals": 8,
62
+ "updated_at": 1782032816
63
+ }
64
+ ```
65
+
66
+ ### `hello`
67
+ Simple connectivity check — confirms the MCP server is reachable and responding.
68
+
69
+ ## Supported chains
70
+
71
+ - Polygon
72
+
73
+ More EVM chains are on the roadmap (see below).
74
+
75
+ ## Installation
76
+
77
+ Requires Python 3.10+ and an RPC provider API key (e.g. [Alchemy](https://www.alchemy.com/), free tier works).
78
+
79
+ ```bash
80
+ git clone https://github.com/matiosera3-ops/defi-mcp.git
81
+ cd defi-mcp
82
+ python -m venv venv
83
+
84
+ # Windows
85
+ .\venv\Scripts\Activate.ps1
86
+ # macOS/Linux
87
+ source venv/bin/activate
88
+
89
+ pip install -e .
90
+ ```
91
+
92
+ Create a `.env` file in the project root:
93
+
94
+ ```
95
+ POLYGON_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/YOUR_API_KEY
96
+ ```
97
+
98
+ ## Usage with Claude Desktop
99
+
100
+ Add this to your Claude Desktop MCP config (`claude_desktop_config.json`):
101
+
102
+ ```json
103
+ {
104
+ "mcpServers": {
105
+ "defi-mcp": {
106
+ "command": "python",
107
+ "args": ["/absolute/path/to/defi-mcp/server.py"]
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ Restart Claude Desktop, and the tools above will be available to the model.
114
+
115
+ ## Testing locally
116
+
117
+ You can test the server directly with the official [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
118
+
119
+ ```bash
120
+ npx @modelcontextprotocol/inspector python server.py
121
+ ```
122
+
123
+ ## Roadmap
124
+
125
+ - [ ] Track Uniswap v3 LP positions and impermanent loss
126
+ - [ ] Simulate swaps with realistic slippage estimates
127
+ - [ ] Monitor protocol TVLs
128
+ - [ ] Additional chains (Ethereum, Arbitrum, Base)
129
+ - [ ] `defi-mcp-cloud` — hosted tier with MEV-specific tools, caching, and higher rate limits
130
+
131
+ ## Architecture
132
+
133
+ Open-core model: this repository (MIT licensed) covers standard on-chain read tools. A separate `defi-mcp-cloud` will offer a hosted version with MEV-related tools, request caching, and managed RPC access for users who don't want to run their own infrastructure.
134
+
135
+ ## Contributing
136
+
137
+ Issues and PRs welcome. This is an early-stage project — feedback on what tools would actually be useful to you is especially valuable.
138
+
139
+ ## License
140
+
141
+ MIT
@@ -0,0 +1,23 @@
1
+ [project]
2
+ name = "defi-mcp"
3
+ version = "0.1.0"
4
+ description = "An MCP server giving AI agents access to DeFi data on EVM chains."
5
+ authors = [{name = "Matthieu Ratsitohery", email = "matiosera3@gmail.com"}]
6
+ license = {text = "MIT"}
7
+ readme = "README.md"
8
+ requires-python = ">=3.10"
9
+ dependencies = [
10
+ "mcp>=1.0.0",
11
+ "web3>=7.0.0",
12
+ "python-dotenv>=1.0.0",
13
+ ]
14
+
15
+ [project.urls]
16
+ Repository = "https://github.com/matiosera3-ops/defi-mcp"
17
+
18
+ [build-system]
19
+ requires = ["setuptools>=61.0"]
20
+ build-backend = "setuptools.build_meta"
21
+
22
+ [tool.setuptools.packages.find]
23
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,29 @@
1
+ """Token addresses and protocol configurations per chain."""
2
+
3
+ TOKEN_ADDRESSES = {
4
+ "polygon": {
5
+ "USDC": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
6
+ "USDC.e": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
7
+ "USDT": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
8
+ "WETH": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
9
+ "WMATIC": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
10
+ "WBTC": "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6",
11
+ "DAI": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
12
+ "AAVE": "0xD6DF932A45C0f255f85145f286eA0b292B21C90B",
13
+ }
14
+ }
15
+ AAVE_POOL_ADDRESSES = {
16
+ "polygon": "0x794a61358D6845594F94dc1DB02A252b5b4814aD",
17
+ }
18
+ # Chainlink price feed addresses (token/USD), per chain.
19
+ CHAINLINK_PRICE_FEEDS = {
20
+ "polygon": {
21
+ "ETH": "0xF9680D99D6C9589e2a93a78A04A279e509205945",
22
+ "WETH": "0xF9680D99D6C9589e2a93a78A04A279e509205945",
23
+ "MATIC": "0xAB594600376Ec9fD91F8e885dADF0CE036862dE0",
24
+ "WMATIC": "0xAB594600376Ec9fD91F8e885dADF0CE036862dE0",
25
+ "WBTC": "0xDE31F8bFBD8c84b5360CFACCa3539B938dd78ae6",
26
+ "USDC": "0xfe4A8cc5b5B2366C1B58Bea3858e81843581b2F7",
27
+ "USDT": "0x0A6513e40db6EB1b165753AD52E80663aeA50545",
28
+ }
29
+ }
File without changes
@@ -0,0 +1,63 @@
1
+ """ABIs for common smart contract standards."""
2
+
3
+ ERC20_ABI = [
4
+ {
5
+ "constant": True,
6
+ "inputs": [{"name": "_owner", "type": "address"}],
7
+ "name": "balanceOf",
8
+ "outputs": [{"name": "balance", "type": "uint256"}],
9
+ "type": "function",
10
+ },
11
+ {
12
+ "constant": True,
13
+ "inputs": [],
14
+ "name": "decimals",
15
+ "outputs": [{"name": "", "type": "uint8"}],
16
+ "type": "function",
17
+ },
18
+ {
19
+ "constant": True,
20
+ "inputs": [],
21
+ "name": "symbol",
22
+ "outputs": [{"name": "", "type": "string"}],
23
+ "type": "function",
24
+ },
25
+ ]
26
+ AAVE_POOL_ABI = [
27
+ {
28
+ "inputs": [{"name": "user", "type": "address"}],
29
+ "name": "getUserAccountData",
30
+ "outputs": [
31
+ {"name": "totalCollateralBase", "type": "uint256"},
32
+ {"name": "totalDebtBase", "type": "uint256"},
33
+ {"name": "availableBorrowsBase", "type": "uint256"},
34
+ {"name": "currentLiquidationThreshold", "type": "uint256"},
35
+ {"name": "ltv", "type": "uint256"},
36
+ {"name": "healthFactor", "type": "uint256"},
37
+ ],
38
+ "stateMutability": "view",
39
+ "type": "function",
40
+ },
41
+ ]
42
+ CHAINLINK_AGGREGATOR_ABI = [
43
+ {
44
+ "inputs": [],
45
+ "name": "latestRoundData",
46
+ "outputs": [
47
+ {"name": "roundId", "type": "uint80"},
48
+ {"name": "answer", "type": "int256"},
49
+ {"name": "startedAt", "type": "uint256"},
50
+ {"name": "updatedAt", "type": "uint256"},
51
+ {"name": "answeredInRound", "type": "uint80"},
52
+ ],
53
+ "stateMutability": "view",
54
+ "type": "function",
55
+ },
56
+ {
57
+ "inputs": [],
58
+ "name": "decimals",
59
+ "outputs": [{"name": "", "type": "uint8"}],
60
+ "stateMutability": "view",
61
+ "type": "function",
62
+ },
63
+ ]
@@ -0,0 +1,21 @@
1
+ """Web3 connection management per chain."""
2
+ import os
3
+ from dotenv import load_dotenv
4
+ from web3 import Web3
5
+
6
+ load_dotenv()
7
+
8
+ RPC_URLS = {
9
+ "polygon": os.getenv("POLYGON_RPC_URL"),
10
+ }
11
+
12
+
13
+ def get_web3(chain: str) -> Web3:
14
+ """Return a Web3 instance connected to the given chain."""
15
+ rpc_url = RPC_URLS.get(chain.lower())
16
+ if not rpc_url:
17
+ raise ValueError(f"Chain '{chain}' not supported or RPC URL not configured")
18
+ return Web3(Web3.HTTPProvider(rpc_url))
19
+
20
+
21
+ SUPPORTED_CHAINS = list(RPC_URLS.keys())
File without changes
@@ -0,0 +1,73 @@
1
+ """Aave v3 user position tool."""
2
+ from web3 import Web3
3
+ from defi_mcp.lib.chains import get_web3
4
+ from defi_mcp.lib.abis import AAVE_POOL_ABI
5
+ from defi_mcp.config import AAVE_POOL_ADDRESSES
6
+
7
+ # Aave v3 base currency on Polygon is USD with 8 decimals (price feed precision).
8
+ BASE_CURRENCY_DECIMALS = 8
9
+
10
+
11
+ def get_aave_position(address: str, chain: str = "polygon") -> dict:
12
+ """Get a user's Aave v3 lending/borrowing position on a given chain.
13
+
14
+ Args:
15
+ address: The wallet address to check
16
+ chain: The blockchain to query. Default: polygon.
17
+
18
+ Returns:
19
+ A dictionary with total collateral, total debt, available borrows,
20
+ liquidation threshold, LTV, and health factor (all in USD where applicable).
21
+ """
22
+ chain = chain.lower()
23
+
24
+ if chain not in AAVE_POOL_ADDRESSES:
25
+ return {"error": f"Chain '{chain}' not supported. Supported: {list(AAVE_POOL_ADDRESSES.keys())}"}
26
+
27
+ try:
28
+ w3 = get_web3(chain)
29
+
30
+ if not Web3.is_address(address):
31
+ return {"error": f"Invalid address: {address}"}
32
+ address = Web3.to_checksum_address(address)
33
+
34
+ pool_address = Web3.to_checksum_address(AAVE_POOL_ADDRESSES[chain])
35
+ contract = w3.eth.contract(address=pool_address, abi=AAVE_POOL_ABI)
36
+
37
+ result = contract.functions.getUserAccountData(address).call()
38
+ (
39
+ total_collateral_base,
40
+ total_debt_base,
41
+ available_borrows_base,
42
+ current_liquidation_threshold,
43
+ ltv,
44
+ health_factor_raw,
45
+ ) = result
46
+
47
+ # Base amounts use the price feed's decimals (8 on Polygon's main market).
48
+ total_collateral_usd = total_collateral_base / (10 ** BASE_CURRENCY_DECIMALS)
49
+ total_debt_usd = total_debt_base / (10 ** BASE_CURRENCY_DECIMALS)
50
+ available_borrows_usd = available_borrows_base / (10 ** BASE_CURRENCY_DECIMALS)
51
+
52
+ # Health factor and LTV/liquidation threshold are 18-decimal fixed point.
53
+ # If there's no debt, Aave returns max uint256 for health factor -> treat as infinite.
54
+ max_uint256 = 2**256 - 1
55
+ if health_factor_raw == max_uint256:
56
+ health_factor = None # represents infinite / no debt
57
+ else:
58
+ health_factor = health_factor_raw / (10 ** 18)
59
+
60
+ return {
61
+ "address": address,
62
+ "chain": chain,
63
+ "total_collateral_usd": total_collateral_usd,
64
+ "total_debt_usd": total_debt_usd,
65
+ "available_borrows_usd": available_borrows_usd,
66
+ "liquidation_threshold_pct": current_liquidation_threshold / 100,
67
+ "ltv_pct": ltv / 100,
68
+ "health_factor": health_factor,
69
+ "has_active_position": total_collateral_usd > 0 or total_debt_usd > 0,
70
+ }
71
+
72
+ except Exception as e:
73
+ return {"error": f"Failed to fetch Aave position: {str(e)}"}
@@ -0,0 +1,56 @@
1
+ """Token balance tool."""
2
+ from web3 import Web3
3
+ from defi_mcp.lib.chains import get_web3
4
+ from defi_mcp.lib.abis import ERC20_ABI
5
+ from defi_mcp.config import TOKEN_ADDRESSES
6
+
7
+
8
+ def get_token_balance(address: str, token_symbol: str, chain: str = "polygon") -> dict:
9
+ """Get the balance of an ERC-20 token for a given wallet address.
10
+
11
+ Args:
12
+ address: The wallet address to check
13
+ token_symbol: The token symbol (e.g. USDC, USDT, WETH, USDC.e)
14
+ chain: The blockchain to query. Default: polygon.
15
+
16
+ Returns:
17
+ A dictionary with the address, token symbol, balance, and raw balance.
18
+ """
19
+ chain = chain.lower()
20
+
21
+ if chain not in TOKEN_ADDRESSES:
22
+ return {"error": f"Chain '{chain}' not supported. Supported: {list(TOKEN_ADDRESSES.keys())}"}
23
+
24
+ # Case-insensitive token symbol matching
25
+ token_map = {k.upper(): k for k in TOKEN_ADDRESSES[chain].keys()}
26
+ token_key = token_map.get(token_symbol.upper())
27
+
28
+ if not token_key:
29
+ supported = list(TOKEN_ADDRESSES[chain].keys())
30
+ return {"error": f"Token '{token_symbol}' not supported on {chain}. Supported: {supported}"}
31
+
32
+ try:
33
+ w3 = get_web3(chain)
34
+
35
+ if not Web3.is_address(address):
36
+ return {"error": f"Invalid address: {address}"}
37
+ address = Web3.to_checksum_address(address)
38
+
39
+ token_address = Web3.to_checksum_address(TOKEN_ADDRESSES[chain][token_key])
40
+ contract = w3.eth.contract(address=token_address, abi=ERC20_ABI)
41
+
42
+ raw_balance = contract.functions.balanceOf(address).call()
43
+ decimals = contract.functions.decimals().call()
44
+ balance = raw_balance / (10 ** decimals)
45
+
46
+ return {
47
+ "address": address,
48
+ "token": token_key,
49
+ "chain": chain,
50
+ "balance": balance,
51
+ "raw_balance": str(raw_balance),
52
+ "decimals": decimals,
53
+ }
54
+
55
+ except Exception as e:
56
+ return {"error": f"Failed to fetch balance: {str(e)}"}
@@ -0,0 +1,51 @@
1
+ """Token price tool using Chainlink price feeds."""
2
+ from web3 import Web3
3
+ from defi_mcp.lib.chains import get_web3
4
+ from defi_mcp.lib.abis import CHAINLINK_AGGREGATOR_ABI
5
+ from defi_mcp.config import CHAINLINK_PRICE_FEEDS
6
+
7
+
8
+ def get_token_price(token_symbol: str, chain: str = "polygon") -> dict:
9
+ """Get the current USD price of a token from a Chainlink price feed.
10
+
11
+ Args:
12
+ token_symbol: The token symbol (e.g. ETH, WETH, MATIC, WMATIC, WBTC, USDC, USDT)
13
+ chain: The blockchain to query. Default: polygon.
14
+
15
+ Returns:
16
+ A dictionary with the token symbol, USD price, and feed update timestamp.
17
+ """
18
+ chain = chain.lower()
19
+
20
+ if chain not in CHAINLINK_PRICE_FEEDS:
21
+ return {"error": f"Chain '{chain}' not supported. Supported: {list(CHAINLINK_PRICE_FEEDS.keys())}"}
22
+
23
+ # Case-insensitive token symbol matching
24
+ feed_map = {k.upper(): k for k in CHAINLINK_PRICE_FEEDS[chain].keys()}
25
+ feed_key = feed_map.get(token_symbol.upper())
26
+
27
+ if not feed_key:
28
+ supported = list(CHAINLINK_PRICE_FEEDS[chain].keys())
29
+ return {"error": f"Token '{token_symbol}' has no price feed on {chain}. Supported: {supported}"}
30
+
31
+ try:
32
+ w3 = get_web3(chain)
33
+ feed_address = Web3.to_checksum_address(CHAINLINK_PRICE_FEEDS[chain][feed_key])
34
+ contract = w3.eth.contract(address=feed_address, abi=CHAINLINK_AGGREGATOR_ABI)
35
+
36
+ round_data = contract.functions.latestRoundData().call()
37
+ _, answer, _, updated_at, _ = round_data
38
+
39
+ decimals = contract.functions.decimals().call()
40
+ price_usd = answer / (10 ** decimals)
41
+
42
+ return {
43
+ "token": feed_key,
44
+ "chain": chain,
45
+ "price_usd": price_usd,
46
+ "decimals": decimals,
47
+ "updated_at": updated_at,
48
+ }
49
+
50
+ except Exception as e:
51
+ return {"error": f"Failed to fetch price: {str(e)}"}
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: defi-mcp
3
+ Version: 0.1.0
4
+ Summary: An MCP server giving AI agents access to DeFi data on EVM chains.
5
+ Author-email: Matthieu Ratsitohery <matiosera3@gmail.com>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/matiosera3-ops/defi-mcp
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: mcp>=1.0.0
12
+ Requires-Dist: web3>=7.0.0
13
+ Requires-Dist: python-dotenv>=1.0.0
14
+ Dynamic: license-file
15
+
16
+ # defi-mcp
17
+
18
+ An MCP (Model Context Protocol) server that gives AI agents direct, on-chain access to DeFi data on EVM chains — wallet balances, Aave v3 lending positions, and live token prices, with no centralized API in the middle.
19
+
20
+ **Status:** Live and functional. 4 tools available today, more on the roadmap.
21
+
22
+ ## Why
23
+
24
+ Most "DeFi + AI" integrations route through a centralized API that can rate-limit, paywall, or simply disappear. `defi-mcp` talks directly to the chain (via your own RPC provider) and to first-party oracles (Chainlink), so the data your AI agent gets is exactly what's on-chain — verifiable, and not dependent on a third-party API staying online.
25
+
26
+ ## Tools
27
+
28
+ ### `get_token_balance`
29
+ Reads the ERC-20 balance of any wallet address for a supported token.
30
+
31
+ Supported tokens on Polygon: `USDC`, `USDC.e`, `USDT`, `WETH`, `WMATIC`, `WBTC`, `DAI`, `AAVE`.
32
+
33
+ ```json
34
+ // get_token_balance(address="0x...", token_symbol="USDC", chain="polygon")
35
+ {
36
+ "address": "0x...",
37
+ "token": "USDC",
38
+ "chain": "polygon",
39
+ "balance": 268.21,
40
+ "raw_balance": "268210000",
41
+ "decimals": 6
42
+ }
43
+ ```
44
+
45
+ ### `get_aave_position`
46
+ Reads a user's full Aave v3 lending/borrowing position: collateral, debt, available borrowing power, and health factor — straight from the Aave v3 Pool contract.
47
+
48
+ ```json
49
+ // get_aave_position(address="0x...", chain="polygon")
50
+ {
51
+ "address": "0x...",
52
+ "chain": "polygon",
53
+ "total_collateral_usd": 969.78,
54
+ "total_debt_usd": 288.16,
55
+ "available_borrows_usd": 484.86,
56
+ "liquidation_threshold_pct": 82.79,
57
+ "ltv_pct": 79.71,
58
+ "health_factor": 2.79,
59
+ "has_active_position": true
60
+ }
61
+ ```
62
+
63
+ A `health_factor` below `1.0` means the position is at risk of liquidation. A `null` health factor means the user has no outstanding debt.
64
+
65
+ ### `get_token_price`
66
+ Reads the current USD price of a token directly from a Chainlink price feed — the same oracle DeFi protocols use internally, so the price is consistent with what `get_aave_position` reports.
67
+
68
+ Supported on Polygon: `ETH`/`WETH`, `MATIC`/`WMATIC`, `WBTC`, `USDC`, `USDT`.
69
+
70
+ ```json
71
+ // get_token_price(token_symbol="WBTC", chain="polygon")
72
+ {
73
+ "token": "WBTC",
74
+ "chain": "polygon",
75
+ "price_usd": 63835.77,
76
+ "decimals": 8,
77
+ "updated_at": 1782032816
78
+ }
79
+ ```
80
+
81
+ ### `hello`
82
+ Simple connectivity check — confirms the MCP server is reachable and responding.
83
+
84
+ ## Supported chains
85
+
86
+ - Polygon
87
+
88
+ More EVM chains are on the roadmap (see below).
89
+
90
+ ## Installation
91
+
92
+ Requires Python 3.10+ and an RPC provider API key (e.g. [Alchemy](https://www.alchemy.com/), free tier works).
93
+
94
+ ```bash
95
+ git clone https://github.com/matiosera3-ops/defi-mcp.git
96
+ cd defi-mcp
97
+ python -m venv venv
98
+
99
+ # Windows
100
+ .\venv\Scripts\Activate.ps1
101
+ # macOS/Linux
102
+ source venv/bin/activate
103
+
104
+ pip install -e .
105
+ ```
106
+
107
+ Create a `.env` file in the project root:
108
+
109
+ ```
110
+ POLYGON_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/YOUR_API_KEY
111
+ ```
112
+
113
+ ## Usage with Claude Desktop
114
+
115
+ Add this to your Claude Desktop MCP config (`claude_desktop_config.json`):
116
+
117
+ ```json
118
+ {
119
+ "mcpServers": {
120
+ "defi-mcp": {
121
+ "command": "python",
122
+ "args": ["/absolute/path/to/defi-mcp/server.py"]
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ Restart Claude Desktop, and the tools above will be available to the model.
129
+
130
+ ## Testing locally
131
+
132
+ You can test the server directly with the official [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
133
+
134
+ ```bash
135
+ npx @modelcontextprotocol/inspector python server.py
136
+ ```
137
+
138
+ ## Roadmap
139
+
140
+ - [ ] Track Uniswap v3 LP positions and impermanent loss
141
+ - [ ] Simulate swaps with realistic slippage estimates
142
+ - [ ] Monitor protocol TVLs
143
+ - [ ] Additional chains (Ethereum, Arbitrum, Base)
144
+ - [ ] `defi-mcp-cloud` — hosted tier with MEV-specific tools, caching, and higher rate limits
145
+
146
+ ## Architecture
147
+
148
+ Open-core model: this repository (MIT licensed) covers standard on-chain read tools. A separate `defi-mcp-cloud` will offer a hosted version with MEV-related tools, request caching, and managed RPC access for users who don't want to run their own infrastructure.
149
+
150
+ ## Contributing
151
+
152
+ Issues and PRs welcome. This is an early-stage project — feedback on what tools would actually be useful to you is especially valuable.
153
+
154
+ ## License
155
+
156
+ MIT
@@ -0,0 +1,18 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/defi_mcp/__init__.py
5
+ src/defi_mcp/config.py
6
+ src/defi_mcp.egg-info/PKG-INFO
7
+ src/defi_mcp.egg-info/SOURCES.txt
8
+ src/defi_mcp.egg-info/dependency_links.txt
9
+ src/defi_mcp.egg-info/requires.txt
10
+ src/defi_mcp.egg-info/top_level.txt
11
+ src/defi_mcp/lib/__init__.py
12
+ src/defi_mcp/lib/abis.py
13
+ src/defi_mcp/lib/chains.py
14
+ src/defi_mcp/tools/__init__.py
15
+ src/defi_mcp/tools/aave_position.py
16
+ src/defi_mcp/tools/balance.py
17
+ src/defi_mcp/tools/price.py
18
+ tests/test_connection.py
@@ -0,0 +1,3 @@
1
+ mcp>=1.0.0
2
+ web3>=7.0.0
3
+ python-dotenv>=1.0.0
@@ -0,0 +1 @@
1
+ defi_mcp
@@ -0,0 +1,25 @@
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from web3 import Web3
4
+
5
+ # Charge le .env
6
+ load_dotenv()
7
+
8
+ # Récupère l'URL Alchemy
9
+ rpc_url = os.getenv("POLYGON_RPC_URL")
10
+
11
+ if not rpc_url:
12
+ print("ERREUR: POLYGON_RPC_URL non trouvée dans .env")
13
+ exit(1)
14
+
15
+ print(f"URL chargée: {rpc_url[:50]}...")
16
+
17
+ # Connexion à Polygon
18
+ w3 = Web3(Web3.HTTPProvider(rpc_url))
19
+
20
+ if w3.is_connected():
21
+ print("Connecte a Polygon")
22
+ print(f"Bloc actuel: {w3.eth.block_number}")
23
+ print(f"Chain ID: {w3.eth.chain_id}")
24
+ else:
25
+ print("Echec de la connexion")