@startminingmcp/mcp 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +379 -0
- package/dist/monthly.d.ts +30 -0
- package/dist/monthly.js +52 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# @startminingmcp/mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for Bitcoin mining data. Get real-time hashprice, difficulty, profitability calculations, and more.
|
|
4
|
+
|
|
5
|
+
## 🚀 Features
|
|
6
|
+
|
|
7
|
+
- **Real-time market data** — BTC price, hashprice, difficulty, network stats
|
|
8
|
+
- **Profitability calculator** — Calculate ROI for any ASIC setup
|
|
9
|
+
- **Historical data** — Price and difficulty history since 2009
|
|
10
|
+
- **Halving tracker** — Current era, next halving, supply stats
|
|
11
|
+
- **Mempool & blocks** — Fee estimates and recent blocks
|
|
12
|
+
- **ASIC prices** — Market prices by efficiency tier
|
|
13
|
+
|
|
14
|
+
## 📦 Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g @startminingmcp/mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or use directly with npx:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx @startminingmcp/mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## ⚙️ Configuration
|
|
27
|
+
|
|
28
|
+
### Claude Desktop
|
|
29
|
+
|
|
30
|
+
Add to your `claude_desktop_config.json`:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"startmining": {
|
|
36
|
+
"command": "npx",
|
|
37
|
+
"args": ["@startminingmcp/mcp"]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### With API Key (optional, for higher rate limits)
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"startmining": {
|
|
49
|
+
"command": "npx",
|
|
50
|
+
"args": ["@startminingmcp/mcp"],
|
|
51
|
+
"env": {
|
|
52
|
+
"STARTMINING_API_KEY": "your_api_key"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 🛠️ Available Tools
|
|
60
|
+
|
|
61
|
+
| Tool | Description |
|
|
62
|
+
|------|-------------|
|
|
63
|
+
| `get_market_data` | Current BTC price, hashprice, difficulty, network stats |
|
|
64
|
+
| `get_hashprice` | Hashprice breakdown ($/PH/day) |
|
|
65
|
+
| `get_difficulty` | Current difficulty + next adjustment prediction |
|
|
66
|
+
| `get_halving_info` | Halving era, next date, supply stats |
|
|
67
|
+
| `get_price_history` | Historical BTC prices (from, to, granularity) |
|
|
68
|
+
| `get_difficulty_history` | Historical difficulty & hashrate |
|
|
69
|
+
| `get_monthly_averages` | Calendar-month averages (UTC) of BTC price, hashrate, hashprice USD & BTC (month: YYYY-MM) |
|
|
70
|
+
| `calculate_profitability` | Mining profitability for given setup |
|
|
71
|
+
| `calculate_breakeven` | Breakeven BTC price |
|
|
72
|
+
| `get_mempool` | Mempool status & fee estimates |
|
|
73
|
+
| `get_recent_blocks` | Recent blocks info |
|
|
74
|
+
| `get_asic_prices` | ASIC market prices by efficiency |
|
|
75
|
+
|
|
76
|
+
## 💡 Example Queries
|
|
77
|
+
|
|
78
|
+
Once configured, you can ask Claude:
|
|
79
|
+
|
|
80
|
+
- "What's the current hashprice?"
|
|
81
|
+
- "Calculate profitability for an S21 (200 TH/s, 3500W) at $0.05/kWh"
|
|
82
|
+
- "When is the next halving?"
|
|
83
|
+
- "Show me BTC price history for last month"
|
|
84
|
+
- "What's the mempool fee situation?"
|
|
85
|
+
- "Give me the September 2026 monthly averages for price, hashrate and hashprice"
|
|
86
|
+
|
|
87
|
+
## 📊 Data Sources
|
|
88
|
+
|
|
89
|
+
- **Price data**: Internal database (since 2009-01-03)
|
|
90
|
+
- **Network data**: Direct Bitcoin Core node
|
|
91
|
+
- **Calculations**: Real-time based on current conditions
|
|
92
|
+
|
|
93
|
+
## 🔗 Links
|
|
94
|
+
|
|
95
|
+
- **Website**: [pro.startmining.io](https://pro.startmining.io)
|
|
96
|
+
- **Company**: [startmining.io](https://startmining.io)
|
|
97
|
+
- **API Docs**: [mining-api.startmining.io](https://mining-api.startmining.io)
|
|
98
|
+
|
|
99
|
+
## 📄 License
|
|
100
|
+
|
|
101
|
+
MIT © [Startmining](https://startmining.io)
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { computeMonthlyAverages, monthRange } from "./monthly.js";
|
|
6
|
+
const API_BASE = process.env.STARTMINING_API_URL || "https://mining-api.startmining.io";
|
|
7
|
+
// Default public API key for MCP (rate limited, read-only)
|
|
8
|
+
const DEFAULT_PUBLIC_KEY = "sm_mcp_public_2026_xKj8mNpL4qRsT9wV2yHz";
|
|
9
|
+
const API_KEY = process.env.STARTMINING_API_KEY || DEFAULT_PUBLIC_KEY;
|
|
10
|
+
const MCP_VERSION = "1.1.0";
|
|
11
|
+
// Analytics tracking (fire-and-forget, non-blocking)
|
|
12
|
+
function trackToolCall(toolName) {
|
|
13
|
+
const payload = {
|
|
14
|
+
tool: toolName,
|
|
15
|
+
version: MCP_VERSION,
|
|
16
|
+
ts: Date.now(),
|
|
17
|
+
};
|
|
18
|
+
fetch(`${API_BASE}/v1/mcp/track`, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: { "Content-Type": "application/json" },
|
|
21
|
+
body: JSON.stringify(payload),
|
|
22
|
+
}).catch(() => { }); // Silent fail, never blocks
|
|
23
|
+
}
|
|
24
|
+
async function fetchAPI(endpoint) {
|
|
25
|
+
const headers = {
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
};
|
|
28
|
+
if (API_KEY) {
|
|
29
|
+
headers["X-API-Key"] = API_KEY;
|
|
30
|
+
}
|
|
31
|
+
const response = await fetch(`${API_BASE}${endpoint}`, { headers });
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
|
34
|
+
}
|
|
35
|
+
return response.json();
|
|
36
|
+
}
|
|
37
|
+
async function postAPI(endpoint, body) {
|
|
38
|
+
const headers = {
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
};
|
|
41
|
+
if (API_KEY) {
|
|
42
|
+
headers["X-API-Key"] = API_KEY;
|
|
43
|
+
}
|
|
44
|
+
const response = await fetch(`${API_BASE}${endpoint}`, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers,
|
|
47
|
+
body: JSON.stringify(body),
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
|
51
|
+
}
|
|
52
|
+
return response.json();
|
|
53
|
+
}
|
|
54
|
+
// Create MCP server
|
|
55
|
+
const server = new Server({
|
|
56
|
+
name: "startmining-mcp",
|
|
57
|
+
version: MCP_VERSION,
|
|
58
|
+
}, {
|
|
59
|
+
capabilities: {
|
|
60
|
+
tools: {},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
// Define available tools
|
|
64
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
65
|
+
tools: [
|
|
66
|
+
{
|
|
67
|
+
name: "get_market_data",
|
|
68
|
+
description: "Get current Bitcoin market and mining data including price, hashprice, difficulty, and network stats",
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {},
|
|
72
|
+
required: [],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "get_hashprice",
|
|
77
|
+
description: "Get current hashprice (mining revenue per PH/day) with detailed breakdown",
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: "object",
|
|
80
|
+
properties: {},
|
|
81
|
+
required: [],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "get_difficulty",
|
|
86
|
+
description: "Get current difficulty and next adjustment prediction",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: "object",
|
|
89
|
+
properties: {},
|
|
90
|
+
required: [],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "get_halving_info",
|
|
95
|
+
description: "Get Bitcoin halving information - current era, next halving date, supply stats",
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: "object",
|
|
98
|
+
properties: {},
|
|
99
|
+
required: [],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "get_price_history",
|
|
104
|
+
description: "Get historical Bitcoin prices",
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: {
|
|
108
|
+
from: {
|
|
109
|
+
type: "string",
|
|
110
|
+
description: "Start date (YYYY-MM-DD)",
|
|
111
|
+
},
|
|
112
|
+
to: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "End date (YYYY-MM-DD)",
|
|
115
|
+
},
|
|
116
|
+
granularity: {
|
|
117
|
+
type: "string",
|
|
118
|
+
enum: ["day", "hour"],
|
|
119
|
+
description: "Data granularity (default: day). For calendar-month averages use get_monthly_averages",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
required: [],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "get_difficulty_history",
|
|
127
|
+
description: "Get historical difficulty and hashrate data",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {
|
|
131
|
+
from: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Start date (YYYY-MM-DD)",
|
|
134
|
+
},
|
|
135
|
+
to: {
|
|
136
|
+
type: "string",
|
|
137
|
+
description: "End date (YYYY-MM-DD)",
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
required: [],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "calculate_profitability",
|
|
145
|
+
description: "Calculate mining profitability for given hashrate and electricity cost",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {
|
|
149
|
+
hashrate_th: {
|
|
150
|
+
type: "number",
|
|
151
|
+
description: "Hashrate in TH/s",
|
|
152
|
+
},
|
|
153
|
+
power_watts: {
|
|
154
|
+
type: "number",
|
|
155
|
+
description: "Power consumption in watts",
|
|
156
|
+
},
|
|
157
|
+
electricity_cost: {
|
|
158
|
+
type: "number",
|
|
159
|
+
description: "Electricity cost in $/kWh",
|
|
160
|
+
},
|
|
161
|
+
btc_price: {
|
|
162
|
+
type: "number",
|
|
163
|
+
description: "BTC price (optional, uses current if not provided)",
|
|
164
|
+
},
|
|
165
|
+
pool_fee_pct: {
|
|
166
|
+
type: "number",
|
|
167
|
+
description: "Pool fee percentage (default: 2)",
|
|
168
|
+
},
|
|
169
|
+
days: {
|
|
170
|
+
type: "number",
|
|
171
|
+
description: "Number of days to calculate (default: 30)",
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
required: ["hashrate_th", "power_watts", "electricity_cost"],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "calculate_breakeven",
|
|
179
|
+
description: "Calculate breakeven BTC price for mining operation",
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: "object",
|
|
182
|
+
properties: {
|
|
183
|
+
hashrate_th: {
|
|
184
|
+
type: "number",
|
|
185
|
+
description: "Hashrate in TH/s",
|
|
186
|
+
},
|
|
187
|
+
power_watts: {
|
|
188
|
+
type: "number",
|
|
189
|
+
description: "Power consumption in watts",
|
|
190
|
+
},
|
|
191
|
+
electricity_cost: {
|
|
192
|
+
type: "number",
|
|
193
|
+
description: "Electricity cost in $/kWh",
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
required: ["hashrate_th", "power_watts", "electricity_cost"],
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: "get_mempool",
|
|
201
|
+
description: "Get mempool status and fee estimates",
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: {},
|
|
205
|
+
required: [],
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "get_recent_blocks",
|
|
210
|
+
description: "Get recent Bitcoin blocks",
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: "object",
|
|
213
|
+
properties: {
|
|
214
|
+
limit: {
|
|
215
|
+
type: "number",
|
|
216
|
+
description: "Number of blocks to return (default: 10)",
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
required: [],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "get_monthly_averages",
|
|
224
|
+
description: "Get calendar-month averages (UTC) of BTC price, network hashrate, and hashprice in USD and BTC, for monthly mining reports. The month must be complete.",
|
|
225
|
+
inputSchema: {
|
|
226
|
+
type: "object",
|
|
227
|
+
properties: {
|
|
228
|
+
month: {
|
|
229
|
+
type: "string",
|
|
230
|
+
description: "Calendar month (YYYY-MM), e.g. 2026-09 for 1-30 September 2026",
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
required: ["month"],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: "get_asic_prices",
|
|
238
|
+
description: "Get ASIC miner price data and efficiency stats",
|
|
239
|
+
inputSchema: {
|
|
240
|
+
type: "object",
|
|
241
|
+
properties: {},
|
|
242
|
+
required: [],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
}));
|
|
247
|
+
// Handle tool calls
|
|
248
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
249
|
+
const { name, arguments: args } = request.params;
|
|
250
|
+
// Track usage (non-blocking)
|
|
251
|
+
trackToolCall(name);
|
|
252
|
+
try {
|
|
253
|
+
switch (name) {
|
|
254
|
+
case "get_market_data": {
|
|
255
|
+
const data = await fetchAPI("/market/current");
|
|
256
|
+
return {
|
|
257
|
+
content: [
|
|
258
|
+
{
|
|
259
|
+
type: "text",
|
|
260
|
+
text: JSON.stringify(data, null, 2),
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
case "get_hashprice": {
|
|
266
|
+
const data = await fetchAPI("/calculator/hashprice");
|
|
267
|
+
return {
|
|
268
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
case "get_difficulty": {
|
|
272
|
+
const data = await fetchAPI("/market/difficulty");
|
|
273
|
+
return {
|
|
274
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
case "get_halving_info": {
|
|
278
|
+
const data = await fetchAPI("/market/halving");
|
|
279
|
+
return {
|
|
280
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
case "get_price_history": {
|
|
284
|
+
const params = new URLSearchParams();
|
|
285
|
+
if (args?.from)
|
|
286
|
+
params.set("from", String(args.from));
|
|
287
|
+
if (args?.to)
|
|
288
|
+
params.set("to", String(args.to));
|
|
289
|
+
if (args?.granularity)
|
|
290
|
+
params.set("granularity", String(args.granularity));
|
|
291
|
+
const query = params.toString() ? `?${params}` : "";
|
|
292
|
+
const data = await fetchAPI(`/price/history${query}`);
|
|
293
|
+
return {
|
|
294
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
case "get_difficulty_history": {
|
|
298
|
+
const params = new URLSearchParams();
|
|
299
|
+
if (args?.from)
|
|
300
|
+
params.set("from", String(args.from));
|
|
301
|
+
if (args?.to)
|
|
302
|
+
params.set("to", String(args.to));
|
|
303
|
+
const query = params.toString() ? `?${params}` : "";
|
|
304
|
+
const data = await fetchAPI(`/difficulty/history${query}`);
|
|
305
|
+
return {
|
|
306
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
case "calculate_profitability": {
|
|
310
|
+
const data = await postAPI("/calculator/profitability", {
|
|
311
|
+
hashrate_th: args?.hashrate_th,
|
|
312
|
+
power_watts: args?.power_watts,
|
|
313
|
+
electricity_cost: args?.electricity_cost,
|
|
314
|
+
btc_price: args?.btc_price,
|
|
315
|
+
pool_fee_pct: args?.pool_fee_pct ?? 2,
|
|
316
|
+
days: args?.days ?? 30,
|
|
317
|
+
});
|
|
318
|
+
return {
|
|
319
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
case "calculate_breakeven": {
|
|
323
|
+
const params = new URLSearchParams({
|
|
324
|
+
hashrate_th: String(args?.hashrate_th),
|
|
325
|
+
power_watts: String(args?.power_watts),
|
|
326
|
+
electricity_cost: String(args?.electricity_cost),
|
|
327
|
+
});
|
|
328
|
+
const data = await fetchAPI(`/calculator/breakeven?${params}`);
|
|
329
|
+
return {
|
|
330
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
case "get_mempool": {
|
|
334
|
+
const data = await fetchAPI("/network/mempool");
|
|
335
|
+
return {
|
|
336
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
case "get_recent_blocks": {
|
|
340
|
+
const limit = args?.limit ?? 10;
|
|
341
|
+
const data = await fetchAPI(`/network/blocks?limit=${limit}`);
|
|
342
|
+
return {
|
|
343
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
case "get_monthly_averages": {
|
|
347
|
+
const month = String(args?.month ?? "");
|
|
348
|
+
const { from, to } = monthRange(month);
|
|
349
|
+
const data = await fetchAPI(`/calculator/hashprice/history?from=${from}&to=${to}`);
|
|
350
|
+
const averages = computeMonthlyAverages(month, data.history);
|
|
351
|
+
return {
|
|
352
|
+
content: [{ type: "text", text: JSON.stringify(averages, null, 2) }],
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
case "get_asic_prices": {
|
|
356
|
+
const data = await fetchAPI("/asic-prices/stats");
|
|
357
|
+
return {
|
|
358
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
default:
|
|
362
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
367
|
+
return {
|
|
368
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
369
|
+
isError: true,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
// Start server
|
|
374
|
+
async function main() {
|
|
375
|
+
const transport = new StdioServerTransport();
|
|
376
|
+
await server.connect(transport);
|
|
377
|
+
console.error("Startmining MCP server running on stdio");
|
|
378
|
+
}
|
|
379
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface HashpriceHistoryPoint {
|
|
2
|
+
date: string;
|
|
3
|
+
hashprice_usd_ph_day: number;
|
|
4
|
+
btc_price_usd: number;
|
|
5
|
+
network_hashrate_eh: number;
|
|
6
|
+
}
|
|
7
|
+
export interface MonthlyAverages {
|
|
8
|
+
month: string;
|
|
9
|
+
from: string;
|
|
10
|
+
to: string;
|
|
11
|
+
days: number;
|
|
12
|
+
btc_price_usd: number;
|
|
13
|
+
network_hashrate_eh: number;
|
|
14
|
+
hashprice_usd_ph_day: number;
|
|
15
|
+
hashprice_usd_th_day: number;
|
|
16
|
+
hashprice_btc_ph_day: number;
|
|
17
|
+
hashprice_sats_th_day: number;
|
|
18
|
+
methodology: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
/** First and last day (YYYY-MM-DD) of a calendar month given as YYYY-MM. */
|
|
21
|
+
export declare function monthRange(month: string): {
|
|
22
|
+
from: string;
|
|
23
|
+
to: string;
|
|
24
|
+
days: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Calendar-month averages of the daily points. Refuses an incomplete month so a
|
|
28
|
+
* monthly report never silently relies on fewer days than the month contains.
|
|
29
|
+
*/
|
|
30
|
+
export declare function computeMonthlyAverages(month: string, history: HashpriceHistoryPoint[]): MonthlyAverages;
|
package/dist/monthly.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const round = (value, decimals) => Math.round(value * 10 ** decimals) / 10 ** decimals;
|
|
2
|
+
const mean = (values) => values.reduce((sum, v) => sum + v, 0) / values.length;
|
|
3
|
+
/** First and last day (YYYY-MM-DD) of a calendar month given as YYYY-MM. */
|
|
4
|
+
export function monthRange(month) {
|
|
5
|
+
const match = /^(\d{4})-(0[1-9]|1[0-2])$/.exec(month);
|
|
6
|
+
if (!match) {
|
|
7
|
+
throw new Error("Invalid month. Use YYYY-MM (e.g. 2026-09)");
|
|
8
|
+
}
|
|
9
|
+
const year = Number(match[1]);
|
|
10
|
+
const monthIndex = Number(match[2]);
|
|
11
|
+
const days = new Date(Date.UTC(year, monthIndex, 0)).getUTCDate();
|
|
12
|
+
return {
|
|
13
|
+
from: `${month}-01`,
|
|
14
|
+
to: `${month}-${String(days).padStart(2, "0")}`,
|
|
15
|
+
days,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Calendar-month averages of the daily points. Refuses an incomplete month so a
|
|
20
|
+
* monthly report never silently relies on fewer days than the month contains.
|
|
21
|
+
*/
|
|
22
|
+
export function computeMonthlyAverages(month, history) {
|
|
23
|
+
const { from, to, days } = monthRange(month);
|
|
24
|
+
const points = history.filter((p) => p.date >= from && p.date <= to);
|
|
25
|
+
const uniqueDays = new Set(points.map((p) => p.date)).size;
|
|
26
|
+
if (uniqueDays !== days) {
|
|
27
|
+
throw new Error(`Incomplete month ${month}: ${uniqueDays}/${days} daily points available. ` +
|
|
28
|
+
`Monthly averages require every day of the calendar month.`);
|
|
29
|
+
}
|
|
30
|
+
const hashpriceUsdPh = mean(points.map((p) => p.hashprice_usd_ph_day));
|
|
31
|
+
// BTC hashprice per day = USD hashprice / BTC price of that same day
|
|
32
|
+
const hashpriceBtcPh = mean(points.map((p) => p.hashprice_usd_ph_day / p.btc_price_usd));
|
|
33
|
+
return {
|
|
34
|
+
month,
|
|
35
|
+
from,
|
|
36
|
+
to,
|
|
37
|
+
days,
|
|
38
|
+
btc_price_usd: round(mean(points.map((p) => p.btc_price_usd)), 2),
|
|
39
|
+
network_hashrate_eh: round(mean(points.map((p) => p.network_hashrate_eh)), 2),
|
|
40
|
+
hashprice_usd_ph_day: round(hashpriceUsdPh, 2),
|
|
41
|
+
hashprice_usd_th_day: round(hashpriceUsdPh / 1000, 5),
|
|
42
|
+
hashprice_btc_ph_day: round(hashpriceBtcPh, 8),
|
|
43
|
+
hashprice_sats_th_day: round((hashpriceBtcPh * 1e8) / 1000, 2),
|
|
44
|
+
methodology: {
|
|
45
|
+
period: "Full calendar month, UTC days",
|
|
46
|
+
btc_price: "Arithmetic mean of daily prices (daily price = last price recorded on the UTC day, ~UTC close)",
|
|
47
|
+
network_hashrate: "Arithmetic mean of daily network hashrate estimates",
|
|
48
|
+
hashprice_usd: "Arithmetic mean of daily USD hashprice (block subsidy + ~2% fee approximation)",
|
|
49
|
+
hashprice_btc: "Arithmetic mean of daily BTC hashprice (daily USD hashprice / daily BTC price)",
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startminingmcp/mcp",
|
|
3
|
+
"mcpName": "io.github.tibsp/startmining-mcp",
|
|
4
|
+
"version": "1.1.0",
|
|
5
|
+
"description": "MCP server for Bitcoin mining data - hashprice, difficulty, profitability calculations",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"bin": {
|
|
11
|
+
"startmining-mcp": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"dev": "tsx src/index.ts"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"bitcoin",
|
|
22
|
+
"mining",
|
|
23
|
+
"hashprice",
|
|
24
|
+
"cryptocurrency",
|
|
25
|
+
"ai",
|
|
26
|
+
"claude"
|
|
27
|
+
],
|
|
28
|
+
"author": {
|
|
29
|
+
"name": "Thibaut Spanier",
|
|
30
|
+
"email": "thibaut@startmining.io",
|
|
31
|
+
"url": "https://startmining.io"
|
|
32
|
+
},
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://pro.startmining.io",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/TIBSP/startmining-mcp"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^20.0.0",
|
|
47
|
+
"tsx": "^4.0.0",
|
|
48
|
+
"typescript": "^5.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|