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.
@@ -0,0 +1,14 @@
1
+ .venv/
2
+ venv/
3
+ __pycache__/
4
+ *.py[cod]
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .eggs/
9
+ .env
10
+ .pytest_cache/
11
+ .mypy_cache/
12
+ tests/
13
+ *.egg
14
+ .git/
@@ -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,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .env
10
+ .venv/
11
+ venv/
12
+ .pytest_cache/
13
+ .mypy_cache/
@@ -0,0 +1,10 @@
1
+ {
2
+ "mcpServers": {
3
+ "defistream": {
4
+ "type": "stdio",
5
+ "command": "defistream-mcp",
6
+ "args": [],
7
+ "env": {}
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,13 @@
1
+ FROM python:3.12-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY pyproject.toml README.md ./
6
+ COPY src/ src/
7
+
8
+ RUN pip install --no-cache-dir . && rm -rf /app/*
9
+
10
+ # Default SSE port
11
+ EXPOSE 8000
12
+
13
+ ENTRYPOINT ["defistream-mcp"]
@@ -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,3 @@
1
+ """DeFiStream MCP Server — Model Context Protocol server for the DeFiStream API."""
2
+
3
+ __version__ = "0.1.0"
@@ -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()