defistream-mcp 0.2.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.
- defistream_mcp-0.2.0/.dockerignore +14 -0
- defistream_mcp-0.2.0/.env.example +38 -0
- defistream_mcp-0.2.0/.gitignore +13 -0
- defistream_mcp-0.2.0/.mcp.json +10 -0
- defistream_mcp-0.2.0/Dockerfile +13 -0
- defistream_mcp-0.2.0/PKG-INFO +224 -0
- defistream_mcp-0.2.0/README.md +196 -0
- defistream_mcp-0.2.0/docker-compose.yml +21 -0
- defistream_mcp-0.2.0/pyproject.toml +52 -0
- defistream_mcp-0.2.0/src/defistream_mcp/__init__.py +3 -0
- defistream_mcp-0.2.0/src/defistream_mcp/api_client.py +102 -0
- defistream_mcp-0.2.0/src/defistream_mcp/config.py +99 -0
- defistream_mcp-0.2.0/src/defistream_mcp/formatters.py +247 -0
- defistream_mcp-0.2.0/src/defistream_mcp/resources.py +65 -0
- defistream_mcp-0.2.0/src/defistream_mcp/server.py +61 -0
- defistream_mcp-0.2.0/src/defistream_mcp/tools.py +727 -0
- defistream_mcp-0.2.0/tests/__init__.py +0 -0
- defistream_mcp-0.2.0/tests/conftest.py +32 -0
- defistream_mcp-0.2.0/tests/test_config.py +110 -0
- defistream_mcp-0.2.0/tests/test_formatters.py +156 -0
- defistream_mcp-0.2.0/tests/test_tools.py +355 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# DeFiStream MCP Server Configuration
|
|
2
|
+
|
|
3
|
+
# API Key (optional for remote SSE mode - users provide their own)
|
|
4
|
+
# For stdio mode or if you want utility tools to work without user key
|
|
5
|
+
# Get your API key from https://defistream.dev
|
|
6
|
+
# DEFISTREAM_API_KEY=dsk_your_api_key_here
|
|
7
|
+
|
|
8
|
+
# API Base URL (optional)
|
|
9
|
+
# Default: https://api.defistream.dev/v1
|
|
10
|
+
# DEFISTREAM_BASE_URL=https://api.defistream.dev/v1
|
|
11
|
+
|
|
12
|
+
# Use local gateway instead of production API (optional)
|
|
13
|
+
# Set to true/1/yes to use local development server
|
|
14
|
+
# DEFISTREAM_LOCAL=false
|
|
15
|
+
|
|
16
|
+
# MCP Transport (optional)
|
|
17
|
+
# Options: stdio (default), http, sse
|
|
18
|
+
# Use "http" for remote server mode (recommended)
|
|
19
|
+
# Use "sse" for legacy SSE transport (deprecated)
|
|
20
|
+
# DEFISTREAM_MCP_TRANSPORT=stdio
|
|
21
|
+
|
|
22
|
+
# SSE Server Host (optional, only used with transport=sse)
|
|
23
|
+
# Default: 0.0.0.0 (all interfaces)
|
|
24
|
+
# DEFISTREAM_MCP_HOST=0.0.0.0
|
|
25
|
+
|
|
26
|
+
# SSE Server Port (optional, only used with transport=sse)
|
|
27
|
+
# Default: 8000
|
|
28
|
+
# DEFISTREAM_MCP_PORT=8000
|
|
29
|
+
|
|
30
|
+
# Query row limit (optional)
|
|
31
|
+
# Maximum rows returned for query results
|
|
32
|
+
# Default: 10000
|
|
33
|
+
# DEFISTREAM_QUERY_ROW_LIMIT=10000
|
|
34
|
+
|
|
35
|
+
# Download directory (optional)
|
|
36
|
+
# Directory for downloaded files (only used with stdio transport)
|
|
37
|
+
# Default: current directory
|
|
38
|
+
# DEFISTREAM_DOWNLOAD_DIR=.
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: defistream-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP server for the DeFiStream API
|
|
5
|
+
Project-URL: Homepage, https://defistream.dev
|
|
6
|
+
Project-URL: Documentation, https://docs.defistream.dev
|
|
7
|
+
Project-URL: Repository, https://github.com/Eren-Nevin/DeFiStream_MCPServer
|
|
8
|
+
Author-email: DeFiStream <support@defistream.dev>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: blockchain,defi,llm,mcp,model-context-protocol
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: httpx>=0.25.0
|
|
21
|
+
Requires-Dist: mcp>=1.7.0
|
|
22
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-xdist>=3.5.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# DeFiStream MCP Server
|
|
30
|
+
|
|
31
|
+
Model Context Protocol (MCP) server that wraps the [DeFiStream](https://defistream.dev) REST API, giving LLMs (Claude Desktop, Claude Code, etc.) direct access to on-chain DeFi event data. Make sure to get an api key from app.defistream.deveasily.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Install
|
|
37
|
+
cd mcp-server
|
|
38
|
+
pip install -e .
|
|
39
|
+
|
|
40
|
+
# Set your API key
|
|
41
|
+
export DEFISTREAM_API_KEY="dsk_your_key_here"
|
|
42
|
+
|
|
43
|
+
# Run (stdio transport)
|
|
44
|
+
defistream-mcp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Docker
|
|
48
|
+
|
|
49
|
+
No Python installation required — run the MCP server directly from Docker Hub:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
docker run -i -e DEFISTREAM_API_KEY=dsk_your_key_here defistream/mcp-server
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Claude Code
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
claude mcp add --transport stdio defistream \
|
|
59
|
+
-- docker run -i -e DEFISTREAM_API_KEY=dsk_your_key_here defistream/mcp-server
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Claude Desktop (Docker)
|
|
63
|
+
|
|
64
|
+
Add to `claude_desktop_config.json`:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"mcpServers": {
|
|
69
|
+
"defistream": {
|
|
70
|
+
"command": "docker",
|
|
71
|
+
"args": [
|
|
72
|
+
"run", "-i",
|
|
73
|
+
"-e", "DEFISTREAM_API_KEY=dsk_your_key_here",
|
|
74
|
+
"defistream/mcp-server"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> **Note:** Claude Desktop passes `env` to the spawned process, but `docker run` requires explicit `-e` flags. Put the API key in `args` as shown above, or set it in your shell before launching Claude Desktop.
|
|
82
|
+
|
|
83
|
+
### Build from Source
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cd mcp-server
|
|
87
|
+
docker build -t defistream/mcp-server .
|
|
88
|
+
docker run -i -e DEFISTREAM_API_KEY=dsk_your_key_here defistream/mcp-server
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Publish to Docker Hub
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
docker login
|
|
95
|
+
docker build -t defistream/mcp-server:latest -t defistream/mcp-server:0.1.0 .
|
|
96
|
+
docker push defistream/mcp-server:latest
|
|
97
|
+
docker push defistream/mcp-server:0.1.0
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Claude Desktop Configuration
|
|
101
|
+
|
|
102
|
+
Add to your `claude_desktop_config.json`:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"mcpServers": {
|
|
107
|
+
"defistream": {
|
|
108
|
+
"command": "defistream-mcp",
|
|
109
|
+
"env": {
|
|
110
|
+
"DEFISTREAM_API_KEY": "dsk_your_key_here"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Environment Variables
|
|
118
|
+
|
|
119
|
+
| Variable | Required | Default | Description |
|
|
120
|
+
|---|---|---|---|
|
|
121
|
+
| `DEFISTREAM_API_KEY` | Yes | — | API key (`dsk_xxx`) |
|
|
122
|
+
| `DEFISTREAM_LOCAL` | No | `false` | Set to `true` to use local gateway (`http://localhost:8081/v1`) |
|
|
123
|
+
| `DEFISTREAM_BASE_URL` | No | `https://api.defistream.dev/v1` | API base URL (overrides `DEFISTREAM_LOCAL`) |
|
|
124
|
+
| `DEFISTREAM_MCP_TRANSPORT` | No | `stdio` | `stdio` or `sse` |
|
|
125
|
+
| `DEFISTREAM_DOWNLOAD_DIR` | No | `.` (cwd) | Default dir for downloaded files |
|
|
126
|
+
| `DEFISTREAM_QUERY_ROW_LIMIT` | No | `200` | Max rows returned by execute_query |
|
|
127
|
+
|
|
128
|
+
### Local Development
|
|
129
|
+
|
|
130
|
+
To test against the local API gateway (port 8081):
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
export DEFISTREAM_API_KEY="dsk_your_key"
|
|
134
|
+
export DEFISTREAM_LOCAL=true
|
|
135
|
+
defistream-mcp
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Or in Claude Desktop / Claude Code config:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"mcpServers": {
|
|
143
|
+
"defistream": {
|
|
144
|
+
"command": "defistream-mcp",
|
|
145
|
+
"env": {
|
|
146
|
+
"DEFISTREAM_API_KEY": "dsk_your_key",
|
|
147
|
+
"DEFISTREAM_LOCAL": "true"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Tools
|
|
155
|
+
|
|
156
|
+
### Utility
|
|
157
|
+
|
|
158
|
+
| Tool | Description |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `supported_networks(protocol)` | List supported networks for a protocol |
|
|
161
|
+
| `supported_events(protocol)` | List supported event types for a protocol |
|
|
162
|
+
| `base_url()` | Get the API base URL |
|
|
163
|
+
| `execute_query(query, limit?)` | Execute a query path and return JSON results |
|
|
164
|
+
| `download_query(query, file_format?, output_dir?)` | Download query results as CSV/Parquet |
|
|
165
|
+
|
|
166
|
+
### Protocol Query Builders
|
|
167
|
+
|
|
168
|
+
Each builder returns a query path string to pass to `execute_query()` or `download_query()`.
|
|
169
|
+
|
|
170
|
+
| Tool | Protocol | Required Params |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| `erc20_query_builder` | ERC-20 tokens | `event_type`, `network`, `token` |
|
|
173
|
+
| `native_token_query_builder` | Native tokens (ETH, BNB, …) | `event_type`, `network` |
|
|
174
|
+
| `aave_v3_query_builder` | AAVE V3 lending | `event_type`, `network` |
|
|
175
|
+
| `uniswap_v3_query_builder` | Uniswap V3 DEX | `event_type`, `network`, `symbol0`, `symbol1`, `fee` |
|
|
176
|
+
| `lido_query_builder` | Lido staking | `event_type`, `network` |
|
|
177
|
+
| `stader_query_builder` | Stader ETHx | `event_type`, `network` |
|
|
178
|
+
| `threshold_query_builder` | Threshold tBTC | `event_type`, `network` |
|
|
179
|
+
|
|
180
|
+
All builders also accept: `block_start/block_end` or `since/until` for range, `verbose`, `with_value`, `aggregate`, `group_by`, `period`, and protocol-specific filter params.
|
|
181
|
+
|
|
182
|
+
- **`with_value`** — Set `true` to enrich events with USD value data. Adds `value_usd` column to individual events, and `agg_value_usd` to aggregate results.
|
|
183
|
+
|
|
184
|
+
### Workflow
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
1. supported_networks("erc20") → check network is valid
|
|
188
|
+
2. erc20_query_builder( → build query path
|
|
189
|
+
event_type="transfer",
|
|
190
|
+
network="ETH",
|
|
191
|
+
token="USDT",
|
|
192
|
+
since="2025-01-01",
|
|
193
|
+
until="2025-01-02"
|
|
194
|
+
)
|
|
195
|
+
3. execute_query(query) → get JSON results
|
|
196
|
+
— or —
|
|
197
|
+
download_query(query, "csv") → save CSV file
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Multi-token queries:** Pass comma-separated known symbol names to query multiple tokens at once (contract addresses not supported for multi-token):
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
erc20_query_builder(
|
|
204
|
+
event_type="transfer",
|
|
205
|
+
network="ETH",
|
|
206
|
+
token="USDT,USDC,DAI",
|
|
207
|
+
since="2025-01-01",
|
|
208
|
+
until="2025-01-02"
|
|
209
|
+
)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Resources
|
|
213
|
+
|
|
214
|
+
| URI | Description |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `defistream://protocols` | Protocol descriptions and builder tool mapping |
|
|
217
|
+
| `defistream://api-limits` | Block limits, row caps, quota info |
|
|
218
|
+
|
|
219
|
+
## Development
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
pip install -e ".[dev]"
|
|
223
|
+
pytest
|
|
224
|
+
```
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# DeFiStream MCP Server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol (MCP) server that wraps the [DeFiStream](https://defistream.dev) REST API, giving LLMs (Claude Desktop, Claude Code, etc.) direct access to on-chain DeFi event data. Make sure to get an api key from app.defistream.deveasily.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install
|
|
9
|
+
cd mcp-server
|
|
10
|
+
pip install -e .
|
|
11
|
+
|
|
12
|
+
# Set your API key
|
|
13
|
+
export DEFISTREAM_API_KEY="dsk_your_key_here"
|
|
14
|
+
|
|
15
|
+
# Run (stdio transport)
|
|
16
|
+
defistream-mcp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Docker
|
|
20
|
+
|
|
21
|
+
No Python installation required — run the MCP server directly from Docker Hub:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
docker run -i -e DEFISTREAM_API_KEY=dsk_your_key_here defistream/mcp-server
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Claude Code
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
claude mcp add --transport stdio defistream \
|
|
31
|
+
-- docker run -i -e DEFISTREAM_API_KEY=dsk_your_key_here defistream/mcp-server
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Claude Desktop (Docker)
|
|
35
|
+
|
|
36
|
+
Add to `claude_desktop_config.json`:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"defistream": {
|
|
42
|
+
"command": "docker",
|
|
43
|
+
"args": [
|
|
44
|
+
"run", "-i",
|
|
45
|
+
"-e", "DEFISTREAM_API_KEY=dsk_your_key_here",
|
|
46
|
+
"defistream/mcp-server"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> **Note:** Claude Desktop passes `env` to the spawned process, but `docker run` requires explicit `-e` flags. Put the API key in `args` as shown above, or set it in your shell before launching Claude Desktop.
|
|
54
|
+
|
|
55
|
+
### Build from Source
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd mcp-server
|
|
59
|
+
docker build -t defistream/mcp-server .
|
|
60
|
+
docker run -i -e DEFISTREAM_API_KEY=dsk_your_key_here defistream/mcp-server
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Publish to Docker Hub
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
docker login
|
|
67
|
+
docker build -t defistream/mcp-server:latest -t defistream/mcp-server:0.1.0 .
|
|
68
|
+
docker push defistream/mcp-server:latest
|
|
69
|
+
docker push defistream/mcp-server:0.1.0
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Claude Desktop Configuration
|
|
73
|
+
|
|
74
|
+
Add to your `claude_desktop_config.json`:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"defistream": {
|
|
80
|
+
"command": "defistream-mcp",
|
|
81
|
+
"env": {
|
|
82
|
+
"DEFISTREAM_API_KEY": "dsk_your_key_here"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Environment Variables
|
|
90
|
+
|
|
91
|
+
| Variable | Required | Default | Description |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| `DEFISTREAM_API_KEY` | Yes | — | API key (`dsk_xxx`) |
|
|
94
|
+
| `DEFISTREAM_LOCAL` | No | `false` | Set to `true` to use local gateway (`http://localhost:8081/v1`) |
|
|
95
|
+
| `DEFISTREAM_BASE_URL` | No | `https://api.defistream.dev/v1` | API base URL (overrides `DEFISTREAM_LOCAL`) |
|
|
96
|
+
| `DEFISTREAM_MCP_TRANSPORT` | No | `stdio` | `stdio` or `sse` |
|
|
97
|
+
| `DEFISTREAM_DOWNLOAD_DIR` | No | `.` (cwd) | Default dir for downloaded files |
|
|
98
|
+
| `DEFISTREAM_QUERY_ROW_LIMIT` | No | `200` | Max rows returned by execute_query |
|
|
99
|
+
|
|
100
|
+
### Local Development
|
|
101
|
+
|
|
102
|
+
To test against the local API gateway (port 8081):
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
export DEFISTREAM_API_KEY="dsk_your_key"
|
|
106
|
+
export DEFISTREAM_LOCAL=true
|
|
107
|
+
defistream-mcp
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Or in Claude Desktop / Claude Code config:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"defistream": {
|
|
116
|
+
"command": "defistream-mcp",
|
|
117
|
+
"env": {
|
|
118
|
+
"DEFISTREAM_API_KEY": "dsk_your_key",
|
|
119
|
+
"DEFISTREAM_LOCAL": "true"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Tools
|
|
127
|
+
|
|
128
|
+
### Utility
|
|
129
|
+
|
|
130
|
+
| Tool | Description |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `supported_networks(protocol)` | List supported networks for a protocol |
|
|
133
|
+
| `supported_events(protocol)` | List supported event types for a protocol |
|
|
134
|
+
| `base_url()` | Get the API base URL |
|
|
135
|
+
| `execute_query(query, limit?)` | Execute a query path and return JSON results |
|
|
136
|
+
| `download_query(query, file_format?, output_dir?)` | Download query results as CSV/Parquet |
|
|
137
|
+
|
|
138
|
+
### Protocol Query Builders
|
|
139
|
+
|
|
140
|
+
Each builder returns a query path string to pass to `execute_query()` or `download_query()`.
|
|
141
|
+
|
|
142
|
+
| Tool | Protocol | Required Params |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| `erc20_query_builder` | ERC-20 tokens | `event_type`, `network`, `token` |
|
|
145
|
+
| `native_token_query_builder` | Native tokens (ETH, BNB, …) | `event_type`, `network` |
|
|
146
|
+
| `aave_v3_query_builder` | AAVE V3 lending | `event_type`, `network` |
|
|
147
|
+
| `uniswap_v3_query_builder` | Uniswap V3 DEX | `event_type`, `network`, `symbol0`, `symbol1`, `fee` |
|
|
148
|
+
| `lido_query_builder` | Lido staking | `event_type`, `network` |
|
|
149
|
+
| `stader_query_builder` | Stader ETHx | `event_type`, `network` |
|
|
150
|
+
| `threshold_query_builder` | Threshold tBTC | `event_type`, `network` |
|
|
151
|
+
|
|
152
|
+
All builders also accept: `block_start/block_end` or `since/until` for range, `verbose`, `with_value`, `aggregate`, `group_by`, `period`, and protocol-specific filter params.
|
|
153
|
+
|
|
154
|
+
- **`with_value`** — Set `true` to enrich events with USD value data. Adds `value_usd` column to individual events, and `agg_value_usd` to aggregate results.
|
|
155
|
+
|
|
156
|
+
### Workflow
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
1. supported_networks("erc20") → check network is valid
|
|
160
|
+
2. erc20_query_builder( → build query path
|
|
161
|
+
event_type="transfer",
|
|
162
|
+
network="ETH",
|
|
163
|
+
token="USDT",
|
|
164
|
+
since="2025-01-01",
|
|
165
|
+
until="2025-01-02"
|
|
166
|
+
)
|
|
167
|
+
3. execute_query(query) → get JSON results
|
|
168
|
+
— or —
|
|
169
|
+
download_query(query, "csv") → save CSV file
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Multi-token queries:** Pass comma-separated known symbol names to query multiple tokens at once (contract addresses not supported for multi-token):
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
erc20_query_builder(
|
|
176
|
+
event_type="transfer",
|
|
177
|
+
network="ETH",
|
|
178
|
+
token="USDT,USDC,DAI",
|
|
179
|
+
since="2025-01-01",
|
|
180
|
+
until="2025-01-02"
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Resources
|
|
185
|
+
|
|
186
|
+
| URI | Description |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `defistream://protocols` | Protocol descriptions and builder tool mapping |
|
|
189
|
+
| `defistream://api-limits` | Block limits, row caps, quota info |
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
pip install -e ".[dev]"
|
|
195
|
+
pytest
|
|
196
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
services:
|
|
2
|
+
mcp-server:
|
|
3
|
+
build:
|
|
4
|
+
context: .
|
|
5
|
+
dockerfile: Dockerfile
|
|
6
|
+
ports:
|
|
7
|
+
- "8912:8912"
|
|
8
|
+
environment:
|
|
9
|
+
- DEFISTREAM_MCP_TRANSPORT=http
|
|
10
|
+
- DEFISTREAM_MCP_HOST=0.0.0.0
|
|
11
|
+
- DEFISTREAM_MCP_PORT=8912
|
|
12
|
+
- DEFISTREAM_BASE_URL=${DEFISTREAM_BASE_URL:-https://api.defistream.dev/v1}
|
|
13
|
+
# API key optional - only needed if you want utility tools to work without user key
|
|
14
|
+
- DEFISTREAM_API_KEY=${DEFISTREAM_API_KEY:-}
|
|
15
|
+
restart: unless-stopped
|
|
16
|
+
healthcheck:
|
|
17
|
+
test: ["CMD", "curl", "-f", "http://localhost:8912/mcp"]
|
|
18
|
+
interval: 30s
|
|
19
|
+
timeout: 10s
|
|
20
|
+
retries: 3
|
|
21
|
+
start_period: 10s
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "defistream-mcp"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "MCP server for the DeFiStream API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "DeFiStream", email = "support@defistream.dev" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["defi", "blockchain", "mcp", "model-context-protocol", "llm"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"mcp>=1.7.0",
|
|
28
|
+
"httpx>=0.25.0",
|
|
29
|
+
"python-dotenv>=1.0.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
dev = [
|
|
34
|
+
"pytest>=7.0.0",
|
|
35
|
+
"pytest-asyncio>=0.21.0",
|
|
36
|
+
"pytest-xdist>=3.5.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
defistream-mcp = "defistream_mcp.server:main"
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://defistream.dev"
|
|
44
|
+
Documentation = "https://docs.defistream.dev"
|
|
45
|
+
Repository = "https://github.com/Eren-Nevin/DeFiStream_MCPServer"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["src/defistream_mcp"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
asyncio_mode = "auto"
|
|
52
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Async HTTP client wrapper for the DeFiStream API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
from .config import ServerConfig
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
_client: DeFiStreamAPIClient | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_client() -> DeFiStreamAPIClient:
|
|
19
|
+
"""Return the module-level API client. Must call ``init_client`` first."""
|
|
20
|
+
if _client is None:
|
|
21
|
+
raise RuntimeError("API client not initialised — call init_client() first")
|
|
22
|
+
return _client
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def init_client(config: ServerConfig) -> DeFiStreamAPIClient:
|
|
26
|
+
"""Create and store the module-level API client."""
|
|
27
|
+
global _client
|
|
28
|
+
_client = DeFiStreamAPIClient(config)
|
|
29
|
+
return _client
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def create_client_with_key(api_key: str) -> DeFiStreamAPIClient:
|
|
33
|
+
"""Create a temporary client with a user-provided API key.
|
|
34
|
+
|
|
35
|
+
Uses the base_url and other settings from the global client's config.
|
|
36
|
+
This allows users to authenticate requests with their own API keys.
|
|
37
|
+
"""
|
|
38
|
+
if _client is None:
|
|
39
|
+
raise RuntimeError("API client not initialised — call init_client() first")
|
|
40
|
+
|
|
41
|
+
temp_config = ServerConfig(
|
|
42
|
+
api_key=api_key,
|
|
43
|
+
base_url=_client.config.base_url,
|
|
44
|
+
transport=_client.config.transport,
|
|
45
|
+
host=_client.config.host,
|
|
46
|
+
port=_client.config.port,
|
|
47
|
+
download_dir=_client.config.download_dir,
|
|
48
|
+
query_row_limit=_client.config.query_row_limit,
|
|
49
|
+
local=_client.config.local,
|
|
50
|
+
)
|
|
51
|
+
return DeFiStreamAPIClient(temp_config)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class DeFiStreamAPIClient:
|
|
55
|
+
"""Lightweight async wrapper around the DeFiStream REST API."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, config: ServerConfig) -> None:
|
|
58
|
+
self.config = config
|
|
59
|
+
self._http = httpx.AsyncClient(
|
|
60
|
+
base_url=config.base_url,
|
|
61
|
+
headers={"X-API-Key": config.api_key},
|
|
62
|
+
timeout=httpx.Timeout(60.0, connect=10.0),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
async def get_json(
|
|
66
|
+
self,
|
|
67
|
+
path: str,
|
|
68
|
+
params: dict[str, Any] | None = None,
|
|
69
|
+
) -> tuple[Any, dict[str, str]]:
|
|
70
|
+
"""GET *path* and return ``(parsed_json, response_headers)``."""
|
|
71
|
+
resp = await self._http.get(path, params=params)
|
|
72
|
+
resp.raise_for_status()
|
|
73
|
+
headers = {
|
|
74
|
+
k: v
|
|
75
|
+
for k, v in resp.headers.items()
|
|
76
|
+
if k.lower().startswith("x-ratelimit") or k.lower() == "x-request-cost"
|
|
77
|
+
}
|
|
78
|
+
return resp.json(), headers
|
|
79
|
+
|
|
80
|
+
async def download_to_file(
|
|
81
|
+
self,
|
|
82
|
+
path: str,
|
|
83
|
+
params: dict[str, Any],
|
|
84
|
+
output_path: Path,
|
|
85
|
+
) -> int:
|
|
86
|
+
"""Stream a GET response to *output_path*. Returns bytes written."""
|
|
87
|
+
total = 0
|
|
88
|
+
async with self._http.stream("GET", path, params=params) as resp:
|
|
89
|
+
resp.raise_for_status()
|
|
90
|
+
with open(output_path, "wb") as f:
|
|
91
|
+
async for chunk in resp.aiter_bytes(chunk_size=65_536):
|
|
92
|
+
f.write(chunk)
|
|
93
|
+
total += len(chunk)
|
|
94
|
+
return total
|
|
95
|
+
|
|
96
|
+
def build_url(self, path: str, params: dict[str, Any]) -> str:
|
|
97
|
+
"""Build the full URL for *path* with query params (for SSE download links)."""
|
|
98
|
+
req = self._http.build_request("GET", path, params=params)
|
|
99
|
+
return str(req.url)
|
|
100
|
+
|
|
101
|
+
async def close(self) -> None:
|
|
102
|
+
await self._http.aclose()
|